您的位置:首页 > 移动开发 > Android开发

Android Intent机制实例详解(Activity篇)

2010-06-18 10:59 696 查看
Android Intent机制实例详解(Activity篇)

Android
中提供了
Intent
机制来协助应用间的交互与通讯,或者采用更准确的说法
是,
Intent
不仅可用于应用程序之间,也可用于应用程序内部的
Activity/Service
之间的交互。
Intent
这个英语单词的本意是“目的、意向”等,对于较少从事
于大型平台开发工作的程序员来说,
这可能
是一个不太容易理解的抽象概念,因为它与我们平常使用的简单函数/
方法调用,或者上节中提到的通过库调用接口的方式不太一样。在

Intent
的使用中你看不到直接的函数调用,相对函数调用来说,
Intent
是更为抽象的概念,利用
Intent
所实现的软件复用的粒度是
Activity/Service
,比函数复用更高一些,另外耦合也更为松散。

Android
中与Intent
相关的还有

Action/Category

Intent Filter
等,另外还有用于广播的
Intent
,这些元素掺杂在一起,导致初学者不太容易迅速掌握
Intent
的用法。在讲解这些名词之前,我们先来从下面的例子中
感受一下
Intent
的一些基本用法,看看它能做些什么,之后再来思考这种机制背后的意义。

理解
Intent
的关键之一是理解清楚Intent
的两种基本用法:一种是显式的

Intent
,即在构造
Intent
对象时就指定接收者,这种方式与普通的函数调用类似,
只是复用的粒度有所差别;另一种是隐式的
Intent
,即Intent
的发送者在构造

Intent
对象时,并不知道也不关心接收者是谁,这种方式与函数调用差别比较大,有利于降低发送者和接收
者之间的耦合。另外
Intent
除了发送外,还可用于广播,这些都将在后文进行详细讲述。

下面的一小节我们来看看显式
Intent
的用法。

显式的
Intent(Explicit Intent)

同一个应用程序中的Activity切换

我们在前面的章节已经讨论过
Activity
的概念,通常一个应用程序中需要多个UI
屏幕,也就需要多个

Activity
类,并且在这些
Activity
之间进行切换,这种切换就是通过
Intent
机制来实现的。

在同一个应用程序中切换
Activity时,我们通常都知道要启动的
Activity

具体是哪一个,因此常用显式的
Intent
来实现。下面的例子用来实现一个非常简单的应用程序
SimpleIntentTest
,它包括两个UI
屏幕也就是两个

Activity——SimpleIntentTest类和
TestActivity

类,
SimpleIntentTest类有一个按钮用来启动
TestActivity。

程序的代码非常简单,
SimpleIntentTest类的源代码如下:

package
com.tope.samples.intent.simple;

import
android.app.Activity;

import
android.content.Intent;

import
android.os.Bundle;

import
android.view.View;

import
android.widget.Button;

public

class
SimpleIntentTest
extends
Activity
implements
View.OnClickListener{

/**

Called

when

the

activity

is

first

created.

*/

@Override

public

void
onCreate(Bundle savedInstanceState) {

super
.onCreate(savedInstanceState);

setContentView(R.layout.
main
);

Button startBtn = (Button)findViewById(R.id.
start_activity
);

startBtn.setOnClickListener(
this
);

}

public

void
onClick(View v) {

switch
(v.getId()) {

case
R.id.
start_activity
:

Intent intent =
new
Intent(
this
, TestActivity.
class
);

startActivity(intent);

break
;

default
:

break
;

}

}

}

上面的代码中,主要是为“
Start activity

按钮添加了
OnClickListener,

使得按钮被点击时执行
onClick()
方法,
onClick()
方法中则利用了
Intent
机制,来启动
TestActivity,关键的代码是下面这两行:

Intent intent =
new
Intent(
this
, TestActivity.
class
);

startActivity(intent);

这里定义
Intent
对象时所用到的是
Intent
的构造函数之一:

Intent

(
Context

packageContext,
Class

<?> cls)

两个参数分别指定
Context

Class
,由于将Class
设置为

