刚才上面多次提到intent , 那个Intent 就是指的的我要讲的intent ,那 intent receiver 又为什么呢,其实她和 activity , service 一样响应相对应的 intent ,只不过 intent-receiver 多为接收事件,然后启动具体的 activity ,接收外部的 intent , 它没有 ui 界面,有点像是: activity 是老板,而 intent-reciever 像是秘书, intent-recieve 为 activity 打理各种事情,然后通知老板即可,其实 intent-reciever 和 sevice 是比较接近的, intent-receiver 可以看成是 activity 的代理,或者是输入事件型的 service ,是事件来了就处理,且很快能处理完,而 service 只是 service 是长生命周期的,往往需要长时间运行,通常为多个 activity 服务 , 正因为这个,他们的资源回收优先级也不同。既然 activity,intent-reciever , service 都用到 intent , 那我们来说说 intent

那intent

    上面我们提到过,activity 是相互独立,一般他们是独立进程。但是刚才提到的面向服务要求又要求各个activity 有高频率的交互,这个怎么解决。这个解决方案就是使用intent.

其实我们可以将intent 看成是activity 通信的标准的。比如Intent 中的内容告诉了系统激发intent 的activity 需要什么服务,服务者activity 应该满足的条件。然后就是android 系统的事了,它负责找出符合条件的activity 服务者,并将intent 给activity 服务者,然后这个acitivity 根据intent

由上面可知,intent

1.       activity 服务者的信息,这个是给android 用来找到合适activity

2.       activity 服务者要做什么操作的信息,这个是给activity

我们就以android 自带notepad

     在android 的notepad 实例程序中,入口activity 是notelist

