前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何在Android中优雅的分发深度链接

如何在Android中优雅的分发深度链接

作者头像
开发者技术前线
发布2020-11-23 15:42:13
2K0
发布2020-11-23 15:42:13
举报
文章被收录于专栏:开发者技术前线

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.

代码语言:javascript
复制
@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中处理多种链接:

代码语言:javascript
复制
@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

代码语言:javascript
复制
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类型的参数,例如:

代码语言:javascript
复制
@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时使用.

代码语言:javascript
复制
@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 传递过来的查询参数:

代码语言:javascript
复制
@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. 下面是一个使用的例子:

代码语言:javascript
复制
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")
        }
    }
}
代码语言:javascript
复制
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        val intentFilter = IntentFilter(DeepLinkHandler.ACTION)
        LocalBroadcastManager.getInstance(this).registerReceiver(DeepLinkReceiver(), intentFilter)
    }
}

自定义注解

我们可以创建自定义注解来减少深度链接的重复. 自定义注解提供公共的前缀, 这些公共前缀会被自动应用到每一个被自定义注解注解的类和方法. 自定义注解一个比较流行的用法便是在web App的深度链接中:

代码语言:javascript
复制
// 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文件中:

代码语言:javascript
复制
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示例):

代码语言:javascript
复制
<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注解的类的列表:

代码语言:javascript
复制
@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文件中添加如下代码:

代码语言:javascript
复制
tasks.withType(JavaCompile) {
  options.compilerArgs << "-AdeepLinkDoc.output=${buildDir}/doc/deeplinks.txt"
}

文档会以下面的格式生成:

代码语言:javascript
复制
* {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

我们可以包含多个路径参数(不需要包含示例应用的包名). 源注解:

代码语言:javascript
复制
@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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-09-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发者技术前线 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是DeepLink深度链接?
    • 举个?
      • 多个深度链接
        • 方法注解
          • 查询参数
            • 回调
              • 自定义注解
                • 使用方法
                • 小贴士
                • 混淆规则
            • 参考
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档