今天是这个系列的最后一篇,如果你还看过之前的内容,可以先阅读:
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的设计开发思路,由于已经过去了很久,当初的开发细节也记得不是很清楚了,大家可以看一下源码,有什么问题欢迎留言。
这个系列就到此结束,最后祝大家五一节日快乐。