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间交互时使用事件总线,如:EventBusotto等。

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>

注意:

  1. xml文件不支持直接使用&、<、>等特殊字符,应该使用其对应的转义字符。具体可参考:XML和HTML常用转义字符
  2. 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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,465评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • 1.什么是Activity?问的不太多,说点有深度的 四大组件之一,一般的,一个用户交互界面对应一个activit...
    JoonyLee阅读 5,726评论 2 51
  • 大致所有的味道除了舌头说好吃之外,还得让思想说好吃,这件事就开始变得人情味起来,饮食文化,是一门很大的学问。 舌尖...
    罗帆文集阅读 476评论 4 4
  • 好不好你不好
    王爷天下阅读 135评论 0 0