TestActivity.class,这样便显式的指定了
TestActivity
类作为

该Intent


接收者,通过后面的startActivity()

方法便可启动
TestActivity


TestActivity
的代码更为简单(定义
TestActivity类需要新建
TestActivity.java

文件,如果你是一个初学者,你需要学会如何在
Eclipse
或其他开发环境下添加一个新的类,本书不作详述,请参
考其他文档),如下所示:

package
com.tope.samples.intent.simple;

import
android.app.Activity;

import
android.os.Bundle;

public

class
TestActivity
extends
Activity {

/**

Called

when

the

activity

is

first

created.

*/

@Override

public

void
onCreate(Bundle savedInstanceState) {

super
.onCreate(savedInstanceState);

setContentView(R.layout.
test_activity
);

}

}

可见
TestActivity仅仅是调用
setContentView

来显示
test_activity.xml
中的内容而已。对于
test_activity.xml及

本例中所用到其他
xml
文件这里不作多余说明,读者练习时可自行参考本书所附光盘中的源代码。

如果我们仅仅是做上面的一些
工作,还不能达到利用
SimpleIntentTest
启动
TestActivity的目的。事实上,这样做
会出现下面的
Exception

,导致程序退出。以下是利用
logcat
工具记录的log
信息(省略了后半部分):

I/ActivityManager( 569): Displayed activity com.tope.samples/.SimpleIntentTest: 3018 ms

I/ActivityManager( 569): Starting activity: Intent { comp={com.tope.samples/com.tope.samples.TestActivity} }

D/AndroidRuntime( 932): Shutting down VM

W/dalvikvm( 932): threadid=3: thread exiting with uncaught exception (group=0x4000fe70)

E/AndroidRuntime( 932): Uncaught handler: thread main exiting due to uncaught exception

E/AndroidRuntime( 932):
android.content.ActivityNotFoundException
: Unable to find explicit activity class {com.tope.samples/com.tope.samples.TestActivity}; have you declared this activity in your AndroidManifest.xml?

E/AndroidRuntime( 932): at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1480)

E/AndroidRuntime( 932): at android.app.Instrumentation.execStartActivity(Instrumentation.java:1454)

E/AndroidRuntime( 932): at android.app.Activity.startActivityForResult(Activity.java:2656)

E/AndroidRuntime( 932): at android.app.Activity.startActivity(Activity.java:2700)

E/AndroidRuntime( 932): at com.tope.samples.SimpleIntentTest.onClick(SimpleIntentTest.java:24)



从这些log
中我们可以看到点击按钮后

startActivity
的调用过程,主要的原因是:“
android.content.ActivityNotFoundException
: Unable to find explicit activity class {com.tope.samples/com.tope.samples.TestActivity}; have you declared this activity in your AndroidManifest.xml?


从这些log
我们可以看到原因是找不到

TestActivity这个
Activity
,并且
log
中还给出了提示:你是否在
AndroidManifest.xml

中声明了这个
Activity?解决问题的方法也就是按照提示在
AndroidManifest.xml
中增加
TestActivity
的声明,如下所示,注意粗体字部分:

<?
xml

version
=
"1.0"

encoding
=
"utf-8"
?>

<
manifest

xmlns:android
=
"http://schemas.android.com/apk/res/android"

package
=
"com.tope.samples"

android:versionCode
=
"1"

android:versionName
=
"1.0"
>

<
application

android:icon
=
"@drawable/icon"

android:label
=
"@string/app_name"
>

<
activity

android:name
=
".SimpleIntentTest"

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
=
".TestActivity"
/>

</
application
>

<
uses-sdk

android:minSdkVersion
=
"3"

/>

</
manifest
>

完成这个修改后再重新运行该
程序,就一切都正常了。


AndroidManifest.xml修改的过程我们可以体会到,
Intent

机制即使在
程序内部

显式指定
接收者,也还是需要在
AndroidManifest.xml
中声明
TestActivity。这个过程并不像一个简单的函数调用,显式的
Intent

也同样
经过了Android

