现在的位置: 首页 > 综合 > 正文

Pro Android学习笔记(十二):了解Intent(下)

2012年12月17日 ⁄ 综合 ⁄ 共 8655字 ⁄ 字号 评论关闭

解析Intent,寻找匹配Activity

如果给出component名字(包名、类名)是explicit intent,否则是implicit intent。对于explicit intent,关键就是component 名字,在<intent-fliter>中声明的其他属性被忽略。对于implicit intent,则根据action,category和data来进行匹配。然而一个intent fliter中可以声明多个actions,多个categories,多个data属性,因此可以满足不同的intent。

对于implicit intent设置了action,则该action必须存在于<intent-fliter>之下的某个<action nandroid:name=”……”>。但是和《Pro Android 4.0》所写的不一样,经过测试,我们发现intent-fliter下必须存在action的声明。 

1)对于设置了action的intent,不能匹配intent-fliter下没有定义action的情况; 
2)对于没有设置action的intent,也不能匹配intent-fliter下没有定义action的情况; 
3)对于没有设置action的intent,可以匹配intent-fliter下任何定义的action,但是intent应提供足够的过滤条件,例如data的Uri(测试显示使用category过滤不能匹配)。

至少在Android 4.2版本下的测试如是。因此,我们应该在inter-fliter中声明action name,intent应该提供action name,如特殊情况不能提供,需要提供data属性进行过滤。但这种特殊情况不应该出现,换言之,我们必须做到action匹配。

如果implicit intent设置了data schema,必须能与intent fliter某个data schema匹配;如果intent fliter下设置了data schema,implicit intent必须提供有效的data URI,不能为空。对于URI,除了schema外,还可以对authority(即domain)有要求,在manifest中定义为如下。

<data android:scheme="wei" android:host="flowingflying.com"/>

匹配Uri为:wei://flowingflying.com/….,还可以通过android:path,android:pathPrefix,android:pathpattern做进一步限制,具体可以查看http://developer.android.com/guide/topics/manifest/data-element.htm

同样,对于data mimetype,也必须双方进行匹配,不存在不能视为匹配。通过setType()进行设置,如intent.setType(“text/html”);此外,对于subType,可以用*作为通配,例如intent.setType(“text/*”);

ACTION_PICK

Activity A通过intent唤起Activity B,在某种情况下,我们希望B在结束时能换回信息给A以便A确定下一步的处理工作,这同时也是利用B作为UI的一种方式。一种常用的方式是B是list列表的UI,下面是一个小例子,我们不具体实现B的list UI,只是模拟选择item后设置返回值和返回信息,返回信息由Intent进行携带,本例将自动关闭B。

Activity B步骤1:在AndriodManifest.xml中B的intent fliter中增设PICK的action

<activity …… >
    <intent-filter>  
        … …
 
        <data android:scheme="wei" />
        <action android:name="android.intent.action.PICK" />   
        <category android:name="android.intent.category.DEFAULT"/>

    </intent-filter>
</activity>

Activity B步骤2:在B中设置返回值和返回信息,本例返回信息模拟一个NotePad的Uri,并自动关闭B

......
private void testPick(int itemIndex){ 
    //由于intent fliter可以带有多个action,需要先过滤,只有PICK的action才进行返回值操作 

    if(Intent.ACTION_PICK.equals(this.getIntent().getAction())){

        //设置返回值RESULT_OK,并同时通过intent携带信息传递,本例通过data传递Uri

        setResult(Activity.RESULT_OK /*返回值*/,

                         new Intent().
setData(getNoteUri(itemIndex))
/*携带信息的intent*/);
        //自动关闭Activity B,只有B结束时,返回值才传递给A。当然也可以手工结束B,如按系统返回键。

        finish();
    }
}

private Uri getNoteUri(int itemIndex){ 
    return Uri.parse("content://com.google.provider.NotePad/notes/" + itemIndex);

}

Activity A:

private static final int PICK_REQUEST_CODE = 1000;

