Intent自动不同组件的方法
组件名称 | 方法名称 |
Activity | startActivity() startActivityForResult() |
Srvice | startService() bindService() |
Broadcasts | sendBroadcast() sendOrderedBroadcast() sendStickyBroadcast() |
Intent由以下几个部分组成:动作(Action)、数据(Data)、分类(Category)、类型(Type)、组件(Component)和扩展信息(Extra)。通过这些可以启动其他组件并携带信息。
Intent在寻找目标组件时有两种方法:第一,通过组件名称直接指定;第二,通过Intent Filter过滤指定。
一、Intent对象及其属性
Intent是对它要完成操作的一种抽象描述,我们可以使用Intent来启动一个Activity、发起一个Broadcast和启动或绑定一个Service。Intent使应用程序代码在运行时动态绑定成为可能,这也降低了不同代码之间的耦合性。Intent最常用的方法是用来启动一个Activity。Intent封装了它要执行动作的属性,这些属性最常见的是Action和Data。
1、Intent的ComponentName属性
后面我们将介绍Intent的查找组件策略,其中一种方法是显示查找,就是直接通过组件名称(Component Name)来查找。Intent的组件对象名称由ComponentName类来封装,组件名称包含包名和类名,被声明在AndroidManifest.xml文件中。
public class TestIntent_ComponentName extends Activity {
private Button myButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myButton = (Button) findViewById(R.id.componentNameButton01);
myButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
//实例化组件名称
ComponentName cn = new ComponentName(TestIntent_ComponentName.this, "mx.android.ch06.TestIntent_ComponentNameAnother");
Intent intent = new Intent();
//为Intent设置组件名称属性
intent.setComponent(cn);
startActivity(intent);
}
});
}
}
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/componentNameButton01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试Intent的组件名称属性" />
</LinearLayout>
public class TestIntent_ComponentNameAnother extends Activity {
private TextView myTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.other);
//获得Intent
Intent intent = getIntent();
//获得组件名称属性
ComponentName cn = intent.getComponent();
//获得包名称
String packageName = cn.getPackageName();
//获得类名称
String className = cn.getClassName();
myTextView = (TextView) findViewById(R.id.componentNameTextView01);
myTextView.setText("组件包名称:" + packageName + "\n" + "组件类名称:" + className);
}
}
other.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/componentNameTextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="" />
</LinearLayout>
2、Intent的Action属性
Action是指Intent要完成的动作,是一个字符串常量。在Intent类里面定义了大量的Action常量属性。例如,ACTION_CALL(打电话)、ACTION_EDIT(编辑数据)ACTION_BATTERY_LOW(电量低广播Action)等。我们也可以定义自己的Action来使用。
我们可以使用setAction()来设置Intent的Action属性,使用getAction()来获得Intent的Action属性。
1)、自定义Action属性
我们可以为Intent定义一个Action属性来访问,Action属性是一个字符串,我们在程序中定义,并在要访问组件(例如Activity)的InterFilter中声明就可以了。下面的实例将演示如何自定义一个Inter Action属性。
public class TestIntent_Action1 extends Activity {
//定义Action属性常量
private static final String MY_ACTION="mx.android.ch06.MY_ACTION";
private Button myButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myButton = (Button) findViewById(R.id.actionButton01);
myButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent intent = new Intent();
//为Intent设置Action属性
intent.setAction(MY_ACTION);
startActivity(intent);
}
});
}
}
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/actionButton01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试Intent的Action属性" />
</LinearLayout>
public class TestIntent_Action1Another extends Activity {
private TextView myTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.result);
//获得Intent对象
Intent intent = getIntent();
//获得Action
String action = intent.getAction();
myTextView = (TextView) findViewById(R.id.actionTextView01);
myTextView.setText(action);
}
}
result.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/actionTextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="" />
</LinearLayout>
AndroidManifest.xml
添加
<activity android:name=".TestIntent_Action1Another">
<intent-filter>
<action android:name="mx.android.ch06.MY_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
2)、访问系统Action属性
例如:我们实现了一个发送短信的程序,在这个程序当中,我们需要选择对方的电话号码,这时候我们可以调用系统电话本来查找电话号码。我们可以使用Intent.ACTION_GET_CONTENT常量调用系统Activity来实现该功能。
public class TestIntent_Action2 extends Activity {
private Button myButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myButton = (Button) findViewById(R.id.action2Button01);
myButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("vnd.android.cursor.item/phone");
startActivity(intent);
}
});
}
}
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/action2Button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试Intent的Action属性" />
</LinearLayout>
3、Intent的Data属性
Intent的Data属性是执行动作的URI和MIME类型,不同的Action有不同的Data数据指定。例如,ACTION_EDIT Action应该和要编辑的文档URI Data匹配,ACTION_VIEW应用和要显示的URI匹配。
Intent的Action和Data属性匹配
Action属性 | Data属性 | 说明 |
ACTION_VIEW | content://contacts/people1 | 显示_id为1的联系人信息 |
ACTION_DIAL | content://contacts/people1 | 将_id为1的联系人电话号码显示在拨号界面中 |
ACTION_VIEW | tel:123 | 显示电话为123的联系人信息 |
ACTION_VIEW | http://www.google.com | 在浏览器中浏览该网址 |
ACTION_VIEW | file:///sdcard/mymusic.mp3 | 播放mp3 |
ACTION_VIEW | geo:39.3256,116.2312 | 显示地图 |
4、Intent的Category属性
Intent的Category属性是一个执行Action的附加信息。例如,CATEGORY_LAUNCHER意味着在加载程序时,Activity出现在最上面。还有CATEGORY_HOME则表示回到Home界面。
当我们创建一个应用程序的时候,程序第一个加载的Activity中有如下代码:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
上述代码表明当前的Activity是加载程序时第一个出现的界面。
下面的实例演示了如何回到Home界面。
public class TestIntent_Gategory extends Activity {
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_category);
btn = (Button) findViewById(R.id.categoryButton01);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
//实例化Intent
Intent intent = new Intent();
//添加Action属性
intent.setAction(Intent.ACTION_MAIN);
//添加Category属性
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);
}
});
}
}
5、Intent的Extras属性
Intent的Extras属性是添加一些组件的附加信息。例如,如果我们要通过一个Activity来发送一个Email,就可以通过Extras属性来添加subject和body。
下面的实例在第一个Activity的EditText中输入年龄,该年龄保存在Intent的Extras属性中,当单击按钮时在第二个Activity中从Intent的属性中取出来显示。
public class TestIntent_Extras extends Activity {
private EditText myEditText;
private Button myButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myEditText = (EditText) findViewById(R.id.extrasEditText);
myButton = (Button) findViewById(R.id.extrasAButton);
myButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent intent = new Intent();
//设置Intent的class属性,跳转到另一个Activity
intent.setClass(TestIntent_Extras.this, TestIntent_ExtrasResult.class);
//为Intent添加额外信息
intent.putExtra("age", myEditText.getText().toString());
startActivity(intent);
}
});
}
}
public class TestIntent_ExtrasResult extends Activity {
private TextView myTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.result);
myTextView = (TextView) findViewById(R.id.extrasresultTextView01);
Intent intent = getIntent();
myTextView.setText(intent.getStringExtra("age"));
}
}
二、标准系统Activity Action应用
Activity Action常量
常量名称 | 常量值 | 意义 |
ACTION_MAIN | android.intent.action.MAIN | 应用程序的入口 |
ACTION_VIEW | android.intent.action.VIEW | 显示数据给用户 |
ACTION_ATTACH_DATA | android.intent.action.ATTACH_DATA | 指明附加给其他地方的一些数据 |
ACTION_EDIT | android.intent.action.EDIT | 显示可编辑的数据 |
ACTION_PICK | android.intent.action.PICK | 选择数据 |
ACTION_CHOOSER | android.intent.action.CHOOSER | 显示一个Activity选择器 |
ACTION_GET_CONTENT | android.intent.action.GET_CONTENT | 获得内容 |
ACTION_DIAL | android.intent.action.GET_CONTENT | 获得打电话面板 |
ACTION_CALL | android.intent.action.DIAL | 直接打电话 |
ACTION_SEND | android.intent.action.SEND | 直接发送短信 |
ACTION_SENDTO | android.intent.action.SENDTO | 选择发送短信 |
ACTION_ANSWER | android.intent.action.ANSWER | 应答电话 |
ACTION_INSERT | android.intent.action.INSERT | 插入数据 |
ACTION_DELETE | android.intent.action.DELETE | 删除数据 |
ACTION_RUN | android.intent.action.RUN | 运行数据 |
ACTION_SYNC | android.intent.action.SYNC | 同步数据 |
ACTION_PICK_ACTIVITY | android.intent.action.PICK_ACTIVITY | 选择Activity |
ACTION_SEARCH | android.intent.action.SEARCH | 搜索 |
ACTION_WEB_SEARCH | android.intent.action.WEB_SEARCH | Web搜索 |
ACTION_FACTORY_TEST | android.intent.action.FACTORY_TEST | 工厂测试入口点 |
1、和打电话相关的标准Activity Action应用
这里我们通过一个实例来演示和打电话相关的标准Activity Action的应用,我们可以通过这些标准Activity Action来查看联系人信息(ACTION_VIEW)、编辑联系人信息(ACTION_EDIT)、显示打电话界面(ACTION_DIAL)、直接拨打电话(ACTION_CALL)等。
下面的实例定义一个Activity继承ListActivity,在ListView中显示不同的应用类型。当该选择项被选择时启动一个Activity演示效果。
public class TestActivityAction_Tel extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//菜单项数组
String[] menus = {"查看电话信息","编辑电话信息","显示拨打电话界面","直接打电话","访问浏览器","访问地图"};
//将菜单项数组设置为ListView的列表项展示
setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,menus));
getListView().setTextFilterEnabled(true);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
//实例化Intent
Intent intent = new Intent();
//声明Uri
Uri uri;
//声明数据字符串
String data;
switch(position){
//查看_id为1的用户电话信息
case 0:
//字符串Uri
data = "content://contacts/people/1";
//将字符串Uri转换为Uri实例
uri = Uri.parse(data);
//设置Intent的Action属性
intent.setAction(Intent.ACTION_VIEW);
//设置Intent的Data属性
intent.setData(uri);
//启动Activity
startActivity(intent);
break;
//编辑_id为1的用户电话信息
case 1:
data = "content://contacts/people/1";
uri = Uri.parse(data);
intent.setAction(Intent.ACTION_EDIT);
intent.setData(uri);
startActivity(intent);
break;
//显示拨打电话界面
case 2:
data = "tel:18217301798";
uri = Uri.parse(data);
intent.setAction(Intent.ACTION_DIAL);
intent.setData(uri);
startActivity(intent);
break;
//直接拨打电话
case 3:
data = "tel:18217301798";
uri = Uri.parse(data);
intent.setAction(Intent.ACTION_CALL);
intent.setData(uri);
startActivity(intent);
break;
//访问浏览器
case 4:
data = "http://www.google.com";
uri = Uri.parse(data);
intent.setAction(Intent.ACTION_VIEW);
intent.setData(uri);
startActivity(intent);
break;
//访问地图
case 5:
data = "geo:39.92,116.46";
uri = Uri.parse(data);
intent = new Intent(Intent.ACTION_VIEW,uri);
startActivity(intent);
break;
default:
break;
}
}
}
2、访问浏览器和地图
浏览器的URI格式是:http://www.xxx.yyy。地图URI的格式是:geo:经度,纬度。
实例在上面例子中
3、发邮件
我们可以使用内置的Gmail引擎来发送邮件,也可以使用SMTP来发送E-mail。
public class TestActivityAction_SendEmail extends Activity {
private EditText sendToText,subjectText,contentText;
private Button myButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
sendToText = (EditText) findViewById(R.id.emailSendToPeople);
subjectText = (EditText) findViewById(R.id.emailSubject);
contentText = (EditText) findViewById(R.id.emailContent);
myButton = (Button) findViewById(R.id.emailSendButton);
myButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
//邮件目标地址
String to = sendToText.getText().toString();
//邮件主题
String subject = subjectText.getText().toString();
//邮件内容
String content = contentText.getText().toString();
//创建Intent
Intent emaillIntent = new Intent(android.content.Intent.ACTION_SEND);
//设置内容类型
emaillIntent.setType("plain/text");
//设置额外信息
emaillIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{to});
emaillIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
emaillIntent.putExtra(android.content.Intent.EXTRA_TEXT, content);
//启动Activity
startActivity(Intent.createChooser(emaillIntent, "发送邮件..."));
}
});
}
}
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/emailTextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="收件人:" />
<EditText
android:id="@+id/emailSendToPeople"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="" />
<TextView
android:id="@+id/emailTextView02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="主题:" />
<EditText
android:id="@+id/emailSubject"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="" />
<TextView
android:id="@+id/emailTextView03"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="内容:" />
<EditText
android:id="@+id/emailContent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="" />
<Button
android:id="@+id/emailSendButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送" />
</LinearLayout>
3、Intent的实现策略
Intent是如何找到目标组件的呢?Intent寻找目标组件的方式可以分为两种。一种是显式Intent,这种方式是通过指定Intent组件名称来实现的,这种方式在我们上面讲到的Intent组件名称属性时提到过,它一般用在源组件知道目标组件名称的前提下,一般是在相同应用程序内容部实现的。那么不同的应用程序之间呢?我们并不知道目标组件的名称,又该如何找到目标组件呢?在这种情况下,我们使用第二种方式,隐式Intent,这种方式是通过Intent Filter实现的。
在使用Intent Filter进行过滤Intent通常考虑的三个属性是Action、Data和Category。
对于显式Intent,Android不需要去做解析,因为目标组件已经很明确,Android需要解析的是那些间接Intent,通过解析,将Intent映射给可以处理此Intent的Activity、IntentReceiver或Service。
Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。在这个解析过程中,Android是通过Intent的Action、Category、Data这三个属性来进行判断的,判断方法说明如下:
1)、Action测试
如果Intent指明定了Action,则目标组件IntentFilter的Action列表中就必须包含这个Action,否则不能匹配。如果Intent中没有指定Action,则Action测试自动通过。
Action的声明在AndroidManifest.xml中。每个Android组件(例如Activity、Service等)都会有一个 <intent-filter>元素来声明Intent过滤器。Action作为 <intent-filter>的子元素来声明,例如:
<intent-filter>
<action android:name="mx.android.ch06.TEST_ACTION1" />
<action android:name="mx.android.ch06.TEST_ACTION2" />
<action android:name="mx.android.ch06.TEST_ACTION3" />
</intent-filter>
</intent-filter> 列表中的Action不能为空,否则程序将阻塞不能通过。如果Intent对象指定了一个Action属性,那么要想通过Action测试,Intent对象指定的属性必须和IntentFilter中的一个匹配,否则不能通过测试。如果Intent对象没有指定Action属性,则自动通过测试。
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/actiontest01Button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试Intent Filter" />
</LinearLayout>
main1.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/actiontest02TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试 Intent Filter" />
</LinearLayout>
AndroidManifest.xml
<intent-filter>
<action android:name="mx.android.ch06.TEST_ACTION1" />
<action android:name="mx.android.ch06.TEST_ACTION2" />
<action android:name="mx.android.ch06.TEST_ACTION3" />
<data android:scheme="content" android:path="mx.android.ch06/abc" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
public class TestActionTest01 extends Activity {
private static final String ACTION1 = "mx.android.ch06.TEST_ACTION1";
private static final String ACTION2 = "mx.android.ch06.TEST_ACTION2";
private static final String ACTION3 = "mx.android.ch06.TEST_ACTION3";
private static final String CATEGORY1 = "mx.android.ch06.CATEGORY1";
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_actiontest01);
btn = (Button) findViewById(R.id.actiontest01Button01);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent intent = new Intent();
//1、Action测试(去掉Intent的Action属性,添加一个Data属性,仍然能成功启动)
//intent.setAction(ACTION1);
Uri data = Uri.parse("content://mx.android.ch06/abc");
intent.setData(data);
//2、Category测试
intent.addCategory(CATEGORY1);
//3、Data测试
intent.setAction("android.intent.action.VIEW");
intent.setData(Uri.parse("http://www.google.com"));
startActivity(intent);
}
});
}
}
这里我们声明了三个字符串常量,分别和AndroidManifest.xml中的Action属性相对应,并在onClick()方法中实例化Intent对象,为其设置Action属性。
2)、Category测试
Category属性也是作为 <intent-filter>子元素来声明的。例如:
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
在Intent对象中出现的Category属性在InterFilter中必须出现,否则不能通过测试,在上面的实例基础上,我们在Activity中为Intent添加了一个Category(见上面实例),在AndroidManifest.xml<intent-filter>中添加了三个<category>。
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="mx.android.ch06.CATEGORY1" />
注意:“android.intent.category.DEFAULT”属性是启动Activity默认的属性,这个必须添加,否则Category测试失败。
3)、Data测试
Data属性是Android要访问的数据,和Action和Category声明方式相同,也是在 <intent-filter>中。
Data属性的生命值中要指定访问数据的URI和MIME类型。也可以在<category>元素中通过一些属性(android:scheme、android:path、android:port、android:host等)来设置,通过这些属性来对应一个典型的URI格式:scheme://host:port/path,例如:http://www.google.com。
Data测试规则说明如下:
- 如果Intent对象中没有包含Data,<intent-filter>列表中也没有包含Data,则通过测试,这种情况一般是通过Action属性来过滤的。
- 如果Intent对象包含URI但是没有包含类型,并且类型不能从URI中自动识别,那么<intent-filter>列表中也只能包含URI,这样才能通过测试。
- 相反,如果Intent对象只包含类型,没有包含URI,那么在<intent-filter>中也只能包含类型,不能包含URI。
- 如果Intent对象中既包含URI又包含Type,那么<intent-filter>中也必须二者都包含才能通过测试。