Intent表明了Android应用的启动“意图”,Android应用将会根据Intent来启动指定组件,至于到底启动哪一个组件,则取决于Intent的各个属性。下面就来介绍一下Intent的各类发展,以及Android如何根据不一样属性来启动相应的组件。Intent是由Component、Action、Data、Category、Extra及Flag六部分组成的,接下来将分别对其进行详细介绍。html
(1)Component namejava
组件名称实际上就是一个ComponentName对象,用于标识惟一的应用程序组件,即指明了指望的Intent组件,这种对象的名称是由目标组件的类名与目标组件的包名组合而成的。须要注意的是Component name是一个可选项,若是被设置,那么Intent对象就显式指定了要转向的组件,若是没有被设置,则Intent对象须要根据其余信息进行筛选查找。android
组件名称经过 setComponent(),setClass(),setClassName()设置,经过getComponent()获取。git
intent.setClassName("com.example.testb", "com.example.testb.MainActivity");
ComponentName com=new ComponentName("com.example.testb","com.example.testb.MainActivity"); intent.setComponent(com);
intent.setClass(this, MainActivity.class);
ComponentName com = intent.getComponent(); String pkgName = com.getPackageName(); String className = com.getClassName();
(2)Actionweb
Action实际上就是一个描述了Intent所触发动做名称的字符串,在Intent类中,已经定义好多字符串常量来表示不一样的Action,固然,开发人员也能够自定义Action,其定义规则一样很是简单。动做名必须是独一无二的字符串,因此,一个好的习惯是使用基于Java包的命名方式的命名系统。数据库
系统定义的Activity Action常量:数组
ACTION_CALL,拨出Data里封装的电话号码,直接打电话出去。浏览器
ACTION_DIAL,跳到拨号界面,未打出去。服务器
ACTION_ANSWER,接听来电。网络
ACTION_DELETE,从容器中删除给定的数据。
ACTION_PICK,从数据中选择一个项目 (item),将被选中的项目返回。
ACTION_DEFAULT,和 ACTION_VIEW 相同,是在数据上执行的标准动做。
ACTION_ALL_APPS,列举全部可用的应用。
ACTION_GET_CONTENT,让用户选择数据并返回。
ACTION_EDIT,为制定的数据显示可编辑界面。
ACTION_BUG_REPORT,显示 activity 报告错误。
ACTION_SEND,由用户指定直接发送方式进行数据发送操做。
ACTION_SENDTO,系统根据不一样的Data类型,经过已注册的对应Application进行数据发送操做。
ACTION_VIEW,向用户显示数据。
ACTION_PICK_ACTIVITY,选择一个 activity,返回被选择的 activity 的类(名)。
ACTION_RUN,运行数据(指定的应用),不管它(应用)是什么。
ACTION_INSERT,在容器中插入一个空项 (item)。
ACTION_WEB_SEARCH,执行 web 搜索。
ACTION_SYNC,执行数据同步。
ACTION_MAIN,声明程序的入口,该Action并不会接收任何数据,同时结束后也不会返回任何数据。
ACTION_CHOOSER, 选择器
Action很大程度上决定了Intent的另外部分的结构, 就像一个方法名决定了它接受的参数和返回值同样. 所以, 最好给Action一个最能反映其做用的名字.一个Intent对象中的Action是使用getAction()和setAction()来读写的。
//打电话 Intent intent = new Intent(); //记得加权限<uses-permission android:name="android.permission.CALL_PHONE"/> //Intent.ACTION_DIAL 这里是跳到拨号界面 //Intent.ACTION_CALL 直接打电话出去 intent.setAction(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:0-123-456-789")); startActivity(intent);
// 选择一张图片并剪切图片 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); intent.putExtra("crop", "true"); // 开启剪切 intent.putExtra("aspectX", 1); // 剪切的宽高比为1:2 intent.putExtra("aspectY", 2); intent.putExtra("outputX", 20); // 保存图片的宽和高 intent.putExtra("outputY", 40); intent.putExtra("output", Uri.fromFile(new File("/mnt/sdcard/temp"))); // 保存路径 intent.putExtra("outputFormat", "JPEG");// 返回格式 startActivityForResult(intent, 0);
//打开相册 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); // 若是要限制上传到服务器的图片类型时能够直接写如:image/jpeg 、 image/png等的类型 intent.setType("image/*"); startActivityForResult(intent, 0);
其它:
MediaStore.ACTION_IMAGE_CAPTURE,打开拍照程序
// 打开拍照程序 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(intent, 0); // 取出照片数据 Bundle extras = intent.getExtras(); Bitmap bitmap = (Bitmap) extras.get("data");
com.android.camera.action.CROP,裁剪
Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*");//uri 图片地址 // 设置裁剪 intent.putExtra("crop", "true"); // aspectX aspectY 是宽高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪图片宽高 intent.putExtra("outputX", 320); intent.putExtra("outputY", 320); intent.putExtra("return-data", true); startActivityForResult(intent, CROP_REQUEST_CODE);
android.provider.Settings.ACTION_WIRELESS_SETTINGS ,进入无线网络设置界面
Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS); startActivityForResult(intent, 0);
Media.RECORD_SOUND_ACTION,打开录音机
Intent intent = new Intent(Media.RECORD_SOUND_ACTION); startActivity(intent);
(3)Data
Data主要是对Intent消息中数据的封装,主要描述Intent的动做所操做到的数据的URI及类型。不一样类型的Action会有不一样的Data封装,例如打电话的Intent会封装tel://格式的电话URI,而ACTION_VIEW的Intent中Data则会封装http:格式的URI。正确的Data封装对Intent匹配请求一样很是重要。
当匹配intent和可以处理intent所带的数据的组件时,知道数据类型(MIME类型)是很重要的。好比,一个展现图像的组件不该该被叫去播放一个音频。
不少状况下,从URI能够看出数据类型,好比content: URIs,表示数据是在设备上,可是是由content provider控制。
Intent.setData(Uri); //一个Uri,Scheme包含在其中 Intent.setType(String); //指定MimeType,好比'image/jpeg', 'audio/mpeg'等 Intent.setDataAndType(Uri, String); //上面二个方法的简便调用方式,一块儿搞进去
geo:latitude,longitude(?q=street+address),打开地图应用程序并显示指定的经纬度(地址)
http://,打开浏览器程序并显示指定的URL
tel:,打开电话应用程序并拨打指定的电话号码
voicemail:,打开电话应用程序并拨下指定语音邮箱的电话号码
mailto:,邮件数据格式,后跟邮件收件人地址。
smsto:,短息数据格式,后跟短信接收号码。
content://,内容数据格式,后跟须要读取的内容。
file://,文件数据格式,后跟文件路径。
market://search?q=搜索条件,在Google Market里搜索应用。
market://details?id=app_id|packagename,打开Google Market直接进入该程序的详细页面。
数据类型也能够显式指定,好比setData()方法指定数据为URI,setType() 指定为MIME type,setDataAndType() 指定它既为URI又为MIME type。读取的时候URI用getData(),MIME type用getType()。
//显示网页 Uri uri = Uri.parse("http://www.oschina.net"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent);
//显示地图(北纬39.9,东经116.3) Uri uri = Uri.parse("geo:39.9,116.3"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent);
//发送短信 记得加上<uses-permission android:name="android.permission.SEND_SMS"/> Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("smsto:0800000123")); intent.putExtra("sms_body", "The SMS text"); startActivity(intent); //传送彩信(至关于发送带附件的短信) Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra("sms_body", "some text"); intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://media/external/images/media/23")); intent.setType("image/png"); startActivity(intent);
//播放多媒体 Uri uri = Uri.parse("file:///sdcard/song.mp3"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); intent.setType("audio/mp3"); startActivity(intent); //或者 Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "song.mp3"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent);
//打开Google Market寻找某个应用 Uri uri = Uri.parse("market://search?q=开源中国"); Intent it = new Intent(Intent.ACTION_VIEW, uri); startActivity(it); //显示某个应用的相关信息 Uri uri = Uri.parse("market://details?id=com.oschina.test"); Intent it = new Intent(Intent.ACTION_VIEW, uri); startActivity(it);
//卸载应用程序 Uri uri = Uri.fromParts("package", strPackageName, null); Intent intent = new Intent(Intent.ACTION_DELETE, uri); startActivity(intent); //安装应用程序 Intent intent = new Intent(Intent.ACTION_VIEW); Uri uri = Uri.parse("file:///sdcard/solitaire.apk"); intent.setDataAndType(uri,"application/vnd.android.package-archive"); startActivity(intent); //或者 Uri uri = Uri.fromParts("package", strPackageName, null); Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED, uri);
//打开联系人列表 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("vnd.android.cursor.item/phone"); startActivityForResult(intent, 0); //或者 content://contacts/people(/+联系人ID) Uri uri = Uri.parse("content://contacts/people"); Intent intent = new Intent(Intent.ACTION_PICK, uri); startActivityForResult(intent, 0);
//打开另外一程序 Intent intent = new Intent(Intent.ACTION_MAIN); ComponentName cn = new ComponentName("com.example.testb","com.example.testb.MainActivity"); intent.setComponent(cn); startActivityForResult(intent, 0);
常见的MIME类型:
超文本标记语言文本 .html,.html text/html
普通文本 .txt text/plain
RTF文本 .rtf application/rtf
GIF图形 .gif image/gif
JPEG图形 .ipeg,.jpg image/jpeg
au声音文件 .au audio/basic
MIDI音乐文件 .mid,.midi audio/midi,audio/x-midi
RealAudio音乐文件 .ra, .ram audio/x-pn-realaudio
MPEG文件 .mpg,.mpeg video/mpeg
AVI文件 .avi video/x-msvideo
GZIP文件 .gz application/x-gzip
TAR文件 .tar application/x-tar
(4)Category
Category是对目标组件类别信息的描述。一样做为一个字符串对象,一个Intent中能够包含多个Category。与Category相关的方法有三个,addCategory添加一个Category,removeCategory删除一个Category,而getCategories获得一个Category。Android系统一样定义了一组静态字符常量来表示Intent的不一样类别,下面列出一些常见的Category常量。
CATEGORY_ALTERNATIVE:你将在这章的后面所看到的,一个Intent Filter的用途是使用动做来帮忙填入上下文菜单。ALTERNATIVE种类指定,在某种数据类型的项目上能够替代默认执行的动做。例如,一个联系人的默认动做时浏览它,替代的多是去编辑或删除它。
CATEGORY_APP_BROWSER:和ACTION_MAIN一块儿使用,用来启动浏览器应用程序。
CATEGORY_APP_CALCULATOR:和ACTION_MAIN一块儿使用,用来启动计算器应用程序。
CATEGORY_APP_CALENDAR:和ACTION_MAIN一块儿使用,用来启动日历应用程序。
CATEGORY_APP_CONTACTS:和ACTION_MAIN一块儿使用,用来启动联系人应用程序。
CATEGORY_APP_EMAIL:和ACTION_MAIN一块儿使用,用来启动邮件应用程序。
CATEGORY_APP_GALLERY:和ACTION_MAIN一块儿使用,用来启动图库应用程序。
CATEGORY_APP_MAPS:和ACTION_MAIN一块儿使用,用来启动地图应用程序。
CATEGORY_APP_MARKET:这个activity容许用户浏览和下载新的应用程序。
CATEGORY_APP_MESSAGING:和ACTION_MAIN一块儿使用,用来启动短信应用程序。
CATEGORY_APP_MUSIC:和ACTION_MAIN一块儿使用,用来启动音乐应用程序
CATEGORY_SELECTED_ALTERNATIVE:设置这个activity是否能够被认为是用户当前选择的数据的一个可选择的action,与ALTERNATIVE相似,但ALTERNATIVE老是使用下面所述的Intent解析来指向单一的动做。SELECTED_ALTERNATIVE在须要一个可能性列表时使用。
CATEGORY_BROWSABLE:设置该组件可使用浏览器启动。
CATEGORY_DEFAULT:Android系统中默认的执行方式,按照普通Activity的执行方式执行。
CATEGORY_GADGET:设置该组件能够内嵌到另外的Activity中。
CATEGORY_HOME:HOME Activity是设备启动(登录屏幕)时显示的第一个Activity。经过指定Intent Filter为HOME种类而不指定动做的话,你正在将其设为本地home画面的替代。
CATEGORY_LAUNCHER:使用这个种类来让一个Activity做为应用程序的启动项。
CATEGORY_MONKEY:这个activity可能被monkey或者其余的自动测试工具执行。
CATEGORY_OPENABLE :用来指示一个GET_CONTENT意图只但愿ContentResolver.openInputStream可以打开URI。
CATEGORY_TAB:代表目标Activity是TabActivity的一个标签下的Activity。
CATEGORY_PREFERNCE:设置该组件为Preference。
CATEGORY_EMBED:可以在上级(父)activity 中运行。
CATEGORY_DEVELOPMENT_PREFERENCE:说明 activity 是一个设置面板 (development preference panel)。
CATEGORY_SAMPLE_CODE :做为一个简单的代码示例使用(通常状况下不使用)。
CATEGORY_DESK_DOCK:指定手机被插入桌面底座(硬件)时运行该Activity。
CATEGORY_CAR_DOCK:指定手机被插入汽车底座(硬件)时运行该Activity。
CATEGORY_CAR_MODE:设置该Activity可在车载环境下使用。
CATEGORY_INFO:用于提供包信息。
CATEGORY_TEST:该Activity是一个测试。
CATEGORY_UNIT_TEST:联合测试使用。
一个Intent最多只能包含一个Action属性,可是一个Intent中能够包含多个Category属性。
//唤起MAIN和LAUNCHER的应用 Intent intent = new Intent(Intent.ACTION_MAIN,null); intent.addCategory(Intent.CATEGORY_LAUNCHER); startActivity(intent);
你会发现唤起的应用里没有本身的应用,由于经过category筛选属于implicit intent的调用方式,不属于指定软件包名及类名的explicit intent的精确调用方式,对于implicit intent调用须要进行声明<category android:name="android.intent.category.DEFAULT"/>,以下:
<activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>
(5)Extra
Extra中封装了一些额外的附加信息,这些信息是以键值对的形式存在的。Intent能够经过putExtras()与getExtras()方法来存储和获取Extra。在Android系统的Intent类中,一样对一些经常使用的Extra键值进行了定义,以下所示。
EXTRA_BCC:存放邮件密送人地址的字符串数组。
EXTRA_CC:存放邮件抄送人地址的字符串数组。
EXTRA_EMAIL:存放邮件地址的字符串数组。
EXTRA_SUBJECT:存放邮件主题字符串。
EXTRA_TEXT:存放邮件内容。
EXTRA_KEY_EVENT:以KeyEvent对象方式存放触发Intent的按键。
EXTRA_PHONE_NUMBER:存放调用ACTION_CALL时的电话号码。
EXTRA_SHORTCUT_ICON:使用ACTION_CREATE_SHORTCUT在HomeActivity建立快捷方式时,对快捷方式的描述信息。(其中ICON和ICON_RESOURCE描述的是快捷方式的图标,类型分别为Bitmap和ShortcutIconResource。INTENT描述的是快捷方式相对应的Intent对象。NAME描述的是快捷方式的名字。)
EXTRA_SHORTCUT_ICON_RESOURCE EXTRA_SHORTCUT_INTENT EXTRA_SHORTCUT_NAME EXTRA_SUBJECT 描述信息主题的键
EXTRA_TITLE 使用ACTION_CHOOSER动做时,描述对话框标题的键
EXTRA_UID 使用ACTION_UID_REMOVED动做时,描述删除的用户id的键
// 给someone@domain.com发邮件发送内容为“Hello”的邮件 Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_EMAIL, "someone@domain.com"); intent.putExtra(Intent.EXTRA_SUBJECT, "Subject"); intent.putExtra(Intent.EXTRA_TEXT, "Hello"); intent.setType("text/plain"); startActivity(intent);
// 给多人发邮件 Intent intent=new Intent(Intent.ACTION_SEND); String[] tos = {"1@abc.com", "2@abc.com"}; // 收件人 String[] ccs = {"3@abc.com", "4@abc.com"}; // 抄送 String[] bccs = {"5@abc.com", "6@abc.com"}; // 密送 intent.putExtra(Intent.EXTRA_EMAIL, tos); intent.putExtra(Intent.EXTRA_CC, ccs); intent.putExtra(Intent.EXTRA_BCC, bccs); intent.putExtra(Intent.EXTRA_SUBJECT, "Subject"); intent.putExtra(Intent.EXTRA_TEXT, "Hello"); intent.setType("message/rfc822"); startActivity(intent);
//调用系统编辑添加联系人 Intent intent = newIntent(Intent.ACTION_INSERT_OR_EDIT); intent.setType(People.CONTENT_ITEM_TYPE); intent.putExtra(Contacts.Intents.Insert.NAME, "My Name"); intent.putExtra(Contacts.Intents.Insert.PHONE, "+1234567890"); intent.putExtra(Contacts.Intents.Insert.PHONE_TYPE,Contacts.PhonesColumns.TYPE_MOBILE); intent.putExtra(Contacts.Intents.Insert.EMAIL, "com@com.com"); intent.putExtra(Contacts.Intents.Insert.EMAIL_TYPE,Contacts.ContactMethodsColumns.TYPE_WORK); startActivity(intent);
//自定义一个chooser,不使用系统的chooser 该chooser能够有本身的标题(Title) Intent intent = new Intent(); intent.setAction(Intent.ACTION_CHOOSER); intent.putExtra(Intent.EXTRA_TITLE, "my chooser"); intent.putExtra(Intent.EXTRA_INTENT,new Intent(Intent.ACTION_GET_CONTENT).setType("*/*").addCategory(Intent.CATEGORY_OPENABLE)); startActivityForResult(intent, 0);
//启动搜索,在如下示例代码中,"ANDROID"为要搜索的字符串,当执行这段代码后, 会在系统的Chooser中显示能够用于搜索的程序列表 Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEARCH); //启动搜索 intent.putExtra(SearchManager.QUERY, "ANDROID"); startActivity(intent);
//启动WEB搜索,在如下示例代码中,"ANDROID"为要搜索的字符串,当执行这段代码后, 会在系统的Chooser中显示能够用于搜索的程序列表,通常状况下系统中安装的浏览器都会显示出来 Intent intent = new Intent(); intent.setAction(Intent.ACTION_WEB_SEARCH); //启动搜索 intent.putExtra(SearchManager.QUERY, "ANDROID"); startActivity(intent);
(6)Flag
首先简单介绍下Task和Activity的关系:
Task就像一个容器,而Activity就至关与填充这个容器的东西,第一个东西(Activity)则会处于最下面,最后添加的东西(Activity)则会在最上面。从Task中取出东西(Activity)是从最顶端取出,也就是说最早取出的是最后添加的东西(Activity),以此类推,最后取出的是第一次添加的Activity,而Activity在Task中的顺序是能够控制的,在Activity跳转时用到Intent Flag能够设置新建activity的建立方式。
(1) 前提: Activity A和Activity B在同一个应用中.
操做: Activity A启动开僻Task堆栈(堆栈状态: A), 在Activity A中启动Activity B(堆栈状态: AB), 按下BACK返回键(堆栈状态: A).
(2) 前提: Activity A和Activity B在同一个应用中, 应用名称为"TaskOne应用".
操做: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),在Activity A中启动Activity B(TaskA堆栈状态: AB), 长按Home键, 返回Launcher, 启动其它应用(如:电子书),开僻一个新Task堆栈, 命名: TaskB, 长按Home健, 返回Launcher, 单击"TaskOne应用"图标, 此时TaskA堆栈返回前台,Activity B为栈顶应用, 供用户使用.
(3) 前提: Activity A在名称为"TaskOne应用"的应用中, Activity C在名称为"TaskTwo应用"的应用中.
操做: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),在Activity A中启动Activity C(TaskA堆栈状态: AC),长按Home键, 返回Launcher, 启动"TaskTwo应用"即Activity C,开僻新的Task堆栈, 命名为TaskB, 按BACK键返回Launcher, 单击"TaskOne应用"图标, 此时TaskA堆栈返回前台,Activity C为栈顶应用, 供用户使用.
①FLAG_ACTIVITY_NEW_TASK: 设置此状态,记住如下原则,首先会查找是否存在和被启动的Activity具备相同的亲和性的任务栈(即taskAffinity,注意同一个应用程序中的activity的亲和性同样,因此下面的a状况会在同一个栈中,前面这句话有点拗口,请多读几遍),若是有,刚直接把这个栈总体移动到前台,并保持栈中的状态不变,即栈中的activity顺序不变,若是没有,则新建一个栈来存放被启动的activity。
a). 前提: Activity A和Activity B在同一个应用中。
操做: Activity A启动开僻Task堆栈(堆栈状态: A), 在Activity A中启动Activity B, 启动Activity B的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK, Activity B被压入Activity A所在堆栈(堆栈状态: AB)。
缘由: 默认状况下同一个应用中的全部Activity拥有相同的关系(taskAffinity).
b). 前提: Activity A在名称为"TaskOne应用"的应用中, Activity C和Activity D在名称为"TaskTwo应用"的应用中。
操做1: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),在Activity A中启动Activity C, 启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C), 长按Home键, 选择TaskA,Activity A回到前台, 再次启动Activity C(两种状况1.从桌面启动;2.从Activity A启动,两种状况同样), 这时TaskB回到前台, Activity C显示, 供用户使用, 即:包含FLAG_ACTIVITY_NEW_TASK的Intent启动Activity的Task正在运行, 则不会为该Activity建立新的Task,而是将原有的Task返回到前台显示。
操做2: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),在Activity A中启动Activity C,启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C), 在Activity C中启动Activity D(TaskB的状态: CD) 长按Home键, 选择TaskA, Activity A回到前台, 再次启动Activity C(从桌面或者ActivityA启动,也是同样的),这时TaskB回到前台, Activity D显示,供用户使用.说明了在此种状况下设置FLAG_ACTIVITY_NEW_TASK后,会先查找是否是有Activity C存在的栈,根据亲和性(taskAffinity),若是有,刚直接把这个栈总体移动到前台,并保持栈中的状态不变,即栈中的顺序不变。
②FLAG_ACTIVITY_CLEAR_TOP:
前提: Activity A, Activity B, Activity C和Activity D在同一个应用中.
操做: Activity A启动开僻Task堆栈(堆栈状态: A), 在Activity A中启动Activity B(堆栈状态: AB), 在Activity B中启动Activity C(堆栈状态: ABC), 在Activity C中启动Activity D(堆栈状态: ABCD), 在Activity D中启动Activity B,启动Activity B的Intent的Flag设置为FLAG_ACTIVITY_CLEAR_TOP, (堆栈状态: AB)。
③FLAG_ACTIVITY_BROUGHT_TO_FRONT:
前提: Activity A在名称为"TaskOne应用"的应用中, Activity C和Activity D在名称为"TaskTwo应用"的应用中.
操做: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),在Activity A中启动Activity C,启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C), 在Activity C中启动Activity D(TaskB的堆栈状态: CD), 长按Home键, 选择TaskA, Activity A回到前台, 在Activity A中再次启动Activity C,在启动Activity C的Intent中设置Flag为FLAG_ACTIVITY_BROUGHT_TO_FRONT, TaskB回到前台,Activity C显示, (TaskB的堆栈状态: C)。
④FLAG_ACTIVITY_MULTIPLE_TASK:
与FLAG_ACTIVITY_NEW_TASK结合使用, 首先在Intent中设置FLAG_ACTIVITY_NEW_TASK, 打开Activity,则启动一个新Task, 接着在Intent中设置FLAG_ACTIVITY_MULTIPLE_TASK, 再次打开同一个Activity,则还会新启动一个Task。
⑤FLAG_ACTIVITY_SINGLE_TOP:
当前Task堆栈中存在ABCD四个Activity, A是栈顶Activity, D为栈底Activity, 存在打开A的Intent中设置了FLAG_ACTIVITY_SINGLE_TOP标志, 则会使用栈顶A, 而不会重新New A.
⑥FLAG_ACTIVITY_RESET_TASK_IF_NEEDED:
通常与FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET结合使用,若是设置该属性,这个activity将在一个新的task中启动或者或者被带到一个已经存在的task的顶部,这时这个activity将会做为这个task的首个页面加载。将会致使与这个应用具备相同亲和力的task处于一个合适的状态(移动activity到这个task或者从中移出),或者简单的重置这个task到它的初始状态FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET:在当前的Task堆栈中设置一个还原点,当带有FLAG_ACTIVITY_RESET_TASK_IF_NEEDED的Intent请求启动这个堆栈时(典型的例子是用户从桌面再次启动这个应用),还原点之上包括这个应用将会被清除。应用场景:在email程序中预览图片时,会启动图片观览的actvity,当用户离开email处理其余事情,而后下次再次从home进入email时,咱们呈现给用户的应该是上次email的会话,而不是图片观览,这样才不会给用户形成困惑。
例: 存在Activity A, Activity B, Activity C, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),在Activity A中启动Activity B(TaskA堆栈状态: AB), 接着Activity B启动Activity C(TaskA堆栈状态: ABC),启动Activity C的Intent中设置FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET标题, 这样TaskA中有一个还原点,当有包含FLAG_ACTIVITY_RESET_TASK_IF_NEEDED的Intent请求TaskA堆栈时(好比请求Activity A),系统就会将还原点以上的Activity清除, TaskA堆栈中只剩下了AB。
例子:
Intent intent = new Intent(); intent.setClassName("com.example.testb", "com.example.testb.MainActivity"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);
IntentFilter实际上至关于Intent的过滤器,一个应用程序开发完成后,须要告诉Android系统本身可以处理哪些隐形的Intent请求,这就须要声明IntentFilter。IntentFilter的使用方法实际上很是简单,仅声明该应用程序接收什么样的Intent请求便可。
IntentFilter过滤Intent时,通常是经过Action、Data及Category三方面进行监测的。接下来分别对这三方面进行介绍。
(1)检查Action
一个Intent只能设置一种Action,可是一个IntentFilter却能够设置多个Action过滤。当IntentFilter设置了多个Action时,只需一个知足便可完成Action验证。当IntentFilter中没有说明任何一个Action时,那么任何的Action都不会与之匹配。而若是Intent中没有包含任何Action,那么只要IntentFilter中含有Action时,便会匹配成功。
String MY_ACTION = "com.view.my_action";//自定义 Intent intent = new Intent(MY_ACTION); startActivity(intent);
<activity android:name=".TestActivity" android:launchMode="singleTop"> <intent-filter> <action android:name="com.view.my_action"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>
(2)检查Data
数据的监测主要包含两部分,即数据的URI及数据类型,而数据URI又被分红三部分进行匹配(scheme、authority、path),只有这些所有匹配时,Data的验证才会成功。
若是你的组件能处理多个的话,你能够包含多个条件。你可使用下面属性的任意组合来指定组件支持的数据:
android:host 指定一个有效的主机名(例如,com.google)。
android:mimetype
容许你设定组件能处理的数据类型。例如,<type android:value=”vnd.android.cursor.dir/*”/>能匹配任何Android游标。
android:path 有效地URI路径值(例如,/transport/boats/)。
android:port 特定主机上的有效端口。
android:scheme 须要一个特殊的图示(例如,content或http)。
例如,下面的URI: content://com.example.project:200/folder/subfolder/etc
scheme
是content,host是"com.example.project",port是200,path是"folder/subfolder/etc"。host和port一块儿构成URI的凭据(authority),若是host没有指定,port也被忽略。
这四个属性都是可选的,但它们之间并不都是彻底独立的。要让authority有意义,scheme必须也要指定。要让path有意义,scheme和authority也都必需要指定。
当比较intent对象和过滤器的URI时,仅仅比较过滤器中出现的URI属性。例如,若是一个过滤器仅指定了scheme,全部有此scheme的URIs都匹配过滤器;若是一个过滤器指定了scheme和authority,但没有指定path,全部匹配scheme和authority的URIs都经过检测,而无论它们的path;若是四个属性都指定了,要都匹配才能算是匹配。然而,过滤器中的path能够包含通配符来要求匹配path中的一部分。
<data>元素的type属性指定数据的MIME类型。Intent对象和过滤器均可以用"*"通配符匹配子类型字段,例如"text/*","audio/*"表示任何子类型。
数据检测既要检测URI,也要检测数据类型。规则以下:
① 一个Intent对象既不包含URI,也不包含数据类型:仅当过滤器也不指定任何URIs和数据类型时,才不能经过检测;不然都能经过。
② 一个Intent对象包含URI,但不包含数据类型:仅当过滤器也不指定数据类型,同时它们的URI匹配,才能经过检测。例如,mailto:和tel:都不指定实际数据。
③ 一个Intent对象包含数据类型,但不包含URI:仅当过滤也只包含数据类型且与Intent相同,才经过检测。
④ 一个Intent对象既包含URI,也包含数据类型(或数据类型可以从URI推断出):数据类型部分,只有与过滤器中之一匹配才算经过;URI部分,它的URI要出如今过滤器中,或者它有content:或file: URI,又或者过滤器没有指定URI。换句话说,若是它的过滤器仅列出了数据类型,组件假定支持content:和file: 。
若是一个Intent可以经过不止一个活动或服务的过滤器,用户可能会被问那个组件被激活。若是没有目标找到,会产生一个异常。
Intent intent = new Intent("com.view.my_action"); intent.setData(Uri.parse("oschina://com.example.project")); startActivity(intent);
<activity android:name=".MainActivity" android:label="@string/title_notes_list"> <intent-filter> <action android:name="com.view.my_action"/> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="oschina" android:host="com.example.project"/> </intent-filter> </activity>
(3)检查Category
IntentFilter一样能够设置多个Category,当Intent中的Category与IntentFilter中的一个Category彻底匹配时,便会经过Category的检查,而其余的Category并不受影响。可是当IntentFilter没有设置Category时,只能与没有设置Category的Intent相匹配。
其它问题:
Intent在寻找目标组件时有两种方法:
第一种,显式调用(Explicit Intent),经过Component name直接指定;
第二种,隐式调用(implicit Intent),没有明确指定目标组件的名称,那么就要经过必定的条件过滤筛选。
Implicit Intent 到底发给哪一个activity?
这须要进行三个匹配,一个是action,一个是category,一个是data。根据三个的匹配结果,找到应该启动的Activity。
Data匹配时的规则一共有四条:
a.若是Intent没有指定Data相关的字段,只能匹配上没有指定Data的IntentFilter。也就是说若是一个Intent没有指定任何的Data(Uri和Type),它只能匹配到没有指定任何Data(Scheme和Type)的IntentFilter。
b.若是一个Intent只指定了Uri可是没有Type(而且Type也不可以从Uri中分析出)只能匹配到仅指定了相应Scheme且没有指定Type的IntentFilter。实际的例子有若是一个Intent是想要发邮件,或是打电话,它们的Intent是相似这样的:"mailto:someone@sb.com"和"tel:1234567"。换句话说,这些Uri自己就是数据,而再也不是一个指向数据的地址。好比:Phone中的Dialer就有以下的IntentFilter:
<intent-filter> <action android:name="android.intent.action.CALL" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="tel" /> </intent-filter>
再如,要处理SD状态变化的IntentFilter:
<intent-filter> <action android:name="android.intent.action.MEDIA_MOUNTED"/> <action android:name="android.intent.action.MEDIA_UNMOUNTED"/> <action android:name="android.intent.action.MEDIA_SHARED"/> <action android:name="android.intent.action.MEDIA_REMOVED"/> <action android:name="android.intent.action.MEDIA_EJECT"/> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file" /> </intent-filter>
再如,要处理Package状态变化的IntentFilter:
<intent-filter> <action android:name="android.intent.action.PACKAGE_ADDED"/> <action android:name="android.intent.action.PACKAGE_REMOVED"/> <action android:name="android.intent.action.PACKAGE_DATA_CLEARED"/> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="package" /> <intent-filter>
可是注意,对于想对数据进行操做的Intent,最好不要只指定Uri,而不指定类型。由于若是这样作一般会匹配到一大堆
c. 若是一个Intent只指定了Type,可是没有指定Uri,它只能匹配到只指定了相应Type且没有指定Scheme的IntentFitler
d. 若是一个Intent即有Uri又有Type,那么它会匹配上:1).Uri和Type都匹配的IntentFilter;2).首先Type要匹配,另外若是Intent的Uri是content:或file:,且IntentFilter没有指定Scheme的IntentFilter。由于对于Android来说content和file这二种Scheme是系统最多见也是用的最多的,因此就当成缺省值来对待。
另外须要注意,Type,由于是MimeType,因此是容许使用通配符的,好比'image/*',能匹配上全部以'image'为开头的类型,也说是说能匹配上全部的图像。
根据Data匹配的例子
假如系统中有四个Activity,A的IntentFilter是这样子的:
<activity ...> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="content" android:mimeType="image/*" /> </intent-filter> </activity>
这代表A能够发送一切图片类型,而且内容必须是由ContentProvider提供的,也就是Uri必须是以"content://"开头的
而另一个Activity B是这样子声明的:
<activity ...> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file" android:mimeType="image/*" /> </intent-filter> </activity>
这代表B能够发送一切图片,但内容必须是单独的一个文件,也就是Uri必须是由"file://"开头的
还有一个C是这样子声明的:
<activity ...> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
这代表C只能接收那些没有指定任何Uri和Type的Action是SEND的Intent。
而D是这样子声明的:
<activity ...> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> </intent-filter> </activity>
这代表D能够发送一切图片,不管是数据库内的(content),仍是单独的文件(file)。
若是一个Intent是这样写的:
Intent share = new Intent(Intent.ACTION_SEND); startActivity(share);
那么它只能匹配C,由于C没有指定数据和类型,Action是SEND,根据规则a,它只能匹配Activity A。但若是给Intent加上额外的条件
share.setDataAndType(uri,"image/jpeg");
那么若是uri是数据库内容,它会匹配到A,若是它是一个文件,会匹配到B。但不管是content仍是file都会匹配到D,由于它能处理以任何形式存储的图片。但始终不会匹配到C,由于C没有声明Data字段,因此不会匹配上。
因此,一般想把组件做为系统公用接口时都是这样子来写:
<activity ...> <intent-filter> <!-- implement public actions such as View, Edit, Pick or Send --> <action android:name="android.intent.action.SEND" /> <!-- never forget default category, otherwise your activity never receives intents --> <category android:name="android.intent.category.DEFAULT" /> <!-- specify mimeType to constrain data type, receive data from both content provider and file --> <data android:mimeType="image/*" /> <!-- specify scheme to constrain data source, if necessary --> <data android:shceme="http" /> </intent-filter> </activity>
Intent和IntentFilter对于组件Activity来说注意事项比较多,可是对于Service和BroadcastReceiver来讲就没有那么多的注意事项了,由于对于Service和BroadcastReceiver一般都不用设置Category和Data。但也有例外,好比前面所讲到的SD相关广播和应用程序安装相关广播。
另外要注意,若是使用Context.startActivity()或Context.startActivityForResult(),Context.bindService()和Context.startService(),若是系统没有为Intent匹配到目标Activity和Service那么会有RuntimeException(ActivityNotFoundException)抛出;若是有多个目标同时匹配,会以列表的方式来让用户选择使用哪一个。