Android Api Guid 之App Components 笔记

Android Api Guid 之App Components 笔记


--

1 每个Android应用程序会被分给一个 linux帐号 usrer ID
2 每个Android应用程序被分配一个 VM
3 Application Component
  共四个组件:
  Activity : 参照博客有关 Activity生命周期的文章(<activity></activity>)
  Service : 参照博客有关Service的文章(<service></service>)
  ContentProvider : 参照博客有关ContentProvider的文章, 难点为 URI及MIME (<provider></provider>)
  BroadcastReceiver : 实现 onReceiver 方法(<receiver></receiver>)
  
  其中 Activity/Service/BraodcastReiver 都可以通过 Intent 激活,相关方法为
  Context.startActivity()/Activity.startActivityForResult()
  Context.startService()/Context.bindService()
  Context.sendBroadcast()/sendOrderedBroadcast()/sendStickyBroadcast()
  
  另外 ContentProvider通过 Context.getContentResolver() 激活
  一个BroadcastReceiver可以不在manifest.xml中注册,而是通过 Context.registerReceiver() 完成.
 
4 一些常识
manifest.xml中可以用的元素有:
<support-screens>声明自己应用程序支持的屏幕尺寸
<uses-configuratio> 声明 input configuration
<uses-feature> 声明 a camera, a light sensor, bluetooth, a certain version of OpenGL, or the fidelity of the touchscreen
<uses-sdk> 声明 支持的API等级

你的 animations, menus, styles, colors, and the layout of activity user interfaces 都应该在xml文件中定义
5 Intent及Intent Filter
 一个Intent对象可以设置的字段:
  1> Component: 如果给ntent指定一个明确的Component,可以用 setComponent,setClass,setClassName,getComponent来设置和获取组件.
  2> Action: 一个Action就是一个字符串,Intent内部定义了一些Action常量即字符串常量.Action能够决定Data和Extra,就像函数名指
     定了函数参数和返回值一样. 所以定义自己的Action字符串时最好也想好需要传递的Data和Extra. 可通过setAction/getAction设置.
  3> Data: 即一个URI,如一个ACTION_EDIT需要一个URI来指定需要编辑的文档. URI从一定程度上能够指定MIME Type. 
     可以用 setData/setType/getData/getType操作.
  4> Category: A string containing additional information about the kind of component that should handle the intent.
     用来标识一个组件的种类,注意种类二字 addCaterogy/removeCategory/getCategories操作
  5> Extras: 一个Action可以跟一个Data,同时一个Action也可以和一个Extra配合,如
     an ACTION_TIMEZONE_CHANGED intent has a "time-zone" extra that identifies the new time zone,
     and ACTION_HEADSET_PLUG has a "state" extra indicating whether the headset is now plugged in or unplugged,
     as well as a "name" extra for the type of headset. If you were to invent a SHOW_COLOR action, the color value
     would be set in an extra key-value pair. 
     可以用 put.../set...来设置(其实是设置时Bundle里),也可以用putExtras/getExtras 直接传递Bundle对象.
  6> Flag: Flags of various sorts. Many instruct the Android system how to launch an activity (for example, which task the activity should belong to)
     and how to treat it after it's launched (for example, whether it belongs in the list of recent activities).
     All these flags are defined in the Intent class. 
  解发google开发的应用程序(browser,dailer,google map等),可以参考file:///D:/Android/android-sdk/docs/guide/appendix/g-app-intents.html
  
  Intent解析:
  //一个没有filter的组件只能接收explicit intent.
  If a component does not have any intent filters, it can receive only explicit intents.
  A component with filters can receive both explicit and implicit intents. 
  在进行filter匹配时,仅仅Intent三个字段参加,Extra和Flag不参加:
  action
  data (both URI and data type)
  category
  一个组件可以有多组 <intent-filter> 只要Intent能通过其中一组即可. 一个explicit intent能够触发一个组件,
  不管这个组件有多少<intent-filter>.
  
  <intent-filter> 标签对应 IntentFilter 类, IntentFilter类主要用于 Context.registerReceiver() 方法,因为
  一个BroadcastReceiver可能没有在manifest.xml中注册.
  
  在测试 intent-filter 必须对 action,data,category都进行测试,有一个通不过则此 intent-filter即不能通过.
  1> action测试
  对于下面这样一个例子:
  <intent-filter . . . >
    <action android:name="com.example.project.SHOW_CURRENT" />
    <action android:name="com.example.project.SHOW_RECENT" />
    <action android:name="com.example.project.SHOW_PENDING" />
    . . .
  </intent-filter>
  需要注意的一点为:
  如果一个 <intent-filter> 没有声明任何 action,则所有的implicit intent都不能通过;
  相反如一个 Intent对象如果没有声明action,则可以通过<intent-filter>的 action 这一项(filter至少包含一个action).

  2> category测试
  一般情况下一个 Intent只有一个action,但却可以有多个category,所以category的测试通过规则略有不同.
  一个Intent中所含的所有category都通过<category>才算通过,当然<category>可以包含更多的category,但决不能
  少于 Intent 中所含的.
  一个例外情况是: Context.startActivity 方法会自动设置一个 android.intent.category.DEFAULT" (CATEGORY_DEFAULT)
  到Intent中,所以需要 startActivity激活的Activity的filter应该包含 DEFAULT category.
  另外 filters with "android.intent.action.MAIN" and "android.intent.category.LAUNCHER" settings are the exception.
  They mark activities that begin new tasks and that are represented on the launcher screen.
  They can include "android.intent.category.DEFAULT" in the list of categories, but don't need to.

  3>Data 测试:
  例子如下:
  <intent-filter . . . >
    <data android:mimeType="video/mpeg" android:scheme="http" . . . /> 
    <data android:mimeType="audio/mpeg" android:scheme="http" . . . />
    . . .
  </intent-filter>
  测试通过原则如下:

    a. An Intent object that contains neither a URI nor a data type passes the test only if the filter likewise does not specify any URIs or data types.

    b. An Intent object that contains a URI but no data type (and a type cannot be inferred from the URI) passes the test only if its URI matches a URI in the filter