这里有一个onlistItenclick
protected   void  onListItemClick(ListView l, View v,  int  position,  long  id) {
        Uri uri = ContentUris.withAppendedId (getIntent().getData(), id);
        
        String action = getIntent().getAction();
         if  (Intent. ACTION_PICK .equals(action) || Intent. ACTION_GET_CONTENT .equals(action)) {
             // The caller is waiting for us to return a note selected by
             // the user.  The have clicked on one, so return it now.
            setResult( RESULT_OK ,  new  Intent().setData(uri));
        }  else  {
             // Launch activity to view/edit the currently selected item
             startActivity(new Intent(Intent.ACTION_EDIT , uri));
        }

这个函数在 listview 的 item 被单击时执行。

我们首先注意的就是这个函数。

startActivity( new Intent(Intent. ACTION_EDIT , uri));

这个函数有一个intent

new Intent(Intent.ACTION_EDIT , uri) 这个可以看出 intent 承载了两个数据,一个是 uri 对像,一个是 Intent. ACTION_EDIT , 这个其实就是一个字符串变量,系统定义一些常量,当然也可使用你自己定义的。

 

如上图是程序运行后的效果,当我们点First 项时就会执行onListItemClick, 然后就会执行 startActivity(new Intent(Intent.ACTION_EDIT , uri)); 这个函数然后就到 android 系统了 ,android 通过 intent 中的信息找到对应的 activity, 在这里是 NoteEditor activity ,然后就会显示如下 .

 

那 android 是怎样找到的对应的 activity 的呢?

这个就是 intent 发挥作用的时候了。

下面我们就来讲讲:

     new Intent(Intent.ACTION_EDIT , uri)

这个函数中 Intent.ACTION_EDIT =” android.intent.action.EDIT”

Uri 中一个重要的元素是 : Uristring

这个其实是uri 特征标志,通过设断点,我们看下这里的uri

 

从上图可以看出是Uristring =content://com.google.provider.NotePad

然后我们看下Androidmanfest.xml,

其中有这个
< provider android:name = "NotePadProvider" 
android:authorities = " com.google.provider.NotePad " 
/>
发现没,也有com.google.provider.NotePad ,这个是content://com.google.provider.NotePad/notes/1
< activity android:name = "NoteEditor" 
android:theme = "@android:style/Theme.Light" 
android:label = "@string/title_note" 
>
             <!-- This filter says that we can view or edit the data of
                 a single note -->
             < intent-filter android:label = "@string/resolve_edit" >
                 < action android:name = "android.intent.action.VIEW" />
                 < action android:name = " android.intent.action.EDIT " />
                 < action android:name = "com.android.notepad.action.EDIT_NOTE" />
                 < category android:name = "android.intent.category.DEFAULT" />
                 < data android:mimeType = "vnd.android.cursor.item/vnd.google.note" />
             </ intent-filter >
 
             <!-- This filter says that we can create a new note inside
                 of a directory of notes. -->
             < intent-filter >
                 < action android:name = "android.intent.action.INSERT" />
                 < category android:name = "android.intent.category.DEFAULT" />
                 < data android:mimeType = "vnd.android.cursor.dir/vnd.google.note" />
             </ intent-filter >
 
         </ activity >

上面有 android.intent.action.EDIT 正好是

Intent.ACTION_EDIT =” android.intent.action.EDIT

这个时候估计大家看出了一点端倪。

现在就开始进入activity

在函数startActivity 执行后,系统接管,然后系统通过获取intent 中的uri ,找到了content://com.google.provider.NotePad/notes/1, 去掉开始的content: 标识,得到com.google.provider.NotePad/notes/1, 然后获取前面的com.google.provider.NotePad ,然后就到注册了的Androidmanfest.xml 找authorities 为com.google.provider.NotePad 的provider ,这个就是后面要讲的contentprovider, 然后就加载provider

< provider android:name = "NotePadProvider" 
android:authorities = " com.google.provider.NotePad " 
/>

在这里是NotePadProvider, 然后调用 NotePadProvider gettype 函数得到服务的类型。

public  String getType(Uri uri) {
         switch  ( sUriMatcher .match(uri)) {
         case   NOTES :
             return  Notes. CONTENT_TYPE ;
 
         case   NOTE_ID :
             return  Notes. CONTENT_ITEM_TYPE ;
 
         default :
             throw   new  IllegalArgumentException( "Unknown URI "  + uri);
        }

上面的sUriMatcher.match 用来检测uri 看自己能不能处理,在这里会返回return Notes.CONTENT_ITEM_TYPE;

这个是public static final String CONTENT_ITEM_TYPE = " vnd.android.cursor.item/vnd.google.note ";

sUriMatcher .match(uri) 返回值其实是由
sUriMatcher  =  new  UriMatcher(UriMatcher. NO_MATCH );
         sUriMatcher .addURI(NotePad. AUTHORITY ,  "notes" ,  NOTES );
         sUriMatcher .addURI(NotePad. AUTHORITY ,  "notes/#" ,  NOTE_ID );

决定的。

如果我们人为的将前面uri 的string 改为 content://com.google.provider.NotePad/notes/100, 然后match 会正常返回Notes. CONTENT_ITEM_TYPE . 可以找到相应activity ,但是在activity 执行managedQuery 时,调用 content-provider ,这个然后发现不存在 id=100 的数据项,就会异常,然后这个 activity 就会 stop.

在这里是正常返回了vnd.android.cursor.item/vnd.google.note

然后系统将这个和”android.intent.action.EDIT ”到androidmanfest.xml 去找匹配的activity.

< intent-filter android:label = "@string/resolve_edit" >
                 < action android:name = "android.intent.action.VIEW" />
                 < action android:name = " android.intent.action.EDIT " />
                 < action android:name = "com.android.notepad.action.EDIT_NOTE" />
                 < category android:name = "android.intent.category.DEFAULT" />
                 < data android:mimeType = " vnd.android.cursor.item/vnd.google.note " />
            </intent-filter>

正好满足上面noteedit activity 的intent-filter 条件, 这样就找到了noteedit activity.

然后就加载这个类实例化,运行,然后就到了notedit 的oncreate 。

protected   void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
 
         final  Intent intent = getIntent();
 
         // Do some setup based on the action being performed.
final  String action = intent.getAction();
         if  (Intent. ACTION_EDIT .equals(action)) {
             // Requested to edit: set that state, and the data being edited.
             mState  =  STATE_EDIT ;
             mUri  = intent.getData();
        }  else   if  (Intent. ACTION_INSERT .equals(action)) {
             // Requested to insert: set that state, and create a new entry
             // in the container.
             mState  =  STATE_INSERT ;
             mUri  = getContentResolver().insert(intent.getData(),  null );
 
             // If we were unable to create a new note, then just finish
             // this activity.  A RESULT_CANCELED will be sent back to the
             // original activity if they requested a result.
             if  ( mUri  ==  null ) {
                Log.e ( TAG ,  "Failed to insert new note into "  + getIntent().getData());
                finish();
                 return ;
            }
 
             // The new entry was created, so assume all will end well and
             // set the result to be returned.
            setResult( RESULT_OK , ( new  Intent()).setAction( mUri .toString()));
 
        }  else  {
             // Whoops, unknown action!  Bail.
            Log.e ( TAG ,  "Unknown action, exiting" );
            finish();
             return ;
        }
 
         // Set the layout for this activity.  You can find it in res/layout/note_editor.xml
        setContentView(R.layout. note_editor );
        
         // The text view for our note, identified by its ID in the XML file.
         mText  = (EditText) findViewById(. note );
 
         // Get the note!
         mCursor  = managedQuery( mUri ,  PROJECTION ,  null ,  null ,  null );
 
         // If an instance of this activity had previously stopped, we can
         // get the original text it started with.
         if  (savedInstanceState !=  null ) {
             mOriginalContent  = savedInstanceState.getString( ORIGINAL_CONTENT );
        }
}

上面 mCursor = managedQuery( mUri , PROJECTION , null , null , null ); 其实就是返回符合要求的数据 , 然后就是显示而已。

而其中 managedQuery 函数,又会用到 content-provider ,这里是 notepadprovider, 它创建一个 notepadprovider 实例,然后调用 query 函数,这个函数以 uri 类型的参数为依据返回合适的数据,这个 query 函数就是我们具体 content-provider 要实现的了,如果是用 sqlite, 那就通过 sqlitedatabase 取数据,如果是文件存储的,就通过 ifile 相关接口读取数据,其实在 query 函数中, content://com.google.provider.NotePad/notes/1, 后面的/notes/1 才用到,对于sqlite 实现的content-privide:  notes 对应一个数据库的table ,1 是这个table 的id 项。

这个在如下可以看到

Contentprovider 的 query 函数里
case   NOTE_ID :
        qb.setTables( NOTES_TABLE_NAME );
        qb.setProjectionMap( sNotesProjectionMap );
        qb.appendWhere(Notes. _ID  +  "="  +  uri.getPathSegments().get(1) );
     break ;

上面讲的 Intent 包含的是 uri,action 对象 , 其实还有一种指定方式,这个下一章节再讲。

 

前面说到intent

这个就是指定其component 属性(调用setComponent(ComponentName) 或者setClass(Context, Class) 来指定)。通过这种方式的赋值的intent ,其实就直接指出了activity 类,这样就没有必要到androidmanfest.xml 找provider ,然后通过provider 的gettype 返回加上action 来找activity 了,而是直接创建activity

其实android 还提供了一种创建activity 的方式,那就是动态menu

public   boolean  onCreateOptionsMenu(Menu menu) {
         super .onCreateOptionsMenu(menu);
 
         // This is our one standard application action -- inserting a
         // new note into the list.
        menu.add(0,  MENU_ITEM_INSERT , 0, R.string. menu_insert )
                .setShortcut( '3' ,  'a' )
                .setIcon(android.R.drawable. ic_menu_add );
 
         // Generate any additional actions that can be performed on the
         // overall list.  In a normal install, there are no additional
         // actions found here, but this allows other applications to extend
         // our menu with their own actions.
        Intent intent =  new  Intent( null , getIntent().getData());
        intent.addCategory(Intent. CATEGORY_ALTERNATIVE );
         menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE , 0, 0,
                new ComponentName(this , NotesList.class ), null , intent, 0,   null );
 
         return   true ;
}