应用程序框架所提供的支持,从满足条件的
Activity
中进行选择,如果不在
AndroidManifest.xml中进行声明,则
Android

应用程序框架找不到所需要的
Activity。

请读者通过我们的示例来逐步
理解
AndroidManifest.xml在这个过程中所扮演的角色,这样有利于理解
Intent
的作用

,及后面的
Intent Filter。当然,这个例子仅仅是开始,且看下文分解



²
不同应用程序之间的Activity切换

上面的例子我们所做的是在同
一应用程序中进行
Activity
的切换,那么在不同的应用程序中,是否也能这么做呢,答案是肯定的,不过对应的代码要稍作修改。本例中我们需要两个应用程序,可利用上例中

SimpleIntentTest作为其中之一,另外还需要写一个新的程序,来调用
SimpleIntentTest
应用程序中的
TestActivity



我们新建程序
CrossIntentTest(注意不是新建一个类,如果是
Eclipse

环境,选择
File->New->Project新建工程),其中只有一个
Activity
,其源代码与
SimpleIntentTest.java

类似


package
com.tope.samples.intent.cross;

import
android.app.Activity;

import
android.content.Intent;

import
android.os.Bundle;

import
android.view.View;

import
android.widget.Button;

public

class
CrossIntentTest
extends
Activity

implements
View.OnClickListener{

/**

Called

when

the

activity

is

first

created.

*/

@Override

public

void
onCreate(Bundle savedInstanceState) {

super
.onCreate(savedInstanceState);

setContentView(R.layout.
main
);

Button startBtn = (Button)findViewById(R.id.
start_activity
);

startBtn.setOnClickListener(
this
);

}

public

void
onClick(View v) {

switch
(v.getId()) {

case
R.id.
start_activity
:

Intent intent =
new
Intent();

intent.setClassName(
"com.tope.samples.intent.simple"
,

"com.tope.samples.intent.simple.TestActivity"
);

startActivity(intent);

break
;

default
:

break
;

}

}

}

注意比较它与
SimpleIntentTest的不同之处主要在于初始化
Intent

对象的过程:

Intent intent =
new
Intent();

intent.setClassName(
"com.tope.samples.intent.simple"
,

"com.tope.samples.intent.simple.TestActivity"
);

startActivity(intent);

这里采用了
Intent
最简单的不带参数的构造函数

然后通过
setClassName()
函数来指定要启动哪个包中的哪个
Activity,

而不是像上例中的通过
Intent

(
Context

packageContext,
Class

<?> cls)
这个构造函数来初始化
Intent
对象

这是因为

要启动的
TestActivity

CrossIntentTest
不在同一个包中

要指定
Class
参数比较麻烦

所以通常启动不同程序的
Activity
时便采用上面的
setClassName()
的方式。除此之外,你也可以利用
Android
提供的类似的
setComponent()
方法,具体使用方法请参考
Android
SDK的文档。


外我们还需要修改SimpleIntentTest

程序中的
AndroidManifest.xml
文件,为
TestActivity
的声明添加
Intent Filter
,即将原来的

<
activity

android:name
=
".TestActivity"
/>

修改为:

<
activity

android:name
=
".TestActivity"
>

<
intent-filter
>

<
action

android:name
=
"android.intent.action.DEFAULT"

/>

</
intent-filter
>

</
activity
>

对于不同应用之间的
Activity
的切换,这里需要在
Intent Filter中

设置至少一个
Action,否则其他的应用将没有

权限调用这个
Activity
。这里我们开始接触
Intent Filter和
Action



概念了,读者应该可以感觉到,设置Intent Filter

Action
主要的目的,是为了让其他需要调用这个
Activity

的程序能够顺利的调用它。除了
Action之外,
Intent Filter
还可以设置
Category


Data等,用来更加精确的匹配
Intent


Activity,这在后文将有详细介绍



程序运行的截图与上例类似,这里就不再重复了。

隐式
Intent(Implicit Intent)

