最全面的Android Intent机制讲解.docx
《最全面的Android Intent机制讲解.docx》由会员分享,可在线阅读,更多相关《最全面的Android Intent机制讲解.docx(9页珍藏版)》请在冰点文库上搜索。
最全面的AndroidIntent机制讲解
最全面的AndroidIntent机制讲解
2013-05-2714:
06佚名jizhuomi字号:
T|T
Android中与Intent相关的还有Action/Category及IntentFilter等,另外还有用于广播的Intent,这些元素掺杂在一起,导致初学者不太容易迅速掌握Intent的用法。
在讲解这些名词之前,我们先来从下面的例子中感受一下Intent的一些基本用法,看看它能做些什么,之后再来思考这种机制背后的意义。
AD:
WOT2014课程推荐:
实战MSA:
用开源软件搭建微服务系统
对于大型软件开发经验较少的程序员来说,这可能是一个不太容易理解的抽象概念,因为它与我们平常使用的简单函数调用,或者通过库调用接口的方式不太一样。
在Intent的使用中你看不到直接的函数调用,相对函数调用来说,Intent是更为抽象的概念,利用Intent所实现的软件复用的粒度是Activity/Service,比函数复用更高一些,另外耦合也更为松散。
Android中与Intent相关的还有Action/Category及IntentFilter等,另外还有用于广播的Intent,这些元素掺杂在一起,导致初学者不太容易迅速掌握Intent的用法。
在讲解这些名词之前,我们先来从下面的例子中感受一下Intent的一些基本用法,看看它能做些什么,之后再来思考这种机制背后的意义。
理解Intent的关键之一是理解清楚Intent的两种基本用法:
一种是显式的Intent,即在构造Intent对象时就指定接收者,这种方式与普通的函数调用类似,只是复用的粒度有所差别;另一种是隐式的Intent,即Intent的发送者在构造Intent对象时,并不知道也不关心接收者是谁,这种方式与函数调用差别比较大,有利于降低发送者和接收者之间的耦合。
另外Intent除了发送外,还可用于广播。
下面的一小节我们来看看显式Intent的用法。
显式的Intent(ExplicitIntent)
同一个应用程序中的Activity切换
通常一个应用程序中需要多个UI屏幕,也就需要多个Activity类,并且在这些Activity之间进行切换,这种切换就是通过Intent机制来实现的。
在同一个应用程序中切换Activity时,我们通常都知道要启动的Activity具体是哪一个,因此常用显式的Intent来实现。
下面的例子用来实现一个非常简单的应用程序SimpleIntentTest,它包括两个UI屏幕也就是两个Activity——SimpleIntentTest类和TestActivity类,SimpleIntentTest类有一个按钮用来启动TestActivity。
程序的代码非常简单,SimpleIntentTest类的源代码如下:
1packagecom.tope.samples.intent.simple;
2importandroid.app.Activity;
3importandroid.content.Intent;
4importandroid.os.Bundle;
5importandroid.view.View;
6importandroid.widget.Button;
7publicclassSimpleIntentTestextendsActivityimplementsView.OnClickListener{
8/**Calledwhentheactivityisfirstcreated.*/
9@Override
10publicvoidonCreate(BundlesavedInstanceState){
11super.onCreate(savedInstanceState);
12setContentView(R.layout.main);
13ButtonstartBtn=(Button)findViewById(R.id.start_activity);
14startBtn.setOnClickListener(this);
15}
16publicvoidonClick(Viewv){
17switch(v.getId()){
18caseR.id.start_activity:
19Intentintent=newIntent(this,TestActivity.class);
20startActivity(intent);
21break;
22default:
23break;
24}
25}
26}
上面的代码中,主要是为“Startactivity”按钮添加了OnClickListener,使得按钮被点击时执行onClick()方法,onClick()方法中则利用了Intent机制,来启动TestActivity,关键的代码是下面这两行:
Intentintent=newIntent(this,TestActivity.class);
startActivity(intent);
这里定义Intent对象时所用到的是Intent的构造函数之一:
Intent(ContextpackageContext,Class
>cls)
两个参数分别指定Context和Class,由于将Class设置为TestActivity.class,这样便显式的指定了TestActivity类作为该Intent的接收者,通过后面的startActivity()方法便可启动TestActivity。
TestActivity的代码更为简单(定义TestActivity类需要新建TestActivity.java文件,如果你是一个初学者,你需要学会如何在Eclipse或其他开发环境下添加一个新的类,这里不作详述,请参考其他文档),如下所示:
27packagecom.tope.samples.intent.simple;
28importandroid.app.Activity;
29importandroid.os.Bundle;
30publicclassTestActivityextendsActivity{
31/**Calledwhentheactivityisfirstcreated.*/
32@Override
33publicvoidonCreate(BundlesavedInstanceState){
34super.onCreate(savedInstanceState);
35setContentView(R.layout.test_activity);
36}
37}
可见TestActivity仅仅是调用setContentView来显示test_activity.xml中的内容而已。
对于test_activity.xml及本例中所用到其他xml文件这里不作多余说明。
如果我们仅仅是做上面的一些工作,还不能达到利用SimpleIntentTest启动TestActivity的目的。
事实上,这样做会出现下面的Exception,导致程序退出。
以下是利用logcat工具记录的log信息(省略了后半部分):
I/ActivityManager(569):
Displayedactivitycom.tope.samples/.SimpleIntentTest:
3018ms
I/ActivityManager(569):
Startingactivity:
Intent{comp={com.tope.samples/com.tope.samples.TestActivity}}
D/AndroidRuntime(932):
ShuttingdownVM
W/dalvikvm(932):
threadid=3:
threadexitingwithuncaughtexception(group=0x4000fe70)
E/AndroidRuntime(932):
Uncaughthandler:
threadmainexitingduetouncaughtexception
E/AndroidRuntime(932):
android.content.ActivityNotFoundException:
Unabletofindexplicitactivityclass
{com.tope.samples/com.tope.samples.TestActivity};haveyoudeclaredthisactivityinyourAndroidManifest.xml?
E/AndroidRuntime(932):
atandroid.app.Instrumentation.checkStartActivityResult(Instrumentation.java:
1480)
E/AndroidRuntime(932):
atandroid.app.Instrumentation.execStartActivity(Instrumentation.java:
1454)
E/AndroidRuntime(932):
atandroid.app.Activity.startActivityForResult(Activity.java:
2656)
E/AndroidRuntime(932):
atandroid.app.Activity.startActivity(Activity.java:
2700)
E/AndroidRuntime(932):
atcom.tope.samples.SimpleIntentTest.onClick(SimpleIntentTest.java:
24)
…
从这些log中我们可以看到点击按钮后startActivity的调用过程,主要的原因是:
“android.content.ActivityNotFoundException:
Unabletofindexplicitactivityclass{com.tope.samples/com.tope.samples.TestActivity};haveyoudeclaredthisactivityinyourAndroidManifest.xml?
”
从这些log我们可以看到原因是找不到TestActivity这个Activity,并且log中还给出了提示:
你是否在AndroidManifest.xml中声明了这个Activity?
解决问题的方法也就是按照提示在AndroidManifest.xml中增加TestActivity的声明,如下所示:
38
xmlversion="1.0"encoding="utf-8"?
>
39android="
40package="com.tope.samples"
41android:
versionCode="1"
42android:
versionName="1.0">
43icon="@drawable/icon"android:
label="@string/app_name">
44name=".SimpleIntentTest"
45android:
label="@string/app_name">
46
47name="android.intent.action.MAIN"/>
48name="android.intent.category.LAUNCHER"/>
49
50
51name=".TestActivity"/>
52
53minSdkVersion="3"/>
54
完成这个修改后再重新运行该程序,就一切都正常了。
别走开,下页为您带来Activity切换和隐式Intent
从AndroidManifest.xml修改的过程我们可以体会到,Intent机制即使在程序内部且显式指定接收者,也还是需要在AndroidManifest.xml中声明TestActivity。
这个过程并不像一个简单的函数调用,显式的Intent也同样经过了Android应用程序框架所提供的支持,从满足条件的Activity中进行选择,如果不在AndroidManifest.xml中进行声明,则Android应用程序框架找不到所需要的Activity。
请读者通过我们的示例来逐步理解AndroidManifest.xml在这个过程中所扮演的角色,这样有利于理解Intent的作用,及后面的IntentFilter。
当然,这个例子仅仅是开始,且看下文分解。
不同应用程序之间的Activity切换
上面的例子我们所做的是在同一应用程序中进行Activity的切换,那么在不同的应用程序中,是否也能这么做呢,答案是肯定的,不过对应的代码要稍作修改。
本例中我们需要两个应用程序,可利用上例中的SimpleIntentTest作为其中之一,另外还需要写一个新的程序,来调用SimpleIntentTest应用程序中的TestActivity。
我们新建程序CrossIntentTest(注意不是新建一个类,如果是Eclipse环境,选择File->New->Project新建工程),其中只有一个Activity,其源代码与SimpleIntentTest.java类似:
55packagecom.tope.samples.intent.cross;
56importandroid.app.Activity;
57importandroid.content.Intent;
58importandroid.os.Bundle;
59importandroid.view.View;
60importandroid.widget.Button;
61publicclassCrossIntentTestextendsActivity
62implementsView.OnClickListener{
63/**Calledwhentheactivityisfirstcreated.*/
64@Override
65publicvoidonCreate(BundlesavedInstanceState){
66super.onCreate(savedInstanceState);
67setContentView(R.layout.main);
68ButtonstartBtn=(Button)findViewById(R.id.start_activity);
69startBtn.setOnClickListener(this);
70}
71publicvoidonClick(Viewv){
72switch(v.getId()){
73caseR.id.start_activity:
74Intentintent=newIntent();
75intent.setClassName("com.tope.samples.intent.simple",
76.tope.samples.intent.simple.TestActivity");
77startActivity(intent);
78break;
79default:
80break;
81}
82}
83}
注意比较它与SimpleIntentTest的不同之处主要在于初始化Intent对象的过程:
84Intentintent=newIntent();
85intent.setClassName("com.tope.samples.intent.simple",
86"com.tope.samples.intent.simple.TestActivity");
87startActivity(intent);
这里采用了Intent最简单的不带参数的构造函数,然后通过setClassName()函数来指定要启动哪个包中的哪个Activity,而不是像上例中的通过Intent(ContextpackageContext,Class
>cls)这个构造函数来初始化Intent对象,这是因为,要启动的TestActivity与CrossIntentTest不在同一个包中,要指定Class参数比较麻烦,所以通常启动不同程序的Activity时便采用上面的setClassName()的方式。
除此之外,你也可以利用Android提供的类似的setComponent()方法,具体使用方法请参考AndroidSDK的文档。
另外我们还需要修改SimpleIntentTest程序中的AndroidManifest.xml文件,为TestActivity的声明添加IntentFilter,即将原来的
88name=".TestActivity"/>
修改为:
89name=".TestActivity">
90
91name="android.intent.action.DEFAULT"/>
92
93
对于不同应用之间的Activity的切换,这里需要在IntentFilter中设置至少一个Action,否则其他的应用将没有权限调用这个Activity。
这里我们开始接触IntentFilter和Action这些概念了,读者应该可以感觉到,设置IntentFilter和Action主要的目的,是为了让其他需要调用这个Activity的程序能够顺利的调用它。
除了Action之外,IntentFilter还可以设置Category、Data等,用来更加精确的匹配Intent与Activity。
隐式Intent(ImplicitIntent)
如果Intent机制仅仅提供上面的显式Intent用法的话,这种相对复杂的机制似乎意义并不是很大。
确实,Intent机制更重要的作用在于下面这种隐式的Intent,即Intent的发送者不指定接收者,很可能不知道也不关心接收者是谁,而由Android框架去寻找最匹配的接收者。
最简单的隐式Intent
我们先从最简单的例子开始。
下面的ImplicitIntentTest程序用来启动Android自带的打电话功能的Dialer程序。
ImplicitIntentTest程序只包含一个java源文件ImplicitIntentTest.java,代码如下所示:
94packagecom.tope.samples.intent.implicit;
95importandroid.app.Activity;
96importandroid.content.Intent;
97importandroid.os.Bundle;
98importandroid.view.View;
99importandroid.widget.Button;
100publicclassImplicitIntentTestextendsActivity
101implementsView.OnClickListener{
102/**Calledwhentheactivityisfirstcreated.*/
103@Override
104publicvoidonCreate(BundlesavedInstanceState){
105super.onCreate(savedInstanceState);
106setContentView(R.layout.main);
107ButtonstartBtn=(Button)findViewById(R.id.dial);
108startBtn.setOnClickListener(this);
109}
110publicvoidonClick(Viewv){
111switch(v.getId()){
112caseR.id.dial:
113Intentintent=newIntent(Intent.ACTION_DIAL);
114startActivity(intent);
115break;
116default:
117break;
118}
119}
120}
该程序在Intent的使用上,与上节中的使用方式有很大的不同,即根本不指定接收者,初始化Intent对象时,只是传入参数,设定Action为Intent.ACTION_DIAL:
Intentintent=newIntent(Intent.ACTION_DIAL);
startActivity(intent);
这里使用的构造函数的原型如下:
Intent(Stringaction);
这里读者可暂时将action理解为描述这个Intent的一种方式,这种使用方式看上去比较奇怪,Intent的发送者只是