private void invokePick(Activity activity){ 
    //设置唤起B的intent,action为PICK,本例中的setData只是为implicit的匹配

    Intent intent = new Intent(Intent.ACTION_PICK); 
    intent.setData(Uri.parse("wei://flowingflying.com/notes")); 

    //不使用startActivity,因为startActivity类似开启一个modal dialog,无法进行callBack,因此采用startActivityForResult()

    activity.startActivityForResult(intent,  PICK_REQUEST_CODE /*request code*/);
//由于可以PICK方式唤起多个不同的Activity,所有的callBack都触发onActivityResult()方法,采用request code用于进行区分。

}

//下面是重写Activity的onActivityResult()方法,当B结束时被触发,其中第一个参数requestCode为startActivityForResult()中所携带的,可以判断是从那个Activity中返回;第二个参数是B的返回值,有系统值RESULT_OK(-1)、RESULT_CANCELED(1),或者用户自定义值,自定义值必须从RESULT_FIRST_USER(1)开始;第三个参数是B通过intent携带的返回信息 

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent outputIntent) {

   // TODO Auto-generated method stub 
    super.onActivityResult(requestCode, resultCode, outputIntent);

    //本例只处理Activity B的返回情况,其request code为PICK_REQUEST_CODE){

    if(requestCode != PICK_REQUEST_CODE){  
        Log.d("WEI","Some one else called this. not us"); 

        return;  
    }
    //如果返回值非OK,即RESULT_CANCELED,不处理 
    if(resultCode != Activity.RESULT_OK){ 
        Log.d("WEI","Result is not OK"); 
        return; 
    }
    //读intent携带的信息
    Uri uri = outputIntent.getData(); 
    Log.d("WEI","uri is " + uri.toString());
 
    //根据携带信息进行处理:本例B返回一个NotePad的content Uri,用之打开NotePad。NotePad在模拟器上是没有的,我们可以通过sample进行导入,具体参见本博最后的附          

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

ACTION_GET_CONTENT

ACTION_GET_CONTENT和ACTION_PICK非常相似,ACTION_PICK是给出URI,系统给出一个或者多个匹配的选择给用户,执行后,唤起的Activity返回值给唤起方。 而ACTION_CET_CONTENT不是基于URI,而是根据MIME Type。

本例使用NotePad,请参考本博最后的附:为模拟器安装NotePad。我们先看看sample NotePad的Manifest.xml文件。该文件展现了多个intent-fliter共存的情况,分别针对应用启动,PICK唤起和GET_CONTENT唤起三种情况。

<activity android:name="NotesList" android:label="@string/title_notes_list">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <action android:name="android.intent.action.EDIT" />
        <action android:name="android.intent.action.PICK" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.GET_CONTENT" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    </intent-filter>
</activity>

代码编写和PICK相似:

private static final int REQUEST_CODE_GET_CONTENT = 101;

private void invokeGetContent(Activity activity){  
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 
    intent.setType("vnd.android.cursor.item/vnd.google.note"); //GET_CONTENT针对MIME TYPE 

    activity.startActivityForResult(intent, REQUEST_CODE_GET_CONTENT); 


    
@Override //接收Acivity的返回信息 
protected void onActivityResult(int requestCode, int resultCode, Intent outputIntent) {  

    if(resultCode != Activity.RESULT_OK){  
        Log.d("Wei","Result is not OK");  
        return;  
    }
  
           
    switch(requestCode){  
    case REQUEST_CODE_PICK:  
       …… //对ACTION_PICK的处理,参考上一例子 
       break;
  
    case REQUEST_CODE_GET_CONTENT:  
        Uri uriContent = outputIntent.getData();  
        Log.d("Wei","uri is " + uriContent.toString());  

        startActivity(new Intent(Intent.ACTION_EDIT,uriContent));  
        break;  
    default:  
        showInfo("Some one else called this. not us");  
        break;
  
    }  
}

Pending Intent

Intent可以作为PendingIntent中的一个参数。PengdingIntent,顾名思义,不是马上调用的,可以在某个事件触发后才唤起。例如在通知中使用,得到用户点击通知时才调起,见Android学习笔记(五四):通知Notification(上);又例如警告,如地理位置匹配时调起,见Android学习笔记(五六):位置Location即时唤起方进程(应用)已经不存在,PendingIntent仍得以很好地保存,很方便进程结束后或其他进程调用

我们做个小实验,看看PendingIntent如何建立:

public static PendingIntent getActivity (Context context, int requestCode, Intent intent, int flags)

public static PendingIntent getActivities (Context context, int requestCode, Intent[] intents, int flags)

Intent intent = new Intent(this,IntentBasicViewActivity.class);
//普通的intent

PendingIntent pi = PendingIntent.getActivity(getApplicationContext(),0,intent,0);

showInfo("intent is " + intent); 
//showInfo()同时在LogCat和UI窗口中显示
showInfo("pending intent is " + pi);
PendingIntent pi1 = PendingIntent.getActivity(getApplicationContext(),0,intent,0);

showInfo("pending intent is " + pi1);

PendingIntent pi2 = PendingIntent.getActivity(getApplicationContext(),1,intent,0);

showInfo("pending intent is " + pi2);

为何使用getActivity如此奇特的方式。普通的intent可以通过startActivity(intent),startService(intent)和sendBroadcast(intent)来唤起Activity,开启服务和唤起broadcast receiver。由于最终要通过PendingIntent中的intent参数实际去唤起对应的Activity、服务或广播接收器,需要告之系统到底是Activity、服务还是广播。相应的也会有:

getBroadcast(Context context, int requestCode, Intent intent, int flags)

getService(Context context, int requestCode, Intent intent, int flags)

至于为何用getxxxx这种方式,据《Pro Android 4.0》是这样解释:Android会存储PendingIntent,并可以多次重用(具体取决于flag参数的设置),如果需要使用相同的intent,需要获得相同的PendingIntent,所有用get。如果要进行区分,则可设置不同的requestCode。

对于flag参数,有:

【1】FLAG_CANCEL_CURRENT:如果当前系统中已经存在一个相同的PendingIntent对象,那么就将先将已有的PendingIntent取消,然后重新生成一个PendingIntent对象。

【2】FLAG_NO_CREATE:如果当前系统中不存在相同的PendingIntent对象,系统将不会创建该PendingIntent对象而是直接返回null。

【3】FLAG_ONE_SHOT:该PendingIntent只作用一次。在该PendingIntent对象通过send()方法触发过后,PendingIntent将自动调用cancel()进行销毁,那么如果你再调用send()方法的话,系统将会返回一个SendIntentException。

【4】FLAG_UPDATE_CURRENT:如果系统中有一个和你描述的PendingIntent对等的PendingInent,那么系统将使用该PendingIntent对象,但是会使用新的Intent来更新之前PendingIntent中的Intent对象数据,例如更新Intent中的Extras。

【说明】这是其中几个flag的解释,参考自http://blog.csdn.net/hudashi/article/details/7060837,更多的请阅读http://developer.android.com/reference/android/app/PendingIntent.html#FLAG_CANCEL_CURRENT

如果我们获得一个PendingIntent,要唤起当中的Intent,可以使用

pi.send(RESULT_CODE); //PendingInten多个send方法

附:为模拟器安装NotePad

从Android SDK Manager上下载sample代码,然后根据下图的操作将之加入模拟器。

为了在PICK和GET_CONTENT调用中有更好的用户体验,当用户选择了item后,NotePad activity能够自动关闭。我们在NotesList.java中加入以下一行代码:

protected void onListItemClick(ListView l, View v, int position, long id) {
    ……
    // Handles requests for note data
    if (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action)) {

        // Sets the result to return to the component that called this Activity.
        // result contains the new URI
        setResult(RESULT_OK, new Intent().setData(uri));
        //Wei Add the following: close NoteList  automatically

        finish();  //wei Add 

    ……
}

相关链接:
我的Android开发相关文章

【上篇】
【下篇】

抱歉!评论已关闭.