如果
Intent
机制仅仅提供上面的显式
Intent
用法的话,这种相对复杂的机制似乎意义并不是很大。确
实,
Intent
机制更重要的作用在于下面这种隐式的
Intent
,即
Intent
的发送者不指定接收者,很可能不知道也不关心接收者是谁,而由
Android
框架去寻找最匹配的接收者。

²
最简单的隐式
Intent

我们先从最简单的例子开始。
下面的
ImplicitIntentTest
程序用来启动
Android
自带的打电话功能的
Dialer
程序。

ImplicitIntentTest
程序只包含一个java
源文件

ImplicitIntentTest.java,代码如下所示:

package
com.tope.samples.intent.implicit;

import
android.app.Activity;

import
android.content.Intent;

import
android.os.Bundle;

import
android.view.View;

import
android.widget.Button;

public

class
ImplicitIntentTest
extends
Activity

implements
View.OnClickListener{

/**

Called

when

the

activity

is

first

created.

*/

@Override

public

void
onCreate(Bundle savedInstanceState) {

super
.onCreate(savedInstanceState);

setContentView(R.layout.
main
);

Button startBtn = (Button)findViewById(R.id.
dial
);

startBtn.setOnClickListener(
this
);

}

public

void
onClick(View v) {

switch
(v.getId()) {

case
R.id.
dial
:

Intent intent =
new
Intent(Intent.
ACTION_DIAL
);

startActivity(intent);

break
;

default
:

break
;

}

}

}


程序在Intent

的使用上,与上节中的使用方式有很大的不同,即根本不指
定接收者,初始化
Intent
对象时,只是传入参数,设定
Action为
Intent.ACTION_DIAL


Intent intent =
new
Intent(Intent.
ACTION_DIAL
);

startActivity(intent);

这里使用的构造函数的原型如
下:

Intent

(
String

action);

有关
Action
的作用后文将有详细说明,这里读者可暂时将它理解为描
述这个
Intent
的一种方式,这种使用方式看上去比较奇怪,
Intent
的发送者只是指定了
Action为
Intent.ACTION_DIAL
,那么怎么找到
接收者呢?来看下面的例子。

²
增加一个接收者

事实上接收者如果希望能够接
收某些
Intent
,需要像上节例子中一样,通过在
AndroidManifest.xml中增加
Activity

的声明,并设置对应的
Intent Filter

Action
,才能被
Android
的应用程序框架所匹配。为了证明这一点,我们修改上一

SimpleIntentTest
程序中的
AndroidManifest.xml
文件,将
TestActivity
的声明部分改为:

<
activity

android:name
=
".TestActivity"
>

<
intent-filter
>

<
action

android:name
=
"android.intent.action.DEFAULT"

/>

<
action

android:name
=
"android.intent.action.DIAL"

/>

<
category

android:name
=
"android.intent.category.DEFAULT"

/>

</
intent-filter
>

</
activity
>

修改完之后注意要重新安装
SimpleIntentTest
程序的apk
包,然后再尝试运行

ImplicitIntentTest
程序(不是
SimpleIntentTest
程序),运行的截图如图
5.5
所示:



图5.5

修改后的运行截图

这个截图中的第二幅表示可以
选择
Dialer
或者
SimpleIntentTest
程序来完成
Intent.ACTION_DIAL
,也就是说,针对
Intent.ACTION_DIAL,
Android

框架找到了两个符合条件的
Activity,因此它将这两个
Activity
分别列出,供用户选择。

回过头来看我们是怎么做到这
一点的。我们仅仅在
SimpleIntentTest
程序的
AndroidManifest.xml
文件中增加了下面的两行:

<
action

android:name
=
"android.intent.action.DIAL"

/>

<
category

android:name
=
"android.intent.category.DEFAULT"

/>

这两行修改了原来的
Intent Filter,这样这个
Activity

才能够接收到我们发送的
Intent
。我们通过这个改动及其作用,可以进一步理解隐式
Intent,
Intent Filter

Action, Category

等概念——
Intent
发送者设定
Action
来说明将要进行的动作,而
Intent
的接收者在
AndroidManifest.xml
文件中通过设定
Intent Filter来声明自己能接收哪些
Intent

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: