回顾Android
中的隐式Intent
的相关知识点。
隐式启动主要解决了界面间的跳转解耦,主要涉及intent-filter
中的三个标签:
data
它们构成了隐式启动的匹配项,通过不同的配置完成不同的路由跳转。
列一个例子,新建一个APP
有两个界面,一为MainActivity
,一个为ActionActivity
,MainActivity
中有一个按钮,通过按钮启动隐式Intent
来启动ActionActivity
。
布局和代码非常简单:
MainActivity
的布局和代码:
//布局中只有一个按钮
xml version="1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
//代码
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = findViewById(R.id.bt_action);
}
}
ActionActivity
的布局和代码:
//布局中就是一行文字说明
xml version="1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ActionActivity">
"我是跳转的页面"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
//代码
public class ActionActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_action);
}
}
给MainActiity
的按钮添加代码,如果找不到的话会抛出异常,因此捕获并打印启动失败的信息:
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try{
//Intent action1 = new Intent("action1");//可以使用构造传入action参数
Intent action1 = new Intent();
action1.setAction("action1");
startActivity(action1);
}catch (Exception e){
Toast.makeText(MainActivity.this,"启动失败",Toast.LENGTH_LONG).show();
e.printStackTrace();
}
});
清单文件中的ActionActivity
的配置,可以看到只使用了action
标签:
<activity android:name=".ActionActivity">
<intent-filter>
<action android:name="action1"/>
intent-filter>
activity>
启动MainActivity
,点击按钮,结果抛出异常:
异常结果为:
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=action1 }
所以实际上ActionActivity
只配置action
标签是不够的。
增加默认的android.intent.category.DEFAULT
:
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="action1"/>
intent-filter>
activity>
intent
的代码不做变动,这时点击按钮可以正常跳转:
说明这里category 是必须要配置的,Intent
虽然没有设置category
,但是默认就是这个android.intent.category.DEFAULT
,所以能够匹配到。
再增加一个action
试试:
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="action1"/>
<action android:name="action2"/>
intent-filter>
activity>
intent
的代码不做变动,点击按钮依旧可以正常跳转,效果不再贴出。
如果给Intent
增加一个intent-filter
标签中没有的action
试试:
Intent action1 = new Intent();
action1.setAction("action1");
action1.setAction("action3");
startActivity(action1);
点击抛出ActivityNotFoundException
异常,,效果不再贴出。
通过上面的测试说明intent
设置的action
必须为目标页面intent-filter
中action
的子集才能正常跳转,并且是区分大小写的。
测试完了action
,更换下category
为自定义的category1
试试:
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="category1"/>
<action android:name="action1"/>
intent-filter>
activity>
代码中:
Intent action1 = new Intent();
action1.setAction("action1");
action1.addCategory("category1");
startActivity(action1);
运行点击抛出ActivityNotFoundException
异常,效果不再贴出。
然后增加android.intent.category.DEFAULT
这个默认的action
在测试:
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="category1"/>
<action android:name="action1"/>
intent-filter>
activity>
运行点击按钮可以正常跳转,效果不再贴出。
说明android.intent.category.DEFAULT
这个默认的action
是必须要在intent-filter
标签中声明的。
如果存在多个action
匹配的页面,那么启动后会弹出选择对话框,比如编写一个新的界面为Action2Activity
,它也接受action1
的动作:
布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Action2Activity">
<TextView
android:text="我也是跳转的页面!"
android:textSize="32dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
LinearLayout>
清单文件:
<activity android:name=".Action2Activity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="action1" />
intent-filter>
activity>
代码只设置action1
:
Intent action1 = new Intent();
action1.setAction("action1");
startActivity(action1);
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="category1"/>
<category android:name="category2"/>
<action android:name="action1" />
intent-filter>
activity>
代码不做变动,点击按钮可以正常跳转。
如果在代码中增加一个category3
试试:
Intent action1 = new Intent();
action1.addCategory("category1");
action1.addCategory("category3");
action1.setAction("action1");
startActivity(action1);
运行点击抛出ActivityNotFoundException
异常,效果不再贴出。
同样说明intent
中的category
,必须为目标页面intent-filter
中category
的子集才能正常跳转,和action
的匹配规则是一样的。
另外说明下,只使用category
而不添加任何action
也是无法匹配的,说明intent
中的action
是必须要设置的。而且如果配置了多个intent-filter
标签也是按照一组一组进行匹配的,直到与其中一组匹配成功。
data
标签自然不是必须的,主要为URL
的表现形式,它的格式如下:
<data android:mimeType=""/>//媒体类型 例如 "image/*"就是匹配图片类型
<data android:scheme=""/>//例如: http,https
<data android:host=""/>//例如:www.csdn.net
<data android:port=""/>//例如:8080
<data android:path=""/> // 路径:如 /nav/blockchain
<data android:pathPrefix=""/>
<data android:pathPattern=""/>// 正则表达式标识的路径
<data android:ssp=""/>
<data android:sspPrefix=""/>
<data android:sspPattern=""/>
data的属性比较多:
mimeType
媒体类型 例如 image/*
就是匹配图片类型,mimeType
类型比较多 ,这里不再列出;scheme
类似如 http
,https
host
类似www.csnd.com
port
类似 8080
path
路径:/nav/blockchain
pathPrefix
路径前缀:/nav
pathPattern
路径的正则表达式方式ssp
//可以匹配系统特定intent 相关文章通过android:ssp高效过滤Android IntentssspPrefix
//可以匹配系统特定intentsspPattern
//可以匹配系统特定intent
类似这样
,
例如action://action1:44/abc/xyz
。
改动下上面的例子:
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="action1"/>
<data android:mimeType="image/*"/>//指定媒体类型为图片的类型
intent-filter>
activity>
代码中不设置action
,只设置mimeType
:
Intent action1 = new Intent();
action1.setType("image/png");//指定了媒体类型为png的图片类型
startActivity(action1);
运行效果:
这里可以看到只要有任何能够匹配的mimeType
为image/png
的应用都会在底部的列表弹出,但是如果Intent
不设置mimeType
而只设置action
,那么是无法匹配的,说明mimeType
的需要优先匹配的。
如果action
和mimeType
完全匹配,就可以更为精准的匹配。
需要注意的是清单文件中设置了mimeType
而不设置scheme
的话,intent
中的默认的scheme
是content
或者file
才能进行匹配(在API 24
以后只能对应content
的scheme
了),意味着mimeType
其实对应了默认的scheme
。
看下代码实现:
Intent action1 = new Intent();
action1.setDataAndType(Uri.parse("content://action1"),"image/png");
action1.setAction("action1");
startActivity(action1);
这里setDataAndType
方法才能同时设置data
和mimeType
,单独使用setData
或者setType
两者都会将对方清空。
同样如果清单文件中指定了多个mimeType
那么,只要Intent
匹配到一个就可以。
在上面的例子中scheme就是content://action1
中的content
了,更改个自定义的scheme
测试:
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="action1" />
<data android:mimeType="image/*"/>
<data android:scheme="action"/>
intent-filter>
activity>
Intent action1 = new Intent();
action1.setDataAndType(Uri.parse("action://action1"),"image/png");
action1.setAction("action1");
startActivity(action1);
运行可以正常启动的,这里不再帖效果。当然清单文件不设置mimeType
的话,Intent
就可以使用setData
方法来设置URL
的。
上面例子的action://action1
中action1
就是host
了,需要注意的是如果清单文件中没有指定host
的话, port
,path
,pathPrefix
,pathPattern
都不会生效。
port
需要和host
放在同一个data
标签内才能生效:
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="action1" />
<data android:scheme="action"/>
<data android:host="action1" android:port="44"/>
<data />
intent-filter>
activity>
代码:
Intent action1 = new Intent();
action1.setData(Uri.parse("action://action1:44"));
action1.setAction("action1");
startActivity(action1);
可以正常跳转。
代码增加/abc/xyz
:
Intent action1 = new Intent();
action1.setData(Uri.parse("action://action1:44/abc/xyz"));
action1.setAction("action1");
startActivity(action1);
清单文件:
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="action1" />
<data android:scheme="action"/>
<data android:host="action1" android:port="44"/>
<data android:path="/abc/xyz"/>//必须以/开头
<data />
intent-filter>
activity>
这样完全匹配。
匹配path
的前缀,上面例子中就是/abc
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="action1" />
<data android:scheme="action"/>
<data android:host="action1" android:port="44"/>
<data android:pathPrefix="/abc"/>
<data />
intent-filter>
activity>
利用正则表达式/.*
匹配/abc
:
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="action1" />
<data android:scheme="action"/>
<data android:host="action1" android:port="44"/>
<data android:pathPattern="/.*/xyz"/>
intent-filter>
activity>
.*
就是表示匹配任意字符,这样也能够灵活匹配,需要注意正则表达式中的符号如果当做字符处理需要进行转义。
比如要匹配action://action1:44/abc/xyz
这样的URL
的话,清单文件中至少需要写到scheme
:
<data android:scheme="action"/>
其实也就是说Intent
中的data
应该为清单文件中data
的子集。
隐式Intent
也可以从网页中启动Activity
,比如有一个远程网页内容如下:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<a href="action://action1:44/abc/xyz?m=1&n=2">启动页面a>
body>
html>
a
标签中的href
为自定义链接,对应的在清单文件中做出改动:
<activity android:name=".ActionActivity">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<action android:name="android.intent.action.VIEW" />
<data
android:scheme="action"
android:host="action1"
android:port="44"
android:path="/abc/xyz"
/>
intent-filter>
activity>
ActionActivity
中可以获取各种数据:
public class ActionActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_action);
Uri data = getIntent().getData();
if (data != null){
Log.d("url",data.toString());
String scheme = data.getScheme();
Log.e("scheme", scheme);
String host = data.getHost();
Log.e("host", host);
int port = data.getPort();
Log.e("port", port+"");
String path = data.getPath();
Log.e("path", path);
List<String> pathSegments = data.getPathSegments();//路径分割
String params = data.getQuery();
Log.e("params", params);
String value = data.getQueryParameter("m");
Log.e("m值", value);
}
}
}
如果从浏览器中打开页面,点击a
标签链接,可以成功跳转并看到打印结果:
08-20 11:33:01.181 4491-4491/cn.franky.test E/scheme: action
08-20 11:33:01.182 4491-4491/cn.franky.test E/host: action1
08-20 11:33:01.182 4491-4491/cn.franky.test E/port: 44
08-20 11:33:01.182 4491-4491/cn.franky.test E/path: /abc/xyz
08-20 11:33:01.182 4491-4491/cn.franky.test E/params: m=1&n=2
08-20 11:33:01.182 4491-4491/cn.franky.test E/m值: 1
隐式Intent
总结到此。