DeepLinkDispatch提供了一种声明式的, 基于注解的API, 用于定义应用深度链接.本文是TonnyL创作的文章,希望可以支持下原作者的博客。如果你想学习,找不到好的途径,学习提高技术的方法,提高面试技术等都可以公众号后台咨询,关注本留言。
点击标题下「蓝色微信名」可快速关注
什么是DeepLink深度链接?
废话不多说,先看图:
DeepLink
一个在Telegram中的dribbble链接, 点击后直接跳转到我的 Mango中, 是不是很神奇?
为什么要使用DeepLink?
一句话总结便是提升用户体验: 原生App在功能和体验上肯定是要强于网页的.
DeepLinkDispatch
DeepLinkDispatch提供了一种声明式的, 基于注解的API, 用于定义应用深度链接.
我们可以注册一个Activity, 并用@DeepLink和一个URI注解,然后她就可以处理特定的深度链接了.没错,就是这么简单. DeepLinkDispatch会对URI进行转换,并将深度链接和URI中特定的参数一起分发给合适的Activity.
举个?
下面,我们注册一个ShotActivity,并从像https://dribbble.com/shots/12345的链接中获取一个ID. 我们使用@DeepLink
注解并且标注出将会有一个参数被标识为id.
@DeepLink("https://dribbble.com/shots/{id}")
class ShotActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val isDeepLink = intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)
if (isDeepLink) {
var id = intent.extras.getString("id")
// Got the id ?
}
}
}
多个深度链接
有时候我们可能需要在一个Activity中处理多种链接:
@DeepLink("https://dribbble.com/shots/{id}, https://dribbble.com/anotherDeepLink")
class ShotActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val isDeepLink = intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)
if (isDeepLink) {
var id = intent.extras.getString("id")
// Got the id ?
}
}
}
方法注解
我们还可以将@DeepLink注解用于任何public static
方法(在Kotlin中即companion object
中的方法). DeepLinkDispatch会调用这个方法来创建Intent
companion object {
@DeepLink("foo://example.com/methodDeepLink/{param1}")
fun intentForDeepLinkMethod(context: Context): Intent {
return Intent(context, MainActivity::class.java)
.setAction(ACTION_DEEP_LINK_METHOD)
}
}
如果我们需要Intent的extras, 可以直接在方法中添加一个Bundle类型的参数,例如:
@DeepLink("foo://example.com/methodDeepLink/{param1}")
fun intentForDeepLinkMethod(context: Context, extras: Bundle): Intent {
val uri = Uri.parse(extras.getString(DeepLink.URI)).buildUpon()
return Intent(context, MainActivity::class.java)
.setData(uri.appendQueryParameter("bar", "baz").build())
.setAction(ACTION_DEEP_LINK_METHOD)
}
如果我们需要定制Activity的返回栈, 可以返回一个TaskStackBuilder而不是一个Intent. DeepLinkDispatch会调用被注解的方法,从TaskStackBuilder的最后一个Intent创建Intent, 当从已经注册的deep link启动Activity时使用.
@DeepLink("http://example.com/deepLink/{id}/{name}")
fun intentForTaskStackBuilderMethods(context: Context): TaskStackBuilder {
val detailsIntent = Intent(context, SecondActivity::class.java).setAction(ACTION_DEEP_LINK_COMPLEX)
val parentIntent = Intent(context, MainActivity::class.java).setAction(ACTION_DEEP_LINK_COMPLEX)
val taskStackBuilder = TaskStackBuilder.create(context)
taskStackBuilder.addNextIntent(parentIntent)
taskStackBuilder.addNextIntent(detailsIntent)
return taskStackBuilder
}
查询参数
查询参数被自动的转换和传递,并且像其他参数一样是可获取的. 例如, 我们可以获取URI foo://example.com/deepLink?qp=123 传递过来的查询参数:
@DeepLink("foo://example.com/deepLink")
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = intent
if (intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)) {
val parameters = intent.extras
parameters?.getString("qp")?.let {
val queryParameter = parameters.getString("qp")
// Do something with the query parameter...
}
}
}
}
回调
我们可以注册BroadcastReceiver,当通过深度链接进入我们的应用时, 她将被调用, 当然, 这是可选的. 当处理深度链接时,DeepLinkDispatch通过LocalBroadcastManager发送广播并传递成功或者失败的Intent. Intent会携带下面的extras被传播.
DeepLinkHandler.EXTRA_URI
: 深度链接的URI.
DeepLinkHandler.EXTRA_SUCCESSFUL
: 深度链接是否成功.
DeepLinkHandler.EXTRA_ERROR_MESSAGE
: 如果出错, 错误信息.
我们可以注册一个接收器,用于接收intent. 下面是一个使用的例子:
class DeepLinkReceiver : BroadcastReceiver() {
companion object {
private val TAG = "DeepLinkReceiver"
}
override fun onReceive(context: Context, intent: Intent) {
val deepLinkUri = intent.getStringExtra(DeepLinkHandler.EXTRA_URI)
if (intent.getBooleanExtra(DeepLinkHandler.EXTRA_SUCCESSFUL, false)) {
Log.i(TAG, "Success deep linking: " + deepLinkUri)
} else {
val errorMessage = intent.getStringExtra(DeepLinkHandler.EXTRA_ERROR_MESSAGE)
Log.e(TAG, "Error deep linking: $deepLinkUri with error message +$errorMessage")
}
}
}
class App : Application() {
override fun onCreate() {
super.onCreate()
val intentFilter = IntentFilter(DeepLinkHandler.ACTION)
LocalBroadcastManager.getInstance(this).registerReceiver(DeepLinkReceiver(), intentFilter)
}
}
自定义注解
我们可以创建自定义注解来减少深度链接的重复. 自定义注解提供公共的前缀, 这些公共前缀会被自动应用到每一个被自定义注解注解的类和方法. 自定义注解一个比较流行的用法便是在web App的深度链接中:
// Prefix all app deep link URIs with "app://airbnb"
@DeepLinkSpec(prefix = arrayOf("app://airbnb"))
annotation class AppDeepLink(vararg val value: String)
// Prefix all web deep links with "http://airbnb.com" and "https://airbnb.com"
@DeepLinkSpec(prefix = arrayOf("http://airbnb.com", "https://airbnb.com"))
annotation class WebDeepLink(vararg val value: String)
// This activity is gonna hanndle the following deep links:
// "app://airbnb/view_users"
// "http://airbnb.com/users"
// "http://airbnb.com/user/{id}"
// "https://airbnb.com/users"
// "https://airbnb.com/user/{id}"
@AppDeepLink("/view_users")
@WebDeepLink("/users", "/user/{id}")
class CustomPrefixesActivity : AppCompatActivity()//...
使用方法
将下面的代码添加至build.gradle文件中:
dependencies {
compile 'com.airbnb:deeplinkdispatch:3.1.0'
annotationProcessor 'com.airbnb:deeplinkdispatch-processor:3.1.0'
}
创建我们的深度链接module(DeepLinkDispatch v3新推出).每一个被@DeepLinkModule注解的类, DeepLinkDispatch都会生成一个Loader类, 其中包含了所有@DeepLink注解的注册信息.
/This will generate a AppDeepLinkModuleLoader class /
@DeepLinkModule
class AppDeepLinkModule
可选部分: 如果我们的Android应用包含了多个module(例如独立的Android library工程), 我们需要为应用中的每一个Module都添加一个@DeepLinkModule注解类, 只有那样DeepLinkDispatch才能在每一个module中的一个loader类收集所有注解. This will generate a LibraryDeepLinkModuleLoader class @DeepLinkModule class LibraryDeepLinkModule
创建一个Activity(例如DeepLinkActivity), 需要带有要处理schema(在AndroidManifest.xml文件中注册, 下面的例子中使用foo示例):
<activity
android:name="com.example.DeepLinkActivity"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="foo" />
</intent-filter>
</activity>
用@DeepLinkHandler
注解我们的DeepLinkActivity
, 并提供所有@DeepLinkModule
注解的类的列表:
@DeepLinkHandler(AppDeepLinkModule::class, LibraryDeepLinkModule::class)
class DeepLinkActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// DeepLinkDelegate, LibraryDeepLinkModuleLoader and AppDeepLinkModuleLoader
// are generated at compile-time.
val deepLinkDelegate = DeepLinkDelegate(AppDeepLinkModuleLoader(), LibraryDeepLinkModuleLoader())
// Delegate the deep link handling to DeepLinkDispatch.
// It will start the correct Activity based on the incoming Intent URI
deepLinkDelegate.dispatchFrom(this)
// Finish this Activity since the correct one has been just started
finish()
}
}
小贴士
从DeepLinkDispatch v3
开始, 我们必须 一直 提供我们自己的Activity类并用@DeepLinkHandler注解. 她将不在被注解处理器(Annotation processor or kapt)自动生成.
Intent filters
只能包含一个URI pattern
的一个数据元素(data element). 过滤额外的URI pattern需要创建独立的intent filters.
参照sample app, 示例讲解了DeepLinkDispatch
库的用法.
开发版本的Snapshots可以在Sonatype's snapshots repository
获取.
生成深度链接的文档
我们可以告知DeepLinkDispatch生成带有所有深度链接注解的txt文本文档, 我们可以使用文档进行进一步的开发或者作为参考. 为了生成文档, 我们需要在build.gradle文件中添加如下代码:
tasks.withType(JavaCompile) {
options.compilerArgs << "-AdeepLinkDoc.output=${buildDir}/doc/deeplinks.txt"
}
文档会以下面的格式生成:
* {DeepLink1}\n|#|\n[Description part of javadoc]\n|#|\n{ClassName}#[MethodName]\n|##|\n
* {DeepLink2}\n|#|\n[Description part of javadoc]\n|#|\n{ClassName}#[MethodName]\n|##|\n
混淆规则
-keep class com.airbnb.deeplinkdispatch.* { ; } -keepclasseswithmembers class * { @com.airbnb.deeplinkdispatch.DeepLink ; }
小贴士: 不要忘记在混淆规则中包含我们使用过的自定义注解, 例如:
-keep @interface your.package.path.deeplink.* { ; } -keepclasseswithmembers class { @your.package.path.deeplink. ; }
测试示例应用
使用adb加载深度链接(在terminal中输入: adb shell).
这将触发一个标准的深度链接. 源注解: @DeepLink(“dld://example.com/deepLink”)
am start -W -a android.intent.action.VIEW -d "dld://example.com/deepLink"
com.airbnb.deeplinkdispatch.sample
我们可以包含多个路径参数(不需要包含示例应用的包名). 源注解:
@DeepLink("http://example.com/deepLink/{id}/{name}")
am start -W -a android.intent.action.VIEW -d "http://example.com/deepLink/123abc/myname"
需要注意的是有可能直接调用adb shell am … 不过这种方式有时可能会丢失URI, 所以最好是从shell中调用.
参考
https://github.com/airbnb/DeepLinkDispatch https://github.com/TonnyL/Mango
作者:TonnyL 链接:http://www.jianshu.com/p/7009e7c52400
转载请联系作者授权
相关推荐
RxFile 一款选择多媒体文件的精巧的工具
技术 - 思维 - 成长
END