上面的 addIntentOptions 就是这种方式。

这个函数其实是,系统通过 Intent 查询是否有符合条件的 activity, 这个过程和上面的一样,如果找到了,就生成一个 menu ,这个 menu 的名字是 androidmanfest.xml 中的符合条件的 activity 中的 label 中指定的字符。

< intent-filter android:label = "@string/resolve_edit" >

如这个就是 string/resolve_edit ,这个也是指向一个 string 资源。

在上面的 onCreateOptionsMenu 中:

由于 intent 的 uri 是 content://com.google.provider.NotePad/notes ,因此得到的 type 是 vnd.android.cursor.dir/vnd.google.note

而 category 是 Intent. CATEGORY_ALTERNATIVE

然后到 androidmanfest.xml 去找,大家自己看一下,应该找不到合适的。这样也是为什么 menu 只有一个 add_note 。

如果修改Intent.CATEGORY_ALTERNATIVE 为Intent.CATEGORY_DEFAULT,

再执行,这个时相当于uri=content://com.google.provider.NotePad/notes

category=android.intent.category.DEFAULT

到 androidmanfest.xml 查找,发现有一个满足。

< intent-filter >
                 < action android:name = "android.intent.action.INSERT" />
                 < category android:name = "android.intent.category.DEFAULT" />
                 < data android:mimeType = "vnd.android.cursor.dir/vnd.google.note" />
             </ intent-filter >

