[关闭]
@stepbystep 2015-01-27T21:35:17.000000Z 字数 16238 阅读 4383

Intent与IntentFilter详解

Android

说明: Intent用于激活组件,包括Activity、Service-服务、BroadcastReceiver-广播接受者,即四大组件中除了内容提供者ContentProvider

使用Android系统内置的Intent来完成发送短信、拨打电话、浏览网页和查看地图等基本功能,利用其Data和Action属性有关

激活组件时,用Intent的Data属性或Extra属性传递数据


课前复习

  1. Intent intent=new Intent(this,NewActivity.class);
  2. this.startActivity(intent);

Intent的六大属性

属性字段 名称 数据类型 获取方法 设置方法
ComponentName 组件名 ComponentName getComponent() setComponetn() setClass() setClassName()
Action 动作 String getAction() setAction()
Data 数据 URL getData() getType() setData() setType() setDataAndType()
Category 分类 String getCategories() addCategory() removeCategory()
Extra 额外信息 键值对形式<String, value>, 2. Bundle get*Extra(); getExtras()->Bundle put*Extra() putExtras()
Action 动作 String getAction() setAction()

Action 动作

Action值 目标组件 功能
ACTION_CALL Activity 拨打Data属性指定的电话号码
ACTION_MAIN Activity 设置Activity做为程序的入口
ACTION_DIAL Activity 将Data属性中的电话号码放入拨打界面,显示给用户,供用户手动拨出这个号码
ACTION_View Activity 使用与Data匹配的应用程序显示Data指定的数据,如网页,播放mp3
ACTION_SEND Activity 发送数据
ACTION_SCREEN_ON Broadcast Receiver 屏幕被打开

