Android:从零开始打造自己的深度链接库(四):DeepLinkSo实战

前言

今天是这个系列的最后一篇,如果你还看过之前的内容,可以先阅读:

Android:从零开始打造自己的深度链接库(一):ARouter简介

Android:从零开始打造自己的深度链接库(二):ARouter源码解析

Android:从零开始打造自己的深度链接库(三):自定义XML协议

我们将借鉴ARouter的开发思路,扩展新的功能。上一篇我们已经订好了XML协议,并且把XML中的内容,保存到了DeepLinkSoConfig中,接下来就是完成剩下的功能。

正文

首先我们需要一个入口,类似于ARouter的SchameFilterActivity:

/**
 * DeepLink中转页面
 * */
class DeepLinkSoActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_deep_link)

        DeepLinkSoNavigator.startActivity(this)
        finish()
    }
}

这个Activity会被其他的App吊起,这个时候会有一个问题:

这个Activity会被入栈到对方的栈中。

为了解决这个问题,我推荐给他设置为SingleInstance,单独申请一个栈,这样就不会显示在启动的app的栈中。


        

            
            
                

                
                

                
            
        

具体的跳转逻辑已经被封装到:

/**
     * 通过中转的页面,跳转到指定的页面
     * */
    fun startActivity(activity: Activity) {
        // 首先判断是否是从深度链接跳转过来的
        val uri = activity.intent.data ?: return
        val page = uri.getQueryParameter("page")
        // 启动app
        if (Utils.isEmpty(page)) {
            DeepLinkSoNavigator.launchApp(activity)
            return
        }

        // 创建跳转请求
        val request = createDeepLinkSoRequest(uri, page)
        // 检查配置是否正确
        if (!checkOptionAndParams(activity, request)) {
            deepLinkFailed(activity)
            return
        }

        // 判断是否被拦截器拦截
        if (needIntercept(activity, request)) {
            deepLinkFailed(activity)
            return
        }

        // 跳转页面
        if (request.option!! is DeepLinkSoActivityOption) {
            startActivity(activity, request)
        }
        // 调用指定的EventHandler处理此次跳转请求
        else if (request.option is DeepLinkSoEventOption) {
            callEventHandler(activity, request)
        }

        activity.finish()
    }

这是整个跳转的逻辑,接下来我们逐个分析每一步。

1、如果我们没有设置任何的跳转参数,会默认启动我们的App。

 if (Utils.isEmpty(page)) {
          DeepLinkSoNavigator.launchApp(activity)
          return
}

2、如果有参数,从Uri中查询出所有的参数,创建跳转的具体请求。

/**
     * 创建深度链接请求
     * */
    private fun createDeepLinkSoRequest(uri: Uri, page: String?): DeepLinkSoRequest {
        // 得到指定的配置信息
        val option = DeepLinkSoClient.config.getOption(page!!)
        // 查询此次跳转在uri中的参数
        val params = option?.let { queryParams(it, uri) }
        return DeepLinkSoRequest(uri, option, params)
    }

    /**
     * 查询此次跳转的参数
     * */
    private fun queryParams(option: DeepLinkSoOption, uri: Uri): HashMap? {
        val params = HashMap()
        if (option.params != null && option.params!!.size > 0) {
            for (param in option.params!!) {
                val value = uri.getQueryParameter(param.key)
                // 目前不允许参数为空
                if (Utils.isEmpty(value)) {
                    // 返回null
                    return null
                }
                // 把参数加入到集合中
                if (param.type == DeepLinkSoConstant.INT) {
                    params[param.key] = value!!.toInt()
                } else if (param.type == DeepLinkSoConstant.LONG) {
                    params[param.key] = value!!.toLong()
                } else if (param.type == DeepLinkSoConstant.FLOAT) {
                    params[param.key] = value!!.toFloat()
                } else if (param.type == DeepLinkSoConstant.DOUBLE) {
                    params[param.key] = value!!.toDouble()
                }
                // 默认是字符串
                else {
                    params[param.key] = value!!
                }
            }
        }
        return params
    }

首先根据page参数去DeepLinkSoConfig中找到我们在XML设置的跳转项,这里会有两个异常情况:

1、如果没有找到option会返回null;
2、如果参数匹配有null的情况;

所以我们检查创建跳转请求的过程是否出现异常:

/**
     * 检查配置是否正确
     * */
    private fun checkOptionAndParams(activity: Activity, request: DeepLinkSoRequest): Boolean {
        // 检查是否得到了option配置
        if (request.option == null) {
            DeepLinkSoClient.config.listener?.onDeepLinkFailed(activity, request,
                    DeepLinkSoFailedException(DeepLinkSoFailedException.PAGE_NOT_REGISTER,
                            "The option of the page is not found, have you register it in DeepLinkSo.xml?"))
            return false
        }

        // 检查了uri链接中是否配置了正确的参数
        if (request.params == null) {
            DeepLinkSoClient.config.listener?.onDeepLinkFailed(activity, request,
                    DeepLinkSoFailedException(DeepLinkSoFailedException.PARAMS_NULL, "param can not be null"))
            return false
        }
        return true
    }

3、拦截器是否要拦截此次跳转

/**
     * 是否要拦截此次跳转
     * */
    private fun needIntercept(activity: Activity, request: DeepLinkSoRequest): Boolean {
        // 先判断公共拦截器
        if (!request.shouldSkipCommonInterceptors() && needInterceptInner(activity, request, DeepLinkSoClient.config.interceptors)) {
            DeepLinkSoClient.config.listener?.onDeepLinkFailed(activity, request,
                    DeepLinkSoFailedException(DeepLinkSoFailedException.INTERCEPT, "failed by common interceptor"))
            return true
        }

        // 再判断私有拦截器
        if (needInterceptInner(activity, request, request.option!!.interceptors)) {
            DeepLinkSoClient.config.listener?.onDeepLinkFailed(activity, request,
                    DeepLinkSoFailedException(DeepLinkSoFailedException.INTERCEPT, "failed by private interceptor"))
            return true
        }

        return false
    }

4、跳转Activity或发送自定义Event。

// 跳转页面
        if (request.option!! is DeepLinkSoActivityOption) {
            startActivity(activity, request)
        }
        // 调用指定的EventHandler处理此次跳转请求
        else if (request.option is DeepLinkSoEventOption) {
            callEventHandler(activity, request)
        }

        activity.finish()

其中跳转到Activity,我们要把对应的参数放到intent中:

/**
     * 启动深度链接对应的页面
     * @param context 跳转的上下文
     * @param request 跳转请求
     * */
    fun startActivity(context: Context, request: DeepLinkSoRequest) {
        val option = request.option!!
        val params = request.params!!
        val intent = Intent(context, option.className)
        // 把参数放入intent
        if (option.params?.size ?: 0 > 0) {
            for (param in option.params!!) {
                if (param.type == DeepLinkSoConstant.INT) {
                    intent.putExtra(param.key, params[param.key] as Int)
                } else if (param.type == DeepLinkSoConstant.LONG) {
                    intent.putExtra(param.key, params[param.key] as Long)
                } else if (param.type == DeepLinkSoConstant.FLOAT) {
                    intent.putExtra(param.key, params[param.key] as Float)
                } else if (param.type == DeepLinkSoConstant.DOUBLE) {
                    intent.putExtra(param.key, params[param.key] as Double)
                } else {
                    intent.putExtra(param.key, params[param.key] as String)
                }
            }
        }
        try {
            context.startActivity(intent)
            DeepLinkSoClient.config.listener?.onDeepLinkSuccess(context, request)
        } catch (e: Exception) {
            DeepLinkSoClient.config.listener?.onDeepLinkFailed(context, request,
                    DeepLinkSoFailedException(
                            DeepLinkSoFailedException.UNKNOWN,
                            "startActivity failed by unknown reason"
                    ))
        }
    }

我们的功能流程已经完成了,最后看看要怎么初始化我们的DeepLinkSo相关配置,目前我们已经已知有三个配置项:

1、通过context获取本地默认XML配置。
2、通过path设置外部XML路径。
3、关于跳转的各种监听。

并且我们希望在完成初始化以后,不能再对配置进行修改,所以这里我选择使用构建者模式,最终的初始化代码为:

public class MyApplication extends Application implements IDeepLinkSoListener {

    @Override
    public void onCreate() {
        super.onCreate();

         DeepLinkSoClient.builder()
                .setContext(this)
                .setDeepLinkSoListener(this)
                .build();
                
        // 申请读写内存卡权限
//        AndPermission.with(this).runtime()
//                .permission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
//                .onGranted(new Action>() {
//                    @Override
//                    public void onAction(List data) {
//                          DeepLinkSoClient.builder()
//                                .setXMLPath(Environment.getExternalStorageDirectory() + "/DeepLinkSo.xml")
//                                .setDeepLinkSoListener(this)
//                                .build();
//                    }
//                })
//                .start();
    }
 }

总结

这篇我们主要是分析了整个DeepLinkSo的设计开发思路,由于已经过去了很久,当初的开发细节也记得不是很清楚了,大家可以看一下源码,有什么问题欢迎留言。

这个系列就到此结束,最后祝大家五一节日快乐。

你可能感兴趣的:(Android,开源框架解析系列)