and the filter likewise does not specify a type. This will be the case only for URIs like mailto: and tel: that do not refer to actual data.

    c. An Intent object that contains a data type but not a URI passes the test only if the filter lists the same data type and similarly does not specify a URI.
    
    d. An Intent object that contains both a URI and a data type (or a data type can be inferred from the URI) passes the data type part of the test only if its type

matches a type listed in the filter. It passes the URI part of the test either if its URI matches a URI in the filter
    /*a b c d 总结为:只有在Intent和Filter完全匹配情况下才能通过*/
   e. (接d)or if it has a content: or file: URI and the filter does not specify a URI. In other words, a component is presumed to support content: and file: data if

its filter lists only a data type.
   /* e的意思为: Intent的URI为content或file且filter没有指定URI则可以通过data测试,这时不关注Filter的 type了,因为可以通过URI解析出来. */

  [小知识]
  (action)ACTION_MAIN     activity     Start up as the initial activity of a task, with no data input and no returned output.
  (category)CATEGORY_LAUNCHER     The activity can be the initial activity of a task and is listed in the top-level application launcher.
  以上两个配合使用
  <intent-filter . . . >
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
  可以想一想,为什么你安装了一个app后,应用程序加载器中就会出现你程序的图标呢?因为Android System的PackageManager会自动的扫描所有manifest.xml文件,
  并把含有 MAIN和Launcher 的 Activity 放到加载器中,并用此Activity的icon和label来显示.
  
  PackageManager有的方法包括 query.../resolve...如 queryIntentActivities/querIntentServices/queryBroadcastReceivers

  

Processes: 

Android系统默认所有组件在同一个进程里(且都在一个 main Thread中).
可以在<activity><service><provider><receiver>中增加 android:process 属性
以使此组件在另外一个进程中运行;同时也可以通过 android:process 让不同应用程序组件在同一个进
程中运行.

<application>也可以有一个 android:process 属性,这样可以给此应用程序内部的组件定义一个默认的
进程.

Process lifecycle: (加红)
当Android需要资源时,它会首先杀死"最不重要"的进程,进程的重要性有五个层次:
1> Foreground process
2> Visible process
3> Service process
4> Background process
5> Empty process

