Android系统会根据Intent来启动某个组件。显示意图明确启动的组件,但是对于隐式意图而言,到底这个Intent会启动哪个组件,取决于Intent中各项属性的设置。下面将详细介绍Intent的各项属性,以及它们的使用与配置。
Intent对象中,封装了六个重要的属性:Component、Action、Data、Category、Extra和Flag。Intent就是依靠这六个属性携带信息,从而确定以什么方式启动那个组件并传递对应数据,需要注意的是,通常这六个属性中,需要几个属性配合使用,而非单独使用,Intent也对这些属性提供了对应的getter、setter方法。下面分别对这六个属性进行详细的讲解。
Intent在内部封装了一个ComponentName类型的对象,它用于明确指定待启动的组件。对于Intent而言,ComponentName是一个可选属性,当通过它明确的指定了需要启动的组件后,Intent就是一个显示意图。也就是说,它所开启的组件必须是当前应用包下的,而当不指定ComponentName属性时,Intent将是一个隐式意图。
ComponentName提供了多个构造方法,这些方法如下:
· ComponentName(String pkg,String cls)
· ComponentName(Context pkg,String cls)
· ComponentName(Context pkg,Class<?> cls)
从ComponentName提供的几个构造方法中可以看出,它需要指定一个包名和一个类名,这样就可以唯一的确定一个组件了。
示例:通过ComponentName来明确启动一个组件。
代码清单:\codes\06\03\IntentComponentNameDemo\src\com\bookdemo\intentcomponentnamedemo\MainActivity.java
btnCompomentName.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 实例化一个ComponentName对象,指定开启的组件 ComponentName componentName = new ComponentName( MainActivity.this, Activity.class); Intent intent = new Intent(); // 为Intent指定Component intent.setComponent(componentName); // 根据Intent对象开启一个Activity startActivity(intent); } });
新开启的页面,显示出开启组件的包名与类名。
代码清单:\codes\06\03\IntentComponentNameDemo\src\com\bookdemo\intentcomponentnamedemo\Activity2.java
public class Activity2 extends Activity { private TextView tvShow; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_page); tvShow = (TextView) findViewById(R.id.tvShow); // 从Intent中获取其中的ComponentName对象 ComponentName componentName = getIntent().getComponent(); // 在TextView上输出包名和类名 tvShow.setText("组件包名为:" + componentName.getPackageName() + "\n" + "组件类名:" + componentName.getClassName()); } }
如果只使用ComponentName属性来通过显式意图来开启一个组件,那么在清单文件AndroidManifest.xml中,可以使用最简便的方式,为需要开启的属性设置android:name属性即可,无需额外配置<intent-filter…/>元素。
代码清单:AndroidManifest.xml
<activity android:name="com.bookdemo.intentcomponentnamedemo.Activity2" >
在模拟器上运行效果如图所示:
为了简化步骤,Intent提供了构造方法,去设置ComponentName属性,所以通常并不显式的去实例化ComponentName对象。
对于上面的示例,可以直接使用Intent的构造方法来指定ComponentName。
代码清单:MainActivity.java
btnIntent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent=new Intent(MainActivity.this, Activity2.class); startActivity(intent); } });
使用Intent的构造方法来简化ComponentName的实例化过程,和直接实例化ComponentName再对Intent进行设置效果是一样的。其实在Intent的构造方法中,同样也是实例化了一个ComponentName对象,这一点通过查看Intent的源码即可看出来。
Android源码中,Intent的一个构造方法:
public Intent(Context packageContext, Class<?> cls) { mComponent = new ComponentName(packageContext, cls); }
通常情况下,对于一些不希望外部应用程序启动并访问的组件,可以不为其设置<intent-filter…>元素而是设置意图过滤器,这样在应用中就可以通过指定ComponentName来指定唯一一个组件来启动。一般而言,对于一个Service,就应该使用ComponentName属性来指定一个显式意图,用来开启Service组件。
Action属性是一个String类型,它指定了当前Intent的作用,通常配合Category属性一起使用。
Action属性一般用于设置隐式意图,它指定了当前Intent需要做什么,是查看内容,还是选择数据。当设置了Action属性之后,系统会根据意图过滤器来过滤符合的组件进行启动。所以Action需要使用<intent-filter…/>元素,并通过<action…/>元素对其进行设置。
为Intent指定Action有两种方式,一种是使用Intent的几个与Action相关的构造方法,在初始化Intent的设置Action属性,另外一种方式就是使用Intent.setAction(String)方法为Intent指定Action属性。值得注意的是,在同一个Intent中,只能为其指定一个Action属性。
除了在清单文件AndroidManifest.xml中,通过<action…/>元素自定义的一些Action以外,Intent还为我们提供了一些标准的Action,这些Action属性值通过静态常量的形式定义在Intent中。这里介绍一些常用的:
· ACTION_MAIN:应用程序的入口。
· ACTION_VIEW:显示指定数据。
· ACTION_EDIT:编辑指定数据。
· ACTION_PICK:从组件中选择某些数据并返回。
· ACTION_DIAL:打开拨号面板。
· ACTION_CALL:直接向指定号码拨打电话。
· ACTION_SEND:发送消息。
· ACTION_ANSWER:应答电话。
· ACTION_INSERT:插入数据。
· ACTION_DELETE:删除数据。
Category属性需要配合Action属性设置一个隐式意图,Action指定了Intent需要做什么,而Category对这个Action进行了分类。比如Action指定需要查看的内容,而Category指定了如何查看内容。
Category属性和Action一样,也是String类型,它同样需要在<intent-filter…/>元素中,通过<category…/>元素对其进行配置。
为Intent指定Category属性可以使用addCategroy()方法,Category属性不同于Action属性,在同一个Intent中,可以指定多个Category属性。
与Action属性一样,除了我们在清单文件AndroidManifest.xml中,通过<category…/>元素自定义的一些Category属性以外,Intent还提供了一些标准的Category属性值,它们被以静态常量的形式定义,方便我们使用,这里介绍一些常用的Intent定义的Category属性:
· CATEGORY_DEFAULT:默认的Category,配置的时候必须要配置这个Category。
· CATEGORY_BROWSABLE:指定Activity能被浏览器安全调用。
· CATEGORY_TAB:指定Action作为TabActivity的Tab页。
· CATEGORY_LAUNCHER:指定Activity为程序的启动页。
· CATEGORY_HOME:指定Activity为系统桌面。
下面通过一个示例程序,演示如何通过指定Action和Category这两个属性,设置一个隐式的意图,开启Activity。
代码清单:\06\03\IntentActionDemo\src\com\bookdemo\intentactiondemo\MainActivity.java
@Override public void onClick(View v) { Intent intent = new Intent(); switch (v.getId()) { case R.id.btnImplicit1: // 指定Action和Category intent.setAction("com.bookdemo.intentactiondemo.MyAction"); intent.addCategory("com.bookdemo.intentactiondemo.OneCategory"); // 判断系统中是否存在此Intent if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); } break; case R.id.btnImplicit2: intent.setAction("com.bookdemo.intentactiondemo.MyAction"); intent.addCategory("com.bookdemo.intentactiondemo.TwoCategory"); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); } break; case R.id.btnImplicit3: intent.setAction("com.bookdemo.intentactiondemo.MyAction"); //如果存在多个匹配的组件,增加一个选择框 Intent choose=Intent.createChooser(intent, "请选择一个Activity"); if (choose.resolveActivity(getPackageManager()) != null) { startActivity(choose); } break; default: break; } }
在清单文件中配置这两个Activity,并配置对应的过滤器。
代码清单:\06\03\IntentActionDemo\AndroidManifest.xml
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.bookdemo.intentactiondemo.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.bookdemo.intentactiondemo.OneActivity" > <intent-filter> <action android:name="com.bookdemo.intentactiondemo.MyAction" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="com.bookdemo.intentactiondemo.OneCategory" /> </intent-filter> </activity> <activity android:name="com.bookdemo.intentactiondemo.TwoActivity" > <intent-filter> <action android:name="com.bookdemo.intentactiondemo.MyAction" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="com.bookdemo.intentactiondemo.TwoCategory" /> </intent-filter> </activity> </application>
这两个新增的Activity,实现的效果是一样的,在屏幕上打印Intent中Action与Category的值,这里给出OneActivity的代码,TwoActivity的与它类似。
代码清单:\06\03\IntentActionDemo\src\com\bookdemo\intentactiondemo\OneActivity.java
public class OneActivity extends Activity { private TextView tvShow; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_page); tvShow = (TextView) findViewById(R.id.tvShow); // 得到当前Intent对象 Intent intent = getIntent(); // 获得Intent的Action属性值 String action = intent.getAction(); String category = ""; // 获取Category属性值 Set<String> set = intent.getCategories(); if (set != null) { Object[] objs = getIntent().getCategories().toArray(); if (objs.length > 0) { category = (String) objs[0]; } } tvShow.setText("这是Activity1 \n action:" + action + "\n" + "categroy:" + category); } }
在模拟器上运行,分别点击开启Activity1和开启Activity2按钮,它们都可以通过Action属性和Category属性唯一匹配一个Activity,效果如下:
选择待开启的Activity按钮的响应事件中,仅设置了Action属性,没有设置Category属性。而通过这个Action,Android系统将会在意图过滤器中,找到两个匹配的Activity。这时,将弹出一个对话框,列出意图过滤器匹配出的符合的Activity,供用户选择。效果如下:
Data属性接受一个Uri类型的对象,通常用于向Action属性指定的行为提供其所需要的数据。
一个标准的Uri,可以决定传递的是何种类型的数据,又可以指定具体的数据值。所以一般而言Uri属性可以简单分为两个部分:数据类型部分和数据部分。例如:tel:13333333333,它可以被拆分成两个部分。
· tel:前缀,用于指定该数据的类型是一个电话号码。
· 13333333333:数据,表明了操作的电话号码。
在创建一个Intent的时候,通常还需要指定其MIME类型,通常使用type属性设置,用于指定Data属性所指定数据的类型。例如:存在一个Activity,它可以指定一个图片文件进行显示,但是无法播放一个音频文件,因为它们都需要指定一个文件,通常使用"file://"为前缀的Url来指定文件的路径,通过设置MIME类型来直接Intent接受的数据类型,可以帮助Android系统找到最匹配的组件来响应这个Intent。
Data属性在清单文件AndroidManifest.xml中,可以在<intent-filter…/>元素之中增加<data…/>元素配置。通常在其中设置android:scheme属性,用于指定Uri的前缀,如果需要设置MIME类型,还需要通过android:mimeType属性进行设置。
为Intent单独设置Data和MIME类型可以分别使用setData()以及setType()。如果在Intent中需要同时用到这两个属性,还可以使用setDataAndType()方法同时设置Data和MIME类型。
Extra属性,是一个Bundle的类型的对象。可以传递一个Key-Value的键值对,在开启组件的时候,携带一些数据到新开启的组件中。
Bundle被Intent维护,而Bundle这个类中,又维护了一个HashMap<String,Object>类型的集合。为了对其中的Map集合进行操作,Bundle提供了一系列putXxx()方法和getXxx()方法,对其中维护的Map集合插入及获取数据。而为了简化操作,Android在设计Intent的时候,为其提供了一系列的putXxx()方法用于向其内维护的Bundle对象插入数据,也提供了getExtras()方法,用于获取Intent内部维护的Bundle对象。
Bundle除了传递自定义的数据之外,对于一些Android系统内置的应用组件而言,也可以通过设置Intent内部定义好的一些Key,来指定开启Android系统内置的应用组件所需要的数据,这些Intent内部定义的Key,都是使用EXTRA_*的格式,很好辨认。如:使用隐式意图指定发送一封邮件,处理设置Action属性为"ACTION_SEND"之外,还需要使用Extra属性,通过EXTRA_EMAIL的Key来指定收件人,以及通过EXTRA_SUBJECT的Key来指定邮件的主题。
Flag,标识了Android通过启动Activity的方式,以确定开启后的Activity在回退栈中的维护过程。如果需要通过编码的方式指定Intent开启的Activity的启动模式,可以在Intent中使用setFlags(int)方法设置这个属性,它需要传递一个int类型的数据,被以静态常量的形式定义在Intent类中。
如果需要在清单文件中为Activity配置Flag属性来指定Activity的启动模式,可以在<activity…/>元素中,使用android:launchMode进行设置。