Android Dynamic Action(动态Action)—像访问网页一样地访问Activity
Android Dynamic Action,简称DA,是一种简便、可变Action的实现方案。DA框架的初衷是为了取代Context.startActivity的调用方式,使用建造者模式(Builder Pattern)构建交互参数,使程序更优美。DA框架能够对任何一个已经存在的Action修改,动态改变原有的跳转逻辑。值得一提的是,DA框架不仅友好地实现了与H5间的跳转交互,也解决了Activity在插件化项目的交互问题。
项目地址:https://github.com/benniaobuguai/android-dynamic-action
DA的URI基本结构
在DA框架下,Activity是一个有趣的概念实体,每一个Activity都可视作DA框架下的一种资源。对于一个客户端而言,每个Activity都是全局唯一可访问的资源,因此每个Activity都有统一资源标识符(URI)。
URI的基本结构:
scheme://com.example.project:8888/path/etc?id=1024
\-----/ \------------------/\--/ \------/\-------/
scheme host port path query parameter
\---------------------/
authority
DA框架基于标准的URI,定制了更符合Android Activity交互的URI结构。
定制后的URI基本结构:
scheme://packageId$ActionName?data={"id":"1024"}
\-----/ \------------------/\-----------------/
scheme host query parameter
基于以上协议,定义属于自己的scheme,每个Activity将具有一个可被访问的URI,你就能够像访问网页一样访问Activity啦!!!
DA的配置文件
DA框架的“动态可变性”体现在配置文件上,DA框架遵循“约定优于配置”的原则,使用更少的配置达到目的。配置文件非常简单,仅包含scheme以及包名的映射关系。
配置文件示例:
<?xml version="1.0" encoding="UTF-8"?>
<DynamicAction xmlns:opencdk="http://www.opencdk.com/dynamicaction"
opencdk:version="1.0.0" >
<constant name="DA.devMode" value="true" />
<constant name="DA.scheme" value="opencdk" />
<constant name="DA.appscheme" value="opencdkexample" />
<package id="0" name="com.opencdk.da.ui" >
</package>
<package id="1" name="com.opencdk.da.ui.user" >
</package>
<package id="2" name="com.opencdk.da.ui.video" >
<!-- 用H5登录界面来修复有BUG的原生登录 -->
<action name="Login" from="home_login_click" to="0$Browser?title=H5登录&url=http://www.opencdk.com/login.html" />
</package>
</DynamicAction>
参数说明:
- DA.devMode:开发模式,开关打开后,后台将可看到更多日志
- DA.scheme:DA框架内部使用的scheme,建议根据自己的业务定义唯一的scheme
- DA.appscheme:非DA框架直接使用,是App提供给第三方App的scheme
- package:包名的映射关系,文章最后有完整配置
- package>id:包id,与包名一一对应
- package>name:包名
- action:动态修复的Action
- action>name:Action的名称
- action>from:事件源,触发此事件的源头
- action>to:目标地址(DA框架标准的URI数据结构)
- action>to>title:Activity标题
- action>to>url:网址
DA的代码实现
假设在com.opencdk.da.ui包下有LoginActivity,访问的scheme可表示为:
opencdk://1$Login
使用DA框架后,启动LoginActivity则变得非常容易:
new DA.Builder(Context)
.setHost("1$Login")
.go();
或:
new DA.Builder(Context)
.setUriString("opencdk://1$Login")
.go();
或:
new DA.Builder(Context)
.setPackageId("1")
.setActionName("Login")
.go();
非常简单的实现方式,为动态运营奠定了基础。基于DA框架,开发同学可以轻松构建项目规范,编写更优雅的代码;测试同学编写测试用例也变得更容易了。
DA的数据交互
DA框架不但支持原生Activity间的数据交互,而且也支持Activity与H5间的数据交互。保证数据协议的一致性,DA框架统一使用JSON进行数据交互(推荐使用fastjson)。
URI表示如下:
opencdk://1$Login?data={"username":"benniaobuguai"}
代码调用:
new DA.Builder(Context)
.setHost("1$Login")
.setData("{\"username\":\"benniaobuguai\"}")
.go();
DA支持Activity回调(Context.startActivityForResult)
URI表示如下:
opencdk://1$Login?data={"username":"benniaobuguai"}&requestCode=10000
代码调用:
new DA.Builder(Context)
.setHost("1$Login")
.setData("{\"username\":\"benniaobuguai\"}")
.setRequestCode(10000)
.go();
PS: 建议在Activity间交互时使用事件总线,如:EventBus,otto等。
DA动态修改默认跳转
- 用一个Activity替换另外一个Activity
<package id="2" name="com.opencdk.da.ui.video" >
<!--- 随机推荐视频列表界面修改成免费视频列表界面 -->
<action name="VideoRandomList" from="home_video_random_click" to="2$VideoFreeList" />
</package>
- 用一个H5界面替换一个Activity
<package id="2" name="com.opencdk.da.ui.video" >
<!-- 随机推荐视频列表界面修改成H5的免费视频推荐界面 -->
<action name="VideoRandomList" from="home_video_random_click" to="0$Browser?title=精彩免费视频推荐&url=http%3a%2f%2fwww.iqiyi.com%2fdianying%2ffree.html" />
</package>
注意:
- xml文件不支持直接使用&、<、>等特殊字符,应该使用其对应的转义字符。具体可参考:XML和HTML常用转义字符
- url地址需要进行URL编码,避免特殊字符对URI的解析造成影响。在线URL编码
DA启用拦截器功能
往往有些时候,我们需要对某些界面进行访问控制。
已经发布的版本,任何用户都可访问VIP视频列表。临时需求变更,只有登录的用户才能访问VIP视频列表界面,可修改配置下发:
<interceptors>
<interceptor
name="LoginInterceptor"
class="com.opencdk.da.interceptor.LoginInterceptor" >
</interceptor>
<interceptor
name="TestInterceptor"
class="com.opencdk.da.interceptor.TestInterceptor" >
</interceptor>
<actionInterceptor>
<accept name="2$VideoVIPList" >
<interceptor-ref>LoginInterceptor</interceptor-ref>
</accept>
</actionInterceptor>
</interceptors>
H5与原生程序的交互
引入DA框架后,在H5界面也可前往任意的原生界面,代码也非常简单。
H5代码片断:
<p><a href="opencdk://1$Login">Login</a></p>
<p><a href="opencdk://2$VideoPlay">Play Video</a></p>
<p><a href="opencdk://0$Browser?url=http://www.opencdk.com">http://www.opencdk.com</a></p>
重写WebView WebViewClient的方法shouldOverrideUrlLoading(WebView view, String url),支持自定义的scheme。
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url != null && url.startsWith(DALoader.getScheme())) {
new DA.Builder(mContext)
.setUriString(url)
.go();
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
});
DA框架解决的核心问题
- 一个地址可达任意Activity
- 任意一个Activity可被动态修改为另一个Activity
- DA框架数据交互扁平化,使用JSON便于与H5交互
- 任意Activity可替换成H5,快速修复原生突发BUG
- 插件化项目中也能满足以上需求
DA框架所遵循的一些约定
DA框架默认遵循以下规则:
- 配置优先级>Java代码优先级,保证通过配置可修复代码编码的缺陷。
- 查找动态配置时,先根据name+from进行精确查找,其次根据name去查找。
示例说明
为了更好地体现动态Action,示例中在assets目录下放了多个配置文件,加载不同的配置文件就相当于网络下发了新的配置文件。
- dynamic_action.cfg,默认的配置文件,仅包含包结构映射关系
- dynamic_action_transfer.cfg,配置一个动态Action
- dynamic_action_interceptor.cfg,配置一个拦截器
进阶与思考
- 客户端与客户端交互
客户端与客户端之间的交互是不安全的,对于暴露给第三方的入口都需要进行校验。就DA框架而言,主要是为了解决内部跳转的统一协议而确定的scheme(opencdk://),提供外部使用的scheme(opencdkexample://)。opencdk://仅供内部使用,认为是可信任的、安全的。opencdkexample://是外部协议,必须经过校验方可拉起我们的客户端。
需要提供外部入口,必须要在AndroidManifest.xml里面定义,
<activity
android:name="com.opencdk.da.ui.SplashActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/DA.Theme.NoTitleBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<!-- 定义给外部调用 的scheme -->
<data android:scheme="opencdkexample" />
</intent-filter>
</activity>
新建一个项目,执行外部调用代码片断:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_DEFAULT);
Uri data = Uri.parse("opencdkexample://0$Browser?url=http://v.qq.com/cover/r/rm3tmmat4li8uul/w0019k37ecc.html");
intent.setData(data);
startActivity(intent);
- iOS对本协议的支持
iOS并无包名概念,如果希望使用同一个URI来跳转至同一个界面,iOS在对URI的处理时,应当直接过滤包名后再使用。
登录配置如下:
opencdk://1$Login?data={"username":"benniaobuguai"}
解析【host】协议时,直接取【ActionName】,忽略前面的 "1$" 即可。
- 对于简单的项目,Android把所有Activity放在一个包名下(不建议这么做),也可与iOS保持URI同一处理逻辑。
URI表示如下:
opencdk://Login?data={"username":"benniaobuguai"}
PS: DA框架当前不支持无包名的实现方式。
注意点
- 避免循环对Action进行中转
- 通过反射访问目标Activity,ActionName的大小写敏感
- 避免过多的包名映射,Activity所在的包不宜过多,包越多维护成本越大。
- Activity不能被混淆
其他
- 运营的灵活性,增强运营配置的自由度
- 界面可替换性,任意Activity可替换成H5,提供快速使用H5修复BUG的能力
- 插件访问简单化,宿主程序是无法直接获取插件的Activity对象,DA框架的作用尤其明显。
- DA框架最大的问题是全局可访问任意Activity,如何保证被访问者的安全,业务不受到影响就显示尤为重要了
- DA框架与传统的Context.startActivity最大的区别在于:交互协议标准化、灵活、运营能力强,动态修复能力强
Q&A:
1.为什么需要制定两个scheme?
答:隔离
2.配置from to过多时, 性能如何?
答:配置from和to时,属于修复BUG的行为,原则上并不会太多。通过HashMap查找,速度很快。
3.配置文件过大时, 性能如何?
答:未验证。
完整的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<DynamicAction xmlns:opencdk="http://www.opencdk.com/dynamicaction"
opencdk:version="1.0.0" >
<constant name="DA.devMode" value="true" />
<constant name="DA.scheme" value="opencdk" />
<constant name="DA.appscheme" value="opencdkexample" />
<package id="0" name="com.opencdk.da.ui" >
</package>
<package id="1" name="com.opencdk.da.ui.user" >
</package>
<package id="2" name="com.opencdk.da.ui.video" >
<action name="VideoRandomList" from="home_video_random_click" to="2$VideoFreeList" />
</package>
<interceptors>
<interceptor
name="LoginInterceptor"
class="com.opencdk.da.interceptor.LoginInterceptor" >
</interceptor>
<interceptor
name="TestInterceptor"
class="com.opencdk.da.interceptor.TestInterceptor" >
</interceptor>
<actionInterceptor>
<accept name="2$VideoVIPList" >
<interceptor-ref>LoginInterceptor</interceptor-ref>
</accept>
</actionInterceptor>
</interceptors>
</DynamicAction>
Android Dynamic Action(动态Action)—像访问网页一样地访问Activity的更多相关文章
- Android 自定义title 之Action Bar
Android 自定义title 之Action Bar 2014-06-29 飞鹰飞龙... 摘自 博客园 阅 10519 转 25 转藏到我的图书馆 微信分享: Action Ba ...
- Android网络:HTTP之利用HttpURLConnection访问网页、获取网络图片实例 (附源码)
http://blog.csdn.net/yanzi1225627/article/details/22222735 如前文所示的TCP局域网传送东西,除了对传输层的TCP/UDP支持良好外,Andr ...
- Android 外部启动activity,自定义action,action常量大全
从任意app,启动另外一个app的activity: 1. Intent i = new Intent(); ComponentName cn = new ComponentN ...
- Struts2的动态Action和全局跳转视图以及配置各项默认值
1:Struts2的默认访问后缀是.action(特别需要注意的是改了配置文件web.xml或者struts.xml需要重启服务器) 2:Struts2中常用的常量介绍:<!-- 一:全局配置 ...
- web10 动态action的应用
电影网站:www.aikan66.com 项目网站:www.aikan66.com游戏网站:www.aikan66.com图片网站:www.aikan66.com书籍网站:www.aikan66.co ...
- struts2 动态Action
1.java 2.struts.xml struts2.5,默认关闭动态Action,着色的是开启和使用动态action 3.JSP 小结:访问时,用!后跟方法名的方法,方法返回值----->r ...
- Struts2 Action 动态传参数
Struts2的两个Action之间传参的问题. 需求功能是这样:Action1 获取数据库配置内容,得到相应Model的 动态URL ,这里的URL 有的是Action有的是JSP页面. 1.使用r ...
- Android开发之Intent.Action
1 Intent.ACTION_MAIN String: android.intent.action.MAIN 标识Activity为一个程序的開始.比較经常使用. Input:nothing Out ...
- Android开发之Intent.Action 各种Action的常见作用
1 Intent.ACTION_MAIN String: android.intent.action.MAIN 标识Activity为一个程序的开始.比较常用. Input:nothing Outpu ...
随机推荐
- UVa-208 Firetruck (图的DFS)
UVA-208 天道好轮回.UVA饶过谁. 就是一个图的DFS. 不过这个图的边太多,要事先判一下起点和终点是否联通(我喜欢用并查集),否则会TLE. #include <iostream> ...
- 检查DLL,EXE文件是64位或者32位的方法
检查DLL,EXE文件是64位或者32位:输入corflags <assembly path>:
- 洛谷P3961 图的遍历
题目来源 做这道题的方法不少. 在这里我只提一种 就是大法师. 可以采用反向建边,从最大的点开始dfs 我们考虑每次从所剩点中最大的一个点出发,我们暂且称它为i,而凡是i这个点所能到达的点,可以到达的 ...
- css 元素垂直居中
通用 <div id="parent"> <div id="child">Content here</div> </d ...
- 为什么我打的jar包没有注解?
本文来自网易云社区 作者:王飞 一.前言 一切问题的起源就是来自一个问题"为什么我打的jar包没有注解?",带着这个疑问查了一圈资料,原来问题主要是在没有将源码中的注释进行抽取打包 ...
- c#笔记2018-12-27
using System; /*2018-12-27 c#学习笔记 * 1.c#判断if /else if /switch * 2.循环while/for/do-while * 3.循环实例: for ...
- 组合数学的卡特兰数 TOJ 3551: Game of Connections
这个就是卡特兰数的经典问题 直接用这个公式就好了,但是这个题涉及大数的处理h(n)=h(n-1)*(4*n-2)/(n+1) 其实见过好几次大数的处理了,有一次他存的恰好不多于30位,直接分成两部分l ...
- element-ul InputNumber 空白
if(this.days == undefined){ this.$nextTick(function(){ this.days = 1; }); }
- PAT天梯赛练习题——L3-007. 天梯地图(多边权SPFA)
L3-007. 天梯地图 时间限制 300 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 本题要求你实现一个天梯赛专属在线地图,队员输入自己学校 ...
- 球形空间产生器 BZOJ 1013
球形空间产生器 [问题描述] 有一个球形空间产生器能够在n维空间中产生一个坚硬的球体.现在,你被困在了这个n维球体中,你只知道球面上n+1个点的坐标,你需要以最快的速度确定这个n维球体的球心坐标,以便 ...