但是执行发现没有增加菜单,为什么啊,其实这个是是由于 onCreateOptionsMenu 和 onPrepareOptionsMenu 函数中都有 menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE) ,相当在同一个 groupid 建菜单,这样后一个执行的会覆盖掉前一个的。

而 onPrepareOptionsMenu 在 onCreateOptionsMenu 后执行,所以 onPrepareOptionsMenu 中覆盖了 onCreateOptionsMenu 添加的 菜单,而由于 onPrepareOptionsMenu 没有给 Menu.CATEGORY_ALTERNATIVE 附新值,故 Menu.CATEGORY_ALTERNATIVE 还是为空。

 

怎么解决,好解决,在 onCreateOptionsMenu 中改 groupid 号,即将 Menu.CATEGORY_ALTERNATIVE 改为 Menu.first, 其他的也行,但注意不要改为 menu.none, 这样会覆盖掉

menu.add(0,  MENU_ITEM_INSERT , 0, R.string. menu_insert )
                .setShortcut( '3' ,  'a' )
                .setIcon(android.R.drawable. ic_menu_add );
添加的菜单。因为 menu.none 也为 0

改完后运行可以看到。

 

 

 

总结下函数
int android.view.Menu.addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems)

函数参数分析:

1.       groupid

2.       itemId

3.       Call 就是发起activity 的activity

4.       Specifics 是用来指定菜单顺序的,一般以uri+action 来选择的activity

5.       Intent 这个一般式uri+categroy 形式来选择的activity

6.       outSpecificItems 这个是返回MenuItem

显示的菜单是满足specifics 和满足intent 的所有activity, 然后显示时是满足specifics

action 为.action.MAIN, 类型为 .category.LAUNCHER 的activity 是特殊的activity ,也就是入口activity ,当一个应用执行时,其实是到androidmandfest.xml

action 为.action.MAIN, 类型为.category.LAUNCHER 的activity, 如果发现就创建这个activity 实例运行,如果没发现就不启动,大家可以试着去将.action.MAIN 改为.action.MAIN1

为什么要这样呢,因为android 程序中没有main 等函数,系统不知道该从哪个activity 开始运行,

 

service

service 其实可以看成是activity 的简化版,activity 除去主窗体的功能外,就是service

一个简单的Servce 实例如下:

package com.wissen.testApp.service;
public class MyService extends Service {
     @Override
     public IBinder onBind(Intent intent) {
         return null;
     }    @Override
     public void onCreate() {
         super.onCreate();
         Toast.makeText(this, “Service created…”, Toast.LENGTH_LONG).show();
     }    @Override
     public void onDestroy() {
         super.onDestroy();
         Toast.makeText(this, “Service destroyed…”, Toast.LENGTH_LONG).show();
     }
 }

