对于Anddroid Intent。这个对大家来说想必是再熟悉不过了。比如开启一个activity啥的,都会用到它。
Intent简略描述
Intent中文意思,意图,可以理解为不同组件通信之间的"媒介",专门提供组件互相调用的相关信息。其中对于Android四大组件,Activity,BroadCaster,Service, ContentProvider,这几个应该有个大致上的了解了。Intent的使用与前三个都有关系。Intent可以理解为一个将要执行的动作的抽象描述,一般来说是作为参数来使用,由Intent来协助完成Android各组件之间的通信。比如说调用startActivity()来启动一个Activity,或者由broadcastIntent()来传递给所有感兴趣的BroadCaseReceiver,再或者由startService()/bindService()来启动一个后台的Service,所以可以看出来,intent主要是用来启动其他的activity,service,所以可以将intent理解为activity之间的粘合剂。
Intent关于启动组件的相关方法
1 Activity 的启动,有 startActivity(), startActivityForResult().
2 Service :有 startService(), bindService()
3 Broadcasts: sendBroadcasts(), sendOrderedBroadcasts(), cendStickyBroadcasts().
Intent涉及的属性:
源码中部分intent类的成员变量:
private String mAction;
private Uri mData;
private String mType;
private String mPackage;
private ComponentName mComponent;
private int mFlags;
private ArraySet<String> mCategories;
private Bundle mExtras;
private Rect mSourceBounds;
private Intent mSelector;
private ClipData mClipData;
private int mContentUserHint = UserHandle.USER_CURRENT;
其中最常用的是Action属性和Data属性。
1 Action属性
action属性一般被作为
mOpenActivityBTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(BActivity.ACTION);
startActivity(intent);
}
});
public static final String ACTION = "android.intent.action.myBActivity";
<activity android:name=".Activity.BActivity">
<!--需要注意的是,action要和category都要设置,否则会出现调用不起来的异常-->
<intent-filter>
<action android:name="android.intent.action.myBActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
action通常用作隐式启动activity。它在intent的初始化里无需给出到底要跳到哪个activity,只需要写下activity具有的特点就可以。这样跳转的时候,跳转条件相匹配的activity就会启动。。针对于这些跳转条件,就是在AndroidManifest.xml文件中给出的。
当然上述代码的action是我们自定义的,对于android系统而言,里面有好多已经写好的action可以供我们使用。如下:
Intent.ACTION_MAIN: 标识Activity为一个程序的启动activity。
Intent.ACTION_CALL: 呼叫执行的电话号码。
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.paese("tel:10086"));
startActivity(intent);
Intent.ACTION_POWER_CONNECTED:插上外部电源的时候发出的广播
Intent.ACTION_POWER_DISCONNECTED: 已断开外部电源连接时发出的广播。
Intent.ACTION_DIAL: 调用拨号面板
Intent intent = new Intent();
intent.setAction(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel : 10086"));
startActivity(intent);
Intent.ACTION_ALL_APPS:列出所有的应用。
Intent.ACTION_ANSWER:处理呼入的电话
Intent.ACTION_BUG_REPORT:显示bug报告。
Intent.ACTION_CALL_BUTTON:相当于按拨号键。
Telephony_SMS_RECEIVED:接收短信的action
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
<data android:host="localhost"/>
</intent-filter>
Intent.ACTION_GET_CONNECT:允许用户选择特殊类的数据,并返回(特殊种类的数据:照一张照片或者录一点音)
Intent.ACTION_BATTERY_LOW:标识电池电量低
Intent.ACTION_SEND:发送邮件的action
Intent>ACTION_CALL_PRIVILEGED:调用skype的action:
Intent intent = newIntent("android.intent.action.CALL_PRIVILEGED");
intent.setClassName("com.skype.raider",
"com.skype.raider.Main");
intent.setData(Uri.parse("tel:" + phone));
startActivity(intent);
Intent.ACTION_CLOSE_SYSTEM_DIALOGS:当屏幕超时进行锁屏时,当用户按下电源按钮,长按或者短按,进行锁屏时,andriod都会广播此Action消息。
2Data和Extras,即执行动作要操作的数据和传递目标的附加信息
浏览器交互案例:
public void invokeWebBrowser() {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://baidu.com"));
startActivity(intent);
}
public void involeWebSearch() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_WEB_SEARCH);
intent.putExtra(SearchManager.QUERY, "android");
startActivity(intent);
}
上述的两个方法,第一个是启动浏览器并打开指定的网页,第二个是进行关键字的搜索。分别针对的action是Intent.ACTION_VIEW, 和 Intent.ACTION_WEB_SEARCH,前者需要指定相应的网页,后者需要指定关键字信息,对于关键字搜索来说,浏览器会按照自己设置的默认搜索引擎进行搜索。
但是应当注意到的是,在打开网页的时候,Inten指定了一个data属性,这其实是指定要操作的数据,是一个URL的形式,我们可以将一个指定前缀的字符串转换成特定的URI类型,比如http, https 表示网络地址类型, tel表示电话号码类型, mailto表示邮箱地址类型,如果要给呼叫指定的电话号码,可以这样写:
public void call10086() {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel : 10086"));
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
Toast.makeText(getApplicationContext(), "没有得到权限", Toast.LENGTH_LONG).show();
return;
}
startActivity(intent);
}
那么我们如何知道目标是否接受这种前缀呢?看下<data>标签的匹配规则。
android:scheme 匹配url中的前缀,除了"http", "https", "tel"之外,还可以定义自己的前缀
android:host: 匹配url中的主机名部分,比如“baiducom”,如果定义为*,就表示匹配任意主机名
android:port:匹配url中的端口
android:path 匹配url中的路径。
如果此时你想写一个自己的activity来响应这样的intent,使其startActivity的时候可以打开自己自定义的这个activity,那么需要在AndroidManifest.xml文件中写出对应的规则即可。
所以看下面代码:
<activity android:name=".Activity.IntentTestActivity2">
<intent-filter>
<action android:name="android.intent.action.INTENTTESTACT2"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="yayali" android:host="music.zhangbichen" android:path="/xuerumo" android:port="8888" />
</intent-filter>
</activity>
可见新写的这个Activity定义了他的隐式启动匹配规则。这个规则是我们自己定义的。那么当我们这么调用的时候,就会打开。
private void openItentTestActivit2() {
Intent intent = new Intent();
intent.setAction(IntentTestActivity2.INTENT_OPEN);
intent.setData(Uri.parse("yayali://music.zhangbichen:8888/xuerumo"));
startActivity(intent);
}
这种情况下,url中的每一个部分必须和TargetActivity匹配信息中全部一致才能跳转成功。否则就会被系统拒绝。
Intent有一个也相当中要的方法,叫putExtra(),这个方法实则是和Extra有些关系。上面也有用到过这个方法,就是搜索关键字的时候。对于putExtra,实则是将数据加到了一个Bundle里面,bundle又维护了一个map。例如:
/**
* Add extended data to the intent. The name must include a package
* prefix, for example the app com.android.contacts would use names
* like "com.android.contacts.ShowAll".
*
* @param name The name of the extra data, with package prefix.
* @param value The boolean data value.
*
* @return Returns the same Intent object, for chaining multiple calls
* into a single statement.
*
* @see #putExtras
* @see #removeExtra
* @see #getBooleanExtra(String, boolean)
*/
public @NonNull Intent putExtra(String name, boolean value) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putBoolean(name, value);
return this;
}
/**
* Inserts a Boolean value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a boolean
*/
public void putBoolean(@Nullable String key, boolean value) {
unparcel();
mMap.put(key, value);
}
category 要执行的目标所局域的特质或者归类
在AndroidManifest.xml中,总会有一个Activity是启动的第一个是anctivity,他们的配置大多是这个样子
<activity android:name=".Activity.IntentTestActivity1" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
里面就用到了一个很重要的关键字--category,上方代码代表的是该应用所在的task中的初始Activity并且出现在系统launcher应用列表中。
常见的category有下面几项:
常量 | 解释 |
CATEGORY_DEFAULT | 默认的category |
CATEGORY_BROWSABLE | 指定了此category后,在网页上点击图片或链接时,系统会考虑将此目标Activity列入可选列表,供用户选择以打开图片或链接。 |
CATEGORY_GADGET | The activity can be embedded inside of another activity that hosts gadgets. |
CATEGORY_HOME | The activity displays the home screen, the first screen the user sees when the device is turned on or when the HOME key is pressed. |
CATEGORY_LAUNCHER | The activity can be the initial activity of a task and is listed in the top-level application launcher. |
CATEGORY_PREFERENCE | 表示该目标Activity是一个首选项界面; |
为Intent设置category时,应是应用addCategory(sting category)方法向intent中添加指定的类别信息,来匹配声明此类别的目标Activity。
如下,是回到Home界面的例子:
private void gotoHome() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);
}
Type:要执行动作的目标Activity所能处理的MIME数据类型。
例如一个可以处理图片的目标activity在其声明中包含这样的mimeType:
<data android:mimeType="image/*"/>
在使用intent进行匹配的时候,我们可以使用setType(String type)或者setDataAndType(Uri data, String type)来设置mimeType。
Component目标组件的包或者类名:
在使用component进行匹配的时候一般采用以下几种形式:
1 intent.setComponent(new ComponentName(getApplicationContext(), TargetActivity.class));
2 intent.setComponent(new ComponentName(getApplicationContext(), "com.scott.intent.TargetActivity"));
3 intent.setComponent(new ComponentName("com.scott.other", "com.scott.other.TargetActivity"));
其中前两种用于匹配同一包中的目标,第三种用于匹配其他包内的目标。