如果一个Activity要执行一个 upload 的操作,它应该启动一个 Service 来执行这个费时的操作,不应该
直接在一个Activity中完成,因为用户可能通过"返回键"退出此Activity.同样一个 BroadcastReceiver
也不应该在 onReceiver 中做太多工作,可以交给 Service来做.

Thread(加红)
应用程序有一个初始的线程,名子为main.
它也称为UI线程,此应用程序的所有操作都是在此线程中完成的,这包括
system calls to each component are dispatched from this thread.
methods that respond to system callbacks (such as onKeyDown() to report 
user actions or a lifecycle callback method) always run in the UI thread of the process
以下是两条准则:
Do not block the UI thread.
Do not access the Android UI toolkit from outside the UI thread.(UI线程非线程安全) 

Worker thread(加红)
那么以下的方法是不是正确的呢?

[java] view plaincopy
  1. public void onClick(View v) {  
  2.     new Thread(new Runnable() {  
  3.         public void run() {  
  4.             Bitmap b = loadImageFromNetwork("http://example.com/image.png");  
  5.             mImageView.setImageBitmap(b);  
  6.         }  
  7.     }).start();  
  8. }  

这种写法是错误的,因为 mImageView 属于 UI thread,它不是线程安全的,你不应该在一个新的线程中
对 mImageView进行修改.

以下方法可以帮助你改变上面的错误:

Activity.runOnUiThread(Runnable)
View.post(Runnable) //相当于 Handler.post()
View.postDelayed(Runnable, long)

例子如下:

[java] view plaincopy
  1. public void onClick(View v) {  
  2.     new Thread(new Runnable() {  
  3.         public void run() {  
  4.             final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");  
  5.             mImageView.post(new Runnable() {  
  6.                 public void run() {  
  7.                     mImageView.setImageBitmap(bitmap);  
  8.                 }  
  9.             });  
  10.         }  
  11.     }).start();  
  12. }  

但当任务变的复杂,上面的代码就不适合了,因为它太难组织了,这时可以使用AsyncTask.

[java] view plaincopy
  1. public void onClick(View v) {  
  2.     new DownloadImageTask().execute("http://example.com/image.png");  
  3. }  
  4.   
  5. private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {  
  6.     /** The system calls this to perform work in a worker thread and 
  7.       * delivers it the parameters given to AsyncTask.execute() */  
  8.     protected Bitmap doInBackground(String... urls) {  
  9.         return loadImageFromNetwork(urls[0]);  
  10.     }  
  11.       
  12.     /** The system calls this to perform work in the UI thread and delivers 
  13.       * the result from doInBackground() */  
  14.     protected void onPostExecute(Bitmap result) {  
  15.         mImageView.setImageBitmap(result);  
  16.     }  
  17. }  

关于 AsyncTask类的详细用法,这里暂不深究.

Thread-safe methods(加红)
我们知识 Service 的 onBinder 方法返回一个 IBinder 对象;同时onBinder方法可供不同的进程调用,
Service属于某一个Process,这个Process有一个 Thread pool,每当有人调用 onBinder方法返回的
IBinder的方法时,系统都会从 Thread pool 中抽出一个线程进行运行,所以 IBinder 对象应该注意
线程安全.
同量 ContentProvider 的 query/insert/update/delete 也应该注意线程安全.

[小知识] 没有成员变量的对象一定是线程安全的. 
         局部变量局部使用是安全的, 为什么?

         因为每个thread 都有自己的运行堆栈,而局部变量是生存在堆栈中,大家不干扰。(加红)


Permission:(加红)
You can use the sharedUserId attribute in the AndroidManifest.xml's manifest tag of each
package to have them assigned the same user ID.

<uses-permission> 来声明自己应用程序需要的权限.

The permissions provided by the Android system can be found at "Manifest.permission".

你也可以定义自己的 permission:

[html] view plaincopy
  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     package="com.me.app.myapp" >  
  3.     <permission android:name="com.me.app.myapp.permission.DEADLY_ACTIVITY"  
  4.         android:label="@string/permlab_deadlyActivity"  //供用户看的简短描述  
  5.         android:description="@string/permdesc_deadlyActivity" //长的描述  
  6.         //最好用系统定义好的group,可以 android.permission_group中找到  
  7.         android:permissionGroup="android.permission-group.COST_MONEY"   
  8.         android:protectionLevel="dangerous" /> //提示用户这个权限是危险的还是一般的  
  9.     ...  
  10. </manifest>  

不但 <application> 可以用 <permission> 声明自己的权限, <activity><service><receiver>
也可以用<permission>声明自己的权限,另外 Context.sendBroadcast()也可以用String声明权限.
<provider>的权限声明有些特殊:

权限标签为:

android:permission  指具有此provider的 read和write 权限.

android:readPermission及android:writePermission.

Context.checkPermission(String, int, int)
PackageManager.checkPermission(String, String) 
可以检查是否具有权限.

ContentProvider还能声明 URI 权限:
android:grantUriPermissions attribute 或 <grant-uri-permissions> tag

这时的ntent 应该具有Flags: 
Intent.FLAG_GRANT_READ_URI_PERMISSION或
Intent.FLAG_GRANT_WRITE_URI_PERMISSION

More information can be found in the Context.grantUriPermission(), 
Context.revokeUriPermission(), and Context.checkUriPermission() methods.


The AndroidManifest.xml File(加红)

它的作用:
1 命名Java package for application. 且 package name 是 unique的
2 描述此APP有哪些组件及各组件的特性
3 It determines which processes will host application components.
4 声明自己请求的权限
5 声明自己的权限
6 It declares the minimum level of the Android API that the application requires.
7 It lists the libraries that the application must be linked against.

普通规则:
除了 <manifest> 标签外,其它各标签的属性必须以 android 为前缀,如 android:name


<manifest> 标签:
xmlns:android
Defines the Android namespace. 
This attribute should always be set to "http://schemas.android.com/apk/res/android".

package:
设置包名,很重要;
这个包名作为默认的进程名,还作为默认的 task affinity 名 for activity

android:sharedUserId
两个或多个app可以共享同一个 UserID;
如果他们有相同的certificate,则可以在同一个Process,共享数据

android:installLocation
只能是以下三个中的一个
"internalOnly"  只能装在内存储
"auto" 如果内存储满则可以外存储
"preferExternal" 优先装在外存储

[html] view plaincopy
  1. <application   
  2.              <!-- 作用于Activity,指一个Activity能否从开始它的进程转移到其它进程 -->  
  3.              <!-- 这和 taskAffinity属性有关 -->  
  4.              android:allowTaskReparenting=["true" | "false"]  
  5.              <!-- BackupAgent的一个子类的名子 -->  
  6.              android:backupAgent="string"  
  7.              android:debuggable=["true" | "false"]  
  8.              android:description="string resource"  
  9.              <!-- 是否可以实例化本应用程序内的组件 -->  
  10.              <!-- 如果为true,则根据各组件的 android:enabled属性决定是否能实例化,-->  
  11.              <!-- 如果为false,则各组件全为false. -->  
  12.              android:enabled=["true" | "false"]  
  13.              android:hasCode=["true" | "false"]  
  14.              <!-- android:hardwareAccelerated 对common 2D图形的操作在Canvas,Paint, -->  
  15.              <!-- Xfermode,ColorFilter,Shader,Camera有加速作用;从 Android3.0开始. -->  
  16.              android:hardwareAccelerated=["true" | "false"]  
  17.              <!-- 此设置作为各组件的default icon -->  
  18.              android:icon="drawable resource"  
  19.              android:killAfterRestore=["true" | "false"]  
  20.              android:largeHeap=["true" | "false"]  
  21.              <!-- 此设置作为各组件的default label -->  
  22.              android:label="string resource"  
  23.              android:logo="drawable resource"  
  24.              android:manageSpaceActivity="string"  
  25.              <!-- 一个Application类的子类名子,在所有组件被实例化前实例化,暂不知作用 -->  
  26.              android:name="string"  
  27.              <!-- 此设置作为default permission for each components -->  
  28.              <!-- 各组件自己的 android:permission 可以覆盖此属性 -->  
  29.              android:permission="string"  
  30.              android:persistent=["true" | "false"]  
  31.              <!-- Each componentcan override this default by setting -->  
  32.              <!-- its own process attribute -->  
  33.              <!-- The name of the default process matches the package name  -->  
  34.              <!-- set by <manifest> element -->  
  35.              <!-- 如果这个名子以 ':'开始,则触发一个private的 new process,不能share的 -->  
  36.              <!-- 如果这个名子以小写字母开头,则分触发一个global precess,可以被share -->  
  37.              android:process="string"  
  38.              android:restoreAnyVersion=["true" | "false"]  
  39.              <!-- 作为默认值对每个Activity,<activity>标签可以覆盖此值, -->  
  40.              <! -- affinity name默认为包名 -->  
  41.              android:taskAffinity="string"  
  42.              <!-- 设置默认的theme for all activities in the application -->  
  43.              <!-- 每个activity可以设置自己的theme来覆盖此属性 -->  
  44.              android:theme="resource or theme"  
  45.              android:uiOptions=["none" | "splitActionBarWhenNarrow"] >  
  46.     . . .  
  47. </application>  
