Android app要打开一个页面,通常有两种实现方式,一种是常见的显式启动。
val intent = Intent()
intent.setClass(MainActivity@this, SecondActivity::class.java)
startActivity(intent)
这种方式在APP内打开其他页面的时候可以这样显示启动,但是如果有外部的其他app,或者webview需要打开一个app的时候,很显然就不能用这种方式了。生活中你肯定遇到过下面的这种场景,你分享了一个京东的购物链接到微信,然后打开链接的时候会提示你打开京东APP。

那这个DeepLink是如何实现的呢?这就要说到Intent的另外一种启动方式:隐式启动。众所周知,显式启动通过设置class的name的方式与注册文件中activity配置的name如果一致的话那么就能找到对应的activity。而隐式启动并不需要设置class的name,它可以通过设置intent的action、category以及data来匹配对应的页面。这种方式可以很方便的从外部拉起app的内页。例如:
<activity
android:name=".SecondActivity"
android:exported="true">
<intent-filter>
<action android:name="com.wcz.deeplinkdemo.SECOND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
在注册文件中配置intent-filter,主要需要配置action和category,action属性的值为一个字符串,它代表了系统中已经定义了一系列常用的动作。常用的类型有:
android.intent.action.MAIN
android.intent.action.VIEW
上面这两种类型你一定不陌生,通常作为app启动页的activity会如下配置:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
ACTION_MAIN:Android Application的入口,每个Android应用必须且只能包含一个此类型的Action声明。
ACTION_VIEW:系统根据不同的Data类型,通过已注册的对应Application显示数据。
action_view是最常见的action类型,默认的action也是这个,可以在Intent类的源码中看到:
/**
* A synonym for {@link #ACTION_VIEW}, the "standard" action that is
* performed on a piece of data.
*/
public static final String ACTION_DEFAULT = ACTION_VIEW;
除了这两种action以外,还有很多种类型的action,当然我们也可以自定义action,来拉起对应的activity。例如在注册文件中配置了SecondActivity的action为"com.wcz.deeplinkdemo.SECOND"之后,对应的在跳转的时候只要设置对应的action,就可以跳转到目标activity。
val intent = Intent("com.wcz.deeplinkdemo.SECOND")
startActivity(intent)
说到这里就不得不提一个最常见的自定义action的场景:消息推送
。最近在接入OPPO厂商推送通道的时候其官方文档上要求给跳转落地页做一个自定义的action配置,如下:
<activity android:name=".broadcastReceiver.InternalActivity"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Translucent">
<intent-filter>
// 自定义action
<action android:name="com.coloros.push.jdmall.internal" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
这个自定义的action需要与oppo推送后台的配置填写一样,当消息通知到来的时候,方便oppo推送的SDK能够通过action准确的拉起对应的InternalActivity。
说回来我们上面提到的从外部链接跳转到App内页应该怎么做呢,这里就不得不提到intent-filter的另外一个属性配置data
:
<data
android:scheme="xxxx"
android:host="xxxx"
android:port="xxxx"
android:path="xxxx"
android:pathPattern="xxxx"
android:pathPrefix="xxxx"
android:mimeType="xxxx"/>
scheme:协议类型,我们可以自定义,一般是项目或公司缩写,String
host:域名地址,String
port:端口,int。
path:访问的路径,String
pathPrefix:访问的路径的前缀,String
pathPattern:访问路径的匹配格式,相对于path和pathPrefix更为灵活,String
mimeType:资源类型,例如常见的:video/*, image/png, text/plain。
通常情况下,只用配置scheme和host就行了,而且这两个是必须要配置的,在app内,scheme通常是比较固定的,host可以根据页面的不同进行自定义。实际上data的配置相当于为当前页面绑定了一个Uri地址,通过这个Uri地址我们就可以访问到当前的activity,整个data的结构就类似于:
<scheme> :// <host> : <port> / [ <path> | <pathPrefix> | <pathPattern> ]
现在我们对SecondActivity增加data属性:
<intent-filter>
// 注意这里的action和category需要指定
<action android:name="com.wcz.deeplinkdemo.SECOND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="wcz"
android:host="demo"
android:path="/deepLink" />
</intent-filter>
然后在代码中实现跳转:
val intent = Intent()
intent.data = Uri.parse("wcz://demo/deepLink")
startActivity(intent)
这样你会发现也能实现跳转。下面我们来实现外部链接跳转到App,首先写一个html文件:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
<meta name="Generator" content="Cocoa HTML Writer">
<meta name="CocoaVersion" content="1561.4">
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 17.0px; font: 12.0px 'Songti SC'; color: #000000; -webkit-text-stroke: #000000; min-height: 17.0px}
span.s1 {font-kerning: none}
</style>
</head>
<body>
<a href="wcz://demo/deepLink">deepLink拉起app</a>
</body>
</html>
很简单的一个html文件,主要就是在a标签中把地址配置好,按照scheme://host:port/path的这种格式。html配置好了之后,放到手机里面,然后用浏览器打开,之后点击按钮你会发现没有任何反应。这里我们看一下SecondActivity中配置的intent-filter,会发现少了一个action,通常在外部要拉起一个app,需要配置
<action android:name="android.intent.action.VIEW" />
,如果不配置这个action,将无法拉起跳转的弹框。
<intent-filter>
// 注意这里的action和category需要指定
<action android:name="com.wcz.deeplinkdemo.SECOND" />
// 沒有这个action不会出现跳转提示框
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="wcz"
android:host="demo"
android:path="/deepLink" />
</intent-filter>
配置完这个action之后,再次尝试,发现还是拉不起来,我们应该还少了什么配置,这里我们如果要想通过浏览器来拉起APP,需要配置
<category android:name="android.intent.category.BROWSABLE" />
在源码api的注释中提到了:如果想要让activity能够安全的被浏览器拉起的话就一定要支持这个category
/**
* Activities that can be safely invoked from a browser must support this
* category. For example, if the user is viewing a web page or an e-mail
* and clicks on a link in the text, the Intent generated execute that
* link will require the BROWSABLE category, so that only activities
* supporting this category will be considered as possible actions. By
* supporting this category, you are promising that there is nothing
* damaging (without user intervention) that can happen by invoking any
* matching Intent.
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE";
所以配置完category之后就是这样的:
<intent-filter>
// 沒有这个action不会出现跳转提示框
<action android:name="android.intent.action.VIEW" />
<action android:name="com.wcz.deeplinkdemo.SECOND" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="wcz"
android:host="demo"
android:path="/deepLink" />
</intent-filter>
最后试一下是否能够拉起:

现在就可以正常拉起App了
总结
可以看到跨进程,或者由浏览器拉起App这种场景都可以通过隐式调用来实现。
网友评论