上例中的这个Service 主要做了这些事:当服务创建和销毁时通过界面消息提示用户。
如Android 中的其它部件一样, Service 也会和一系列Intent 关联。Service 的运行入口需要在AndroidManifest.xml 中进行配置, 如下:

<service class=”.service.MyService”>
     <intent-filter>
         <action android:value=”com.wissen.testApp.service.MY_SERVICE” />
     </intent-filter>
 </service>

 

  之后我们的Service 就可以被其他代码所使用了。
这个和activity

<activity android:name="NoteEditor"…>
改成<service class=”.service.MyService”>

使用Service:
   应用程序可以通过调用 Context.startService 方法来启动Service 。如果当前没有指定的Service 实例被创建,则该方法会调用 Service 的onCreate 方法来创建一个实例;否则调用Service 的onStart

Intent serviceIntent = new Intent();
 serviceIntent.setAction(”com.wissen.testApp.service.MY_SERVICE”);
 startService(serviceIntent);// 也许你会说为什么没有contexta 啊,因为activity 是                      context 的继承,也就是context 上面等价this. startService也就是context. startService

和activity 的使用也没什么差别,将startActivity

  变成startService,

content-provider

      其实在activity 和intent 都说到了content-provider. 不过说的只是provider 的gettype 功能在寻找activity 的作用,其实provider 还有其他的作用,且是主要作用,那就是内容提供商。应用程序可以在Content Provider 中执行如下操作:

 

Content-provider
 查询数据 
修改数据 
添加数据 
删除数据 
返回数据类型 
分别对应: 
public   int  delete(Uri uri, String selection, String[] selectionArgs) {
        //  TODO  Auto-generated method stub
        return  0;
    }
 
     @Override
     public  String getType(Uri uri) {
        //  TODO  Auto-generated method stub
        return   null ;
    }
 
     @Override
     public  Uri insert(Uri uri, ContentValues values) {
        //  TODO  Auto-generated method stub
        return   null ;
    }
 
     @Override
     public   boolean  onCreate() {
        //  TODO  Auto-generated method stub
        return   false ;
    }
 
     @Override
     public  Cursor query(Uri uri, String[] projection, String selection,
           String[] selectionArgs, String sortOrder) {
        //  TODO  Auto-generated method stub
        return   null ;
    }
 
     @Override
     public   int  update(Uri uri, ContentValues values, String selection,
           String[] selectionArgs) {
        //  TODO  Auto-generated method stub
        return  0;
    }

这些都要你自己实现,不同的实现就是对应不同的content-provider

但是activity 使用content-provider

而是调用managedQuery ,getContentResolver().delete,update 等来实现,这些函数其实是先找到符合条件的content-provider, 然后再调用具体的content-provider 的函数来实现,那又是怎么找到content-provider ,就是通过uri 中的authority 来找到content-provider

   利用uri 如何找到content-provider

到此为止,android 程序的框架就讲完了,具体就看对API

后记:

Anroid 平台:

  非常佩服google ,google 的android 刚出来就这么引人瞩目,且吸引这么都人去从事这个,这就是google 的强大。android 不仅仅是一个优化了的嵌入式linux 操作系统,也不仅仅是java 开发环境,也不仅仅是改版的java 虚拟机 Dalvik ,也不仅仅是创新android 窗体系统,而是整个平台,这一整个平台的各个部分google 都针对嵌入式应用做了优化。结果是,这平台大大降低了开发者进入门槛,只要你会java 你就可以开发,同时平台的设计考虑,使得其在嵌入式终端上运行的效率也能得到保证,估计也只有google 这种大公司,极具创新的公司才有这种实力和魄力。google 的几十万linux 服务器可以看出其在Linux 上的实力,同时google 的创新能力不让人怀疑其能够提出一些创新的框架,有了这些,不难看出google 搞出android

   估计大家当初用c# 就是看好c# 程序的开发非常方便,容易上手,现在嵌入式的软件的开发随着android 平台的诞生也将变得如此容易,也难怪越来越多的手机厂商将采用android

   同时手机应用商店的模式,使得应用程序员可以靠开发养活自己,靠开发赚钱,靠开发支撑一个公司,所以现在越来越多的商业公司切入到android