[html] view plaincopy
  1. <activity   
  2.     <!-- 参照application的此属性,另Activity部分有详述 -->  
  3.     android:allowTaskReparenting=["true" | "false"]  
  4.     <!-- 此值只对root activity有意义,它作用于root activity所在的task -->  
  5.     <!-- 如果为true则系统保持状态 -->  
  6.     <!-- 如果为false,30分钟后,系统清空activity stack, 只保留root activity-->  
  7.     <!-- 保留状态还是很重要的,例如 browser. 此值默认为false -->  
  8.     android:alwaysRetainTaskState=["true" | "false"]  
  9.     <!-- 只对root activity有效 -->  
  10.     <!-- 当用户从Launcher加载此APP时,清空此Task除root activity以外的activity -->  
  11.     <!-- android:allowTaskReparenting为true时,相应的activity会被移走,不会被destroy -->  
  12.     android:clearTaskOnLaunch=["true" | "false"]  
  13.     <!-- 如果配置发生变化activity会重启,如果声明以下配置变化,则对应配置发生变化时不会 -->  
  14.     <!-- 在以下配置发生变化时,不重启activity,而是调用onConfigurationChanged()方法-->  
  15.         <!-- 可以参照Api Guide中的 App Resouces下面的Handing Runntime Changes部分 -->  
  16.         android:configChanges=["mcc", "mnc", "locale",  
  17.                          "touchscreen", "keyboard", "keyboardHidden",  
  18.                          "navigation", "screenLayout", "fontScale", "uiMode",  
  19.                          "orientation", "screenSize", "smallestScreenSize"]  
  20.     <!-- 是否能够实例化此Activity,请参考application的此属性 -->  
  21.     android:enabled=["true" | "false"]  
  22.     <!-- 用到时再查此属性 -->  
  23.     android:excludeFromRecents=["true" | "false"]  
  24.     <!-- 是否能被其它application激活,如果有intent-filter则默认为true -->  
  25.     android:exported=["true" | "false"]  
  26.     <!-- 从HOME重新加载一个任务时,此Activity是否被shutdown,默认为false -->  
  27.     <!-- 此属性比allowTaskReparenting优先级高 -->  
  28.     android:finishOnTaskLaunch=["true" | "false"]  
  29.     <!-- 参考application -->  
  30.     android:hardwareAccelerated=["true" | "false"]  
  31.     android:icon="drawable resource"  
  32.     android:label="string resource"  
  33.     <!-- 查看Activity相关博客,其中singleTask -->  
  34.     android:launchMode=["multiple" | "singleTop" |  
  35.                       "singleTask" | "singleInstance"]  
  36.     <!-- 正常情况下被实例化的activity属于定义它的Process -->  
  37.     <!--如果此属性为true,则此activity可以属于激活它的Process(很少使用) -->  
  38.     android:multiprocess=["true" | "false"]  
  39.     <!-- 名子的第一个字符如果为'.',则会用包名代替'.' -->  
  40.     android:name="string"  
  41.     <!-- 默认为false,如果为true则当此activity变的invisible时会执行自己的finish -->  
  42.     <!-- 这样不会在back stack留下自己的轨迹 -->  
  43.     android:noHistory=["true" | "false"]    
  44.     <!-- 引入于API16,暂不关注 -->  
  45.     android:parentActivityName="string"  
  46.     <!-- application的permission为此处的默认 -->  
  47.     <!-- 一个activity也可以有权限,其它组件相应的也可以permission -->  
  48.     android:permission="string"  
  49.     <!-- The name of the process in which the activity should run. -->  
  50.     <!-- 默认所有的app组件都在同一个Process中运行,此Process的名子为包名 -->  
  51.     <!-- 可以以 ':' 或 "小写字母" 开头,具体意义请参照application的此属性-->  
  52.     android:process="string"  
  53.       
  54.     android:screenOrientation=["unspecified" | "user" | "behind" |  
  55.                              "landscape" | "portrait" |  
  56.                              "reverseLandscape" | "reversePortrait" |  
  57.                              "sensorLandscape" | "sensorPortrait" |  
  58.                              "sensor" | "fullSensor" | "nosensor"]  
  59.     <!-- activity destroy时是否需要保存状态即调用onSaveInstanceState方法 -->  
  60.     android:stateNotNeeded=["true" | "false"]  
  61.     <!-- 参照Activity相关博客 -->  
  62.     android:taskAffinity="string"  
  63.     <!-- 如果此属性没有设置则继承application的theme属性 -->  
  64.     <!-- 如果两个都没有设置则用系统默认属性 -->  
  65.     android:theme="resource or theme"  
  66.     android:uiOptions=["none" | "splitActionBarWhenNarrow"]  
  67.     android:windowSoftInputMode=["stateUnspecified",  
  68.                                "stateUnchanged", "stateHidden",  
  69.                                "stateAlwaysHidden", "stateVisible",  
  70.                                "stateAlwaysVisible", "adjustUnspecified",  
  71.                                "adjustResize", "adjustPan"] >     
  72.     . . .  
  73. </activity>  