当然,也可以自定义动作(自定义的动作在使用时,需要加上包名作为前缀,如"com.example.shiyanlou.SHOW_COLOR”),并可定义相应的Activity来处理我们的自定义动作。

ComponentName 组件名

  1. // 第一种隐式
  2. Intent intent = new Intent(this, MainActivity.class);
  3. startActivity(intent);
  4. // 第二种方式
  5. Intent intent = new Intent();
  6. ComponentName componentName = new ComponentName(this, MainActivity.class);
  7. intent.setComponent(componentName);
  8. startActivity(intent);

不设置组件名的Intent叫做隐式Intent,这种Intent主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。在这个解析过程中,Android是通过Intent的action、type、category这三个属性来进行判断的。

  1. Intent intent = new Intent();
  2. intent.setAction("com.example.shiyanlou.test");
  3. startActivity(intent);

使用场合:
显式意图比较适合于在用一个应用程序中,由于指定了目标组件,启动速度比较快
隐式意图即适合同一个应用和可以用在不同的应用程序之间,建议在不同的应用程序之间激活组件或者传递数据时使用隐式意图。

Data 数据

Data值 说明 示例
file:/// 本地文件数据,后接文件路径 file:///mnt/sdcard/mp3/alone.mp3
http:// 超文本链接,接网络资源的地址 http://www.baidu.com
smsto:// 短信,后接目标号码 smsto://182*
tel:// 电话,后接目标号码 tel://182*
mailto:// 电子邮件,后接接收人地址 mailto://193*@qq.com
content:// 内容,后接内容的定位 content://contacts/people/*

Action与Data的配合使用

  1. startActivity(new Intent(Intent.ACTION_VIEW, URI.parse("file:///mnt/adcard/mp3/alone.mp3")));
  1. startActivity(new Intent(Intetn.ACTION_VIEW, URI.parse("http://www.baidu.com")));
  1. startActivity(new Intent(Intetn.ACTION_CALL, URI.parse("tel://10086")));
  1. startActivity(new Intent(Intetn.ACTION_SENDTO, URI.parse("smsto://10086")));

Category 分类

Category值 功能
CATEGORY_BROWSABLE 目标Activity可以被浏览器使用超链接触发,并能显示相应的数据
CATEGORY_HOME 显示Home界面的Activity,可以用来显示Home界面
CATEGORY_LAUNCHER 一个任务栈栈底的Activity,一般是程序的入口Activity
CATEGORY_DEFAULT 默认的,Android系统会给隐式Intent自动加上这个值

- Category主要被设置在IntentFilter中
- 对于显式Intent,如果不指定Category,则无论IntentFilter的内容是什么都是匹配的。
- 对于隐式Intent,Android会自动加上Category_DEFAULT,这样的话,IntentFilter中也必须加上此条目,否则匹配就会失败

Extra 额外信息

  1. // 方式一
  2. // 发送方
  3. Intent intent = new Intent(this, MainActivity.class);
  4. intent.putExtra("name", "shiyanlou");
  5. intent.putExtra("time", "2015-01-21 14:22");
  6. intent.putExtra("lines", 1000);
  7. startActiviy(intent);
  8. // 接收方
  9. Intent intent = getIntent();
  10. String name = intent.getStringExtra("name");
  11. String time = intetn.getStringExtra("time");
  12. int lines = intent.getIntExtra("lines", 0);
  13. // 方式二
  14. // 发送方
  15. Intent intent = new Intent(this, MainActivity.class);
  16. Bundle bundle = new Bundle();
  17. bundle.putString("name", "shiyanlou");
  18. bundle.putString("time", "2015-01-21 14:22");
  19. bundle.putInt("lines", 1000);
  20. intent.putExtras(bundle);
  21. startActivity(intent);
  22. // 接收方
  23. Intent intent = getIntent();
  24. Bundle bundle = intent.getExtras();
  25. String name = bundle.getString("name");
  26. String time = bundle.getString("time");
  27. int lines = bundle.getInt("lines", 0);

Flag 标识

Intent发送短息,拨打电话,浏览网页,播放多媒体实验

本实验主要是为了验证Action与Data配合使用的实验,当然实验中也插入了一些其他的知识供大家学习。

MainActivity.java

  1. package com.example.shiyanlou.intentdemo;
  2. import android.app.Activity;
  3. import android.content.Intent;
  4. import android.net.Uri;
  5. import android.os.Bundle;
  6. import android.os.Environment;
  7. import android.telephony.SmsManager;
  8. import android.text.TextUtils;
  9. import android.view.View;
  10. import android.widget.EditText;
  11. import android.widget.Toast;
  12. import java.io.File;
  13. import java.io.FileOutputStream;
  14. import java.io.InputStream;
  15. import java.io.OutputStream;
  16. import java.util.ArrayList;
  17. public class MainActivity extends Activity{
  18. private EditText editText;
  19. private EditText editPhoneNumber;
  20. @Override
  21. protected void onCreate(Bundle savedInstanceState) {
  22. super.onCreate(savedInstanceState);
  23. // 导航布局文件资源
  24. setContentView(R.layout.activity_main);
  25. // findView,给editText赋值等
  26. editText = (EditText) findViewById(R.id.edit_text);
  27. editPhoneNumber = (EditText) findViewById(R.id.edit_phone_number);
  28. // 注册点击事件,一个listenr变量可以给多个view使用,
  29. // 因为在onClick事件中调用了switch case语句,通过View.getId()函数来判断执行
  30. ClickHandleClass listener = new ClickHandleClass();
  31. findViewById(R.id.btn_call).setOnClickListener(listener);
  32. findViewById(R.id.btn_openg_url).setOnClickListener(listener);
  33. findViewById(R.id.btn_pay_vedio).setOnClickListener(listener);
  34. findViewById(R.id.btn_send_msg).setOnClickListener(listener);
  35. }
  36. /**
  37. * 从raw文件拷贝数据到fullPath, 需要注册权限
  38. * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  39. * <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
  40. */
  41. private void writeAssetsToSdcard(String fullPath){
  42. File audioFile = new File(fullPath);
  43. // 如果文件已经存在就不用拷贝了
  44. if(audioFile.exists()){
  45. return;
  46. }
  47. // 获取当前文件所在的目录函数用getParent()
  48. File dir = new File(audioFile.getParent());
  49. if (!dir.exists()) {
  50. // 创建目录
  51. if (!dir.mkdir()) {
  52. // 创建目录失败, Toast吐司可以在手机底部弹出一个提示
  53. Toast.makeText(MainActivity.this, "创建mp3目录失败", Toast.LENGTH_SHORT).show();
  54. System.out.print("**"+dir.getAbsolutePath()+"**");
  55. return;
  56. }
  57. }
  58. // 下面执行拷贝数据的函数
  59. try {
  60. OutputStream myOutput = new FileOutputStream(fullPath);
  61. InputStream myInput = getResources()
  62. .openRawResource(R.raw.alone);
  63. byte[] buffer = new byte[1024];
  64. int length;
  65. while ((length = myInput.read(buffer)) > 0) {
  66. myOutput.write(buffer, 0, length);
  67. }
  68. myOutput.close();
  69. myInput.close();
  70. } catch (Exception e) {
  71. /**
  72. * 如果没有注册权限 java.io.FileNotFoundException: /storage/sdcard0/alone.mp3: open failed: EACCES (Permission denied)
  73. */
  74. e.printStackTrace();
  75. }
  76. }
  77. /**
  78. * 打开手机存储卡中的文件, 内部自己创建了Intent,并启动了Intent
  79. */
  80. private void openMp3OnSdcard(){
  81. Intent intent = new Intent();
  82. intent.setAction(Intent.ACTION_VIEW);
  83. //intent.setDataAndType(Uri.fromFile(new File("file:///mnt/sdcard/"+fullPath)), "audio/mp3"); //name.mp3值文件名
  84. //使用api获得手机的sdcard路径适用于所有手机:/mnt/sdcard
  85. String path= Environment.getExternalStorageDirectory().getAbsolutePath();
  86. String fileName=new File(path+File.separator+ "raw/alone.mp3").getAbsolutePath(); //name.mp3值文件名
  87. // 将raw文件写入目录
  88. writeAssetsToSdcard(fileName);
  89. // 此处设置Data和Type
  90. /*
  91. * setType()在设置type时会自动清空data,同理在setData()设置data时也会自动清空type,
  92. * 因此如果想同时设置data和type则只能使用setDataAndType(data,type);
  93. */
  94. //Type表示类型,如"audio/mp3",系统根据此来转换资源,如果省略,系统会根据URI的内容去添加Type的值
  95. intent.setDataAndType(Uri.parse("file://" + fileName),"audio/mp3");
  96. //隐式意图使用这个方法启动Activity时会自动添加一个Category:android.intent.category.DEFAULT
  97. startActivity(intent);
  98. }
  99. /**
  100. * 拨打电话的程序
  101. * @param phoneNumber 电话号码
  102. */
  103. private void call(String phoneNumber){
  104. /*
  105. com.example.shiyanlou.intentdemo E/AndroidRuntime﹕ FATAL EXCEPTION: main
  106. java.lang.SecurityException: Permission Denial: starting Intent
  107. { act=android.intent.action.CALL dat=tel:xxxxxxxxxxxxx cmp=com.android.phone/.OutgoingCallBroadcaster }
  108. from ProcessRecord{40fe1510 866:com.example.shiyanlou.intentdemo/u0a10047}
  109. (pid=866, uid=10047) requires android.permission.CALL_PHONE
  110. */
  111. Intent intent = new Intent();
  112. //指定隐式意图的动作:拨打电话(转到电话拨号器的界面并填充号指定的号码)
  113. //intent.setAction(Intent.ACTION_DIAL);
  114. //指定拨打电话的数据(模式+电话号码)
  115. intent.setAction(Intent.ACTION_CALL);
  116. // 设置intent的Data属性
  117. Uri data=Uri.parse("tel://"+phoneNumber);
  118. intent.setData(data);
  119. // 拨打电话
  120. startActivity(intent);
  121. }
  122. /**
  123. * 短信发送函数
  124. * @param phoneNumber 电话号码
  125. * @param text 短信内容
  126. */
  127. private void sendMsg(String phoneNumber, String text){
  128. /*
  129. *
  130. * com.example.shiyanlou.intentdemo E/AndroidRuntime﹕ FATAL EXCEPTION: main
  131. * java.lang.SecurityException: Sending SMS message: uid 10047 does not have android.permission.SEND_SMS
  132. * 如果报上面的错,则说明没有注册发送短信的权限
  133. * SmsManager采用单例设计模式
  134. */
  135. SmsManager smsManager=SmsManager.getDefault();
  136. /*
  137. * 进行普通短信的发送,如果短信内容太长,超过160字母后就会发生异常:
  138. * E/AndroidRuntime(439): java.lang.NullPointerException
  139. * at android.telephony.SmsManager.sendTextMessage(SmsManager.java:87),
  140. * 解决方式为将短信内容进行拆分,SmsManager内部有封装好的方法: divideMessage(String content);
  141. */
  142. if(text.length()>70){
  143. //进行短信的拆分
  144. ArrayList<String> msgs= smsManager.divideMessage(text);
  145. for(String msg:msgs){
  146. smsManager.sendTextMessage(phoneNumber, null, msg, null, null);
  147. }
  148. }else{
  149. smsManager.sendTextMessage(phoneNumber, null, text, null, null);
  150. }
  151. // Toast吐司可以在手机底部弹出一个提示
  152. Toast.makeText(MainActivity.this, "短信发生成功! ", Toast.LENGTH_SHORT).show();
  153. }
  154. /**
  155. * 利用Intent打开一个网页
  156. * @param url 网页地址
  157. */
  158. private void opengWebByUrl(String url){
  159. Intent intent = new Intent();
  160. intent.setAction(Intent.ACTION_VIEW);
  161. intent.setData(Uri.parse(url));
  162. startActivity(intent);
  163. }
  164. // 定义内部类,实现点击事件
  165. class ClickHandleClass implements View.OnClickListener{
  166. @Override
  167. public void onClick(View v) {
  168. String str = editText.getText().toString().trim();
  169. String phone = editPhoneNumber.getText().toString().trim();
  170. switch (v.getId()){
  171. case R.id.btn_call:
  172. if(TextUtils.isEmpty(phone)){
  173. // 使editPhoneNumber获取焦点
  174. editPhoneNumber.requestFocus();
  175. editPhoneNumber.setError("拨打号码不能为空!");
  176. return;
  177. }
  178. // 调用拨打电话的程序
  179. call(phone);
  180. break;
  181. case R.id.btn_openg_url:
  182. if(TextUtils.isEmpty(str)){
  183. // editText获取焦点
  184. editText.requestFocus();
  185. editText.setError("网址不能为空!");
  186. editText.setHint("示例:http://www.baidu.com");
  187. return;
  188. }
  189. try {
  190. opengWebByUrl(str);
  191. }catch (Exception e){
  192. Toast.makeText(MainActivity.this, "网址解析异常", Toast.LENGTH_SHORT).show();
  193. editText.setError("示例:http://www.baidu.com");
  194. }
  195. break;
  196. case R.id.btn_pay_vedio:
  197. // 播放sdcard根目录的alone.mp3文件, 为了防止没有这样的文件,本工程会将raw文件中带的alone.mp3文件拷贝过去!
  198. openMp3OnSdcard();
  199. break;
  200. case R.id.btn_send_msg:
  201. if(TextUtils.isEmpty(phone)){
  202. editPhoneNumber.requestFocus();
  203. editPhoneNumber.setError("短信号码不能为空!");
  204. return;
  205. }
  206. if(TextUtils.isEmpty(str)){
  207. editText.requestFocus();
  208. editText.setError("短信内容不能为空!");
  209. return;
  210. }
  211. // 调用发送短信的函数
  212. sendMsg(phone, str);
  213. break;
  214. }
  215. }
  216. }
  217. }

activity_main.xml

  1. <!-- 外面用ScrollView的原因是有的屏幕太小了... 显示不下这么多控件,他可以在竖直方向是滚动内容
  2. 注意,它只能有一个子组件,该布局文件中就是LinearLayout
  3. -->
  4. <ScrollView
  5. xmlns:android="http://schemas.android.com/apk/res/android"
  6. xmlns:tools="http://schemas.android.com/tools"
  7. android:layout_width="match_parent"
  8. android:layout_height="wrap_content" >
  9. <!-- 记得设置android:orientation="vertical"-->
  10. <LinearLayout android:layout_width="match_parent"
  11. android:layout_height="match_parent"
  12. android:padding="20dp"
  13. android:orientation="vertical">
  14. <!--EditText可以输入文本的控件,inputType表示输入的类型,设置为phone,number等-->
  15. <!--android:textStyle="bold":改变字体样式(normal(正常),bold(加粗),italic(倾斜))-->
  16. <!--textColor #AA2988e4":设置字体颜色颜色,8位模式时前两位表示透明度-->
  17. <!--A(Alpha:透明度;00完全透明;FF:完全不透明)RGB-->
  18. <EditText
  19. android:id="@+id/edit_phone_number"
  20. android:layout_width="match_parent"
  21. android:layout_height="wrap_content"
  22. android:hint="请输入电话号码"
  23. android:textColor="#AA2988e4"
  24. android:textStyle="bold"
  25. android:inputType="phone"
  26. />
  27. <!--此处的android:singleLine="false"-->
  28. <!--android:scrollbars="vertical"是为了防止短信内容过多,可以在竖直方向上滚动内容-->
  29. <!--android:layout_marginTop="20dp" 表示距离上一个控件的间隔为20dp-->
  30. <EditText
  31. android:id="@+id/edit_text"
  32. android:layout_width="match_parent"
  33. android:layout_height="100dp"
  34. android:layout_marginTop="20dp"
  35. android:hint="请输入文本"
  36. android:gravity="start"
  37. android:textColor="#AA2988e4"
  38. android:singleLine="false"
  39. android:scrollbars="vertical"
  40. />
  41. <Button
  42. android:id="@+id/btn_call"
  43. android:text="拨打电话"
  44. android:background="#2988e4"
  45. android:textColor="#fff"
  46. android:layout_marginTop="20dp"
  47. android:layout_width="match_parent"
  48. android:layout_height="wrap_content" />
  49. <Button
  50. android:id="@+id/btn_send_msg"
  51. android:text="发送短信"
  52. android:background="#2988e4"
  53. android:textColor="#fff"
  54. android:layout_marginTop="20dp"
  55. android:layout_width="match_parent"
  56. android:layout_height="wrap_content" />
  57. <Button
  58. android:id="@+id/btn_openg_url"
  59. android:text="打开网页"
  60. android:background="#2988e4"
  61. android:textColor="#fff"
  62. android:layout_marginTop="20dp"
  63. android:layout_width="match_parent"
  64. android:layout_height="wrap_content" />
  65. <Button
  66. android:id="@+id/btn_pay_vedio"
  67. android:text="播放多媒体"
  68. android:background="#2988e4"
  69. android:textColor="#fff"
  70. android:layout_marginTop="20dp"
  71. android:layout_width="match_parent"
  72. android:layout_height="wrap_content" />
  73. </LinearLayout>
  74. </ScrollView>

AndroidManefest.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.example.shiyanlou.intentdemo" >
  4. <!--发送短信权限-->
  5. <uses-permission android:name="android.permission.SEND_SMS"/>
  6. <!--拨打电话权限-->
  7. <uses-permission android:name="android.permission.CALL_PHONE"/>
  8. <!--读写储存卡权限-->
  9. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  10. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
  11. <application
  12. android:allowBackup="true"
  13. android:icon="@drawable/ic_launcher"
  14. android:label="@string/app_name"
  15. android:theme="@style/AppTheme" >
  16. <activity
  17. android:name="com.example.shiyanlou.intentdemo.MainActivity"
  18. android:label="IntentDemo">
  19. <intent-filter>
  20. <action android:name="android.intent.action.MAIN" />
  21. <category android:name="android.intent.category.LAUNCHER" />
  22. </intent-filter>
  23. </activity>
  24. </application>
  25. </manifest>

实验结果及分析

进入实验界面如下:
此处输入图片的描述

拨打号码测试
直接点击拨打电话会有如下提示:
此处输入图片的描述

为了测试拨打电话的功能,我们需要再创建并启动一个模拟器。
此处输入图片的描述

启动后的新模拟器如下图
此处输入图片的描述

我们可以看到,每个模拟器上面都有一个5554,5556的编号,这被定义为该手机的号码。拨打号码如下:
此处输入图片的描述

拨打成功界面如下: (可以看到,尾号5554就是拨打过去的电话号码)
此处输入图片的描述

发送短信测试
输入发送好5556,以及短信内容如下:
此处输入图片的描述

等待一会儿,短信就回成功发送,本次发送了两条。
此处输入图片的描述

打开网页测试
因为intent根据http://解析出为打开网页的动作,故而如果网址输入不是http://开头就会出现解析错误
此处输入图片的描述

错误如下:
此处输入图片的描述

输入正确的http://www.baidu.com
此处输入图片的描述

调用系统浏览器,正确跳转到相应页面。
此处输入图片的描述

打开mp3播放测试
点击播放多媒体按钮,会首先将raw目录下的alone.mp3拷贝到sdcard上,再启动系统对应的播放程序,播放音乐。
此处输入图片的描述


IntentFilter

  1. IntentFilter filter = new IntentFilter();
  2. filter.addAction("action_1");
  3. filter.addAction("action_2");
  1. <!-- manifest中实现 -->
  2. <intent-filter>
  3. <action android:name="android.intent.action.MAIN" />
  4. <category android:name="android.intent.category.LAUNCHER" />
  5. </intent-filter>

IntentFilter知识点总结

流程如下:
隐式意图的匹配过程:通过查找功能清单文件中配置的意图过滤器(intent-filter)中的action,category,data和启动组件的意图(intent)进行匹配来决定是否实例化相应的组件.
1. 如果意图过滤器中没有配置任何内容,那么启动的意图中也没有配置任何内容,这时不会匹配成功.
2. 如果意图过滤器中的配置信息和启动这个Activity的意图中的字符串完全匹配,则隐式意图匹配成功(意图过滤器中至少有一个action).
3. 意图中有的配置信息,在意图过滤器中必须有才能匹配成功.意图过滤器中有的内容,意图中不一定都有,只要意图过滤器中的一个action或者category和意图中的action或者category匹配成功即可.总之:意图中有的内容,意图过滤器中必须有才能匹配成功。意图过滤器中有的内容,意图中可以只有部分匹配即可。
4. 一个android组件的配置中可以有多个意图过滤器,只有意图对象和其中任何一个意图过滤器匹配成功即可完成激活组件的工作

本节实验

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注