[html] view plaincopy
  1. <provider android:authorities="list"  
  2.           android:enabled=["true" | "false"]  
  3.           android:exported=["true" | "false"]  
  4.           <!-- 如果true,则URI permission可以访问所有数据 -->  
  5.           <!-- 如果false,则只能访问grant-uri-permission定义的数据 -->  
  6.           <!-- 此属性如果设为true,则能跳过readPermission/writePermisson/permission的限制-->  
  7.           <!-- 会用到Intent.FLAG_GRANT_READ_URI_PERMISSON/WRITE_URI_PERMSIION-->  
  8.           android:grantUriPermissions=["true" | "false"]  
  9.           android:icon="drawable resource"  
  10.           <!-- 一个Process如果多外ContentProvider有依赖关系,则initOrder值大的先被init -->  
  11.           android:initOrder="integer"  
  12.           android:label="string resource"  
  13.           <!--如果为true,则ContentProvider为分配到激活它的进程中,而不是它所属的app的进程中-->  
  14.           <!-- 有时这样可以节约进程间交互对资源的消费,但坏处是什么呢? -->  
  15.           android:multiprocess=["true" | "false"]  
  16.           android:name="string"  
  17.           android:permission="string"  
  18.           <!-- 看Activity的此属性,具体如何用暂未涉及 -->  
  19.           android:process="string"  
  20.           android:readPermission="string"  
  21.           android:syncable=["true" | "false"]  
  22.           android:writePermission="string" >  
  23.     <!-- \有特殊意义,因此这里需要escape -->  
  24.     <!-- * -> \\*  \ -> \\\\ -->  
  25.     <grant-uri-permission android:path="string"  
  26.                       android:pathPattern="string"  
  27.                       android:pathPrefix="string" />  
  28. </provider>  
[html] view plaincopy
  1. <!-- 作为一个Bundle对象中的元素传递给parent component -->  
  2. <!-- 可以通过PackageItemInfo.metaData得到这个Bundle -->  
  3. <meta-data android:name="string"  
  4.            android:resource="resource specification"  
  5.            android:value="string" />  
上一篇:ViewPager


下一篇:JS弄ASP.NET(C#)在页GridView信息选择行