Android Dynamic Action(動態(tài)Action)—像訪問網(wǎng)頁一樣地訪問Activity

Android Dynamic Action隔披,簡稱DA洒疚,是一種簡便征冷、可變Action的實現(xiàn)方案择膝。DA框架的初衷是為了取代Context.startActivity的調(diào)用方式,使用建造者模式(Builder Pattern)構(gòu)建交互參數(shù)资盅,使程序更優(yōu)美调榄。DA框架能夠?qū)θ魏我粋€已經(jīng)存在的Action修改,動態(tài)改變原有的跳轉(zhuǎn)邏輯呵扛。值得一提的是每庆,DA框架不僅友好地實現(xiàn)了與H5間的跳轉(zhuǎn)交互,也解決了Activity在插件化項目的交互問題今穿。
項目地址:https://github.com/benniaobuguai/android-dynamic-action

DA的URI基本結(jié)構(gòu)

在DA框架下缤灵,Activity是一個有趣的概念實體,每一個Activity都可視作DA框架下的一種資源。對于一個客戶端而言腮出,每個Activity都是全局唯一可訪問的資源帖鸦,因此每個Activity都有統(tǒng)一資源標(biāo)識符(URI)。

URI的基本結(jié)構(gòu):

scheme://com.example.project:8888/path/etc?id=1024
\-----/  \------------------/\--/ \------/\-------/
scheme            host       port   path     query parameter
         \---------------------/
                authority

DA框架基于標(biāo)準(zhǔn)的URI胚嘲,定制了更符合Android Activity交互的URI結(jié)構(gòu)作儿。
定制后的URI基本結(jié)構(gòu):

scheme://packageId$ActionName?data={"id":"1024"}
\-----/  \------------------/\-----------------/
scheme            host         query parameter

基于以上協(xié)議,定義屬于自己的scheme馋劈,每個Activity將具有一個可被訪問的URI攻锰,你就能夠像訪問網(wǎng)頁一樣訪問Activity啦!<宋怼娶吞!

DA的配置文件

DA框架的“動態(tài)可變性”體現(xiàn)在配置文件上,DA框架遵循“約定優(yōu)于配置”的原則械姻,使用更少的配置達(dá)到目的妒蛇。配置文件非常簡單,僅包含scheme以及包名的映射關(guān)系楷拳。
配置文件示例:

<?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登錄界面來修復(fù)有BUG的原生登錄 -->
        <action name="Login" from="home_login_click"  to="0$Browser?title=H5登錄&url=http://www.opencdk.com/login.html" />
    </package>

</DynamicAction>

參數(shù)說明:

  • DA.devMode:開發(fā)模式绣夺,開關(guān)打開后,后臺將可看到更多日志
  • DA.scheme:DA框架內(nèi)部使用的scheme唯竹,建議根據(jù)自己的業(yè)務(wù)定義唯一的scheme
  • DA.appscheme:非DA框架直接使用乐导,是App提供給第三方App的scheme
  • package:包名的映射關(guān)系,文章最后有完整配置
  • package>id:包id浸颓,與包名一一對應(yīng)
  • package>name:包名
  • action:動態(tài)修復(fù)的Action
  • action>name:Action的名稱
  • action>from:事件源,觸發(fā)此事件的源頭
  • action>to:目標(biāo)地址(DA框架標(biāo)準(zhǔn)的URI數(shù)據(jù)結(jié)構(gòu))
  • action>to>title:Activity標(biāo)題
  • action>to>url:網(wǎng)址

DA的代碼實現(xiàn)

假設(shè)在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();

非常簡單的實現(xiàn)方式,為動態(tài)運營奠定了基礎(chǔ)蛾狗〗粒基于DA框架,開發(fā)同學(xué)可以輕松構(gòu)建項目規(guī)范沉桌,編寫更優(yōu)雅的代碼谢鹊;測試同學(xué)編寫測試用例也變得更容易了。

DA的數(shù)據(jù)交互

DA框架不但支持原生Activity間的數(shù)據(jù)交互留凭,而且也支持Activity與H5間的數(shù)據(jù)交互佃扼。保證數(shù)據(jù)協(xié)議的一致性,DA框架統(tǒng)一使用JSON進(jìn)行數(shù)據(jù)交互(推薦使用fastjson)蔼夜。
URI表示如下:

opencdk://1$Login?data={"username":"benniaobuguai"}

代碼調(diào)用:

new DA.Builder(Context)
    .setHost("1$Login")
    .setData("{\"username\":\"benniaobuguai\"}")
    .go();

DA支持Activity回調(diào)(Context.startActivityForResult)

URI表示如下:

opencdk://1$Login?data={"username":"benniaobuguai"}&requestCode=10000

代碼調(diào)用:

new DA.Builder(Context)
    .setHost("1$Login")
    .setData("{\"username\":\"benniaobuguai\"}")
  .setRequestCode(10000)
    .go();

PS: 建議在Activity間交互時使用事件總線兼耀,如:EventBusotto等。

DA動態(tài)修改默認(rèn)跳轉(zhuǎn)

  • 用一個Activity替換另外一個Activity
<package id="2" name="com.opencdk.da.ui.video" >
    <!--- 隨機(jī)推薦視頻列表界面修改成免費視頻列表界面 -->
    <action name="VideoRandomList" from="home_video_random_click" to="2$VideoFreeList" />
</package>
  • 用一個H5界面替換一個Activity
<package id="2" name="com.opencdk.da.ui.video" >
    <!-- 隨機(jī)推薦視頻列表界面修改成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文件不支持直接使用&瘤运、<窍霞、>等特殊字符,應(yīng)該使用其對應(yīng)的轉(zhuǎn)義字符拯坟。具體可參考:XML和HTML常用轉(zhuǎn)義字符
  2. url地址需要進(jìn)行URL編碼但金,避免特殊字符對URI的解析造成影響。在線URL編碼

DA啟用攔截器功能

往往有些時候郁季,我們需要對某些界面進(jìn)行訪問控制冷溃。

已經(jīng)發(fā)布的版本,任何用戶都可訪問VIP視頻列表巩踏。臨時需求變更秃诵,只有登錄的用戶才能訪問VIP視頻列表界面,可修改配置下發(fā):

<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框架解決的核心問題

  • 一個地址可達(dá)任意Activity
  • 任意一個Activity可被動態(tài)修改為另一個Activity
  • DA框架數(shù)據(jù)交互扁平化,使用JSON便于與H5交互
  • 任意Activity可替換成H5派近,快速修復(fù)原生突發(fā)BUG
  • 插件化項目中也能滿足以上需求

DA框架所遵循的一些約定

DA框架默認(rèn)遵循以下規(guī)則:

  • 配置優(yōu)先級>Java代碼優(yōu)先級攀唯,保證通過配置可修復(fù)代碼編碼的缺陷。
  • 查找動態(tài)配置時渴丸,先根據(jù)name+from進(jìn)行精確查找侯嘀,其次根據(jù)name去查找。

示例說明

為了更好地體現(xiàn)動態(tài)Action谱轨,示例中在assets目錄下放了多個配置文件戒幔,加載不同的配置文件就相當(dāng)于網(wǎng)絡(luò)下發(fā)了新的配置文件。

  • dynamic_action.cfg土童,默認(rèn)的配置文件诗茎,僅包含包結(jié)構(gòu)映射關(guān)系
  • dynamic_action_transfer.cfg,配置一個動態(tài)Action
  • dynamic_action_interceptor.cfg献汗,配置一個攔截器

進(jìn)階與思考

  • 客戶端與客戶端交互
    客戶端與客戶端之間的交互是不安全的敢订,對于暴露給第三方的入口都需要進(jìn)行校驗。就DA框架而言罢吃,主要是為了解決內(nèi)部跳轉(zhuǎn)的統(tǒng)一協(xié)議而確定的scheme(opencdk://)楚午,提供外部使用的scheme(opencdkexample://)。opencdk://僅供內(nèi)部使用刃麸,認(rèn)為是可信任的醒叁、安全的。opencdkexample://是外部協(xié)議,必須經(jīng)過校驗方可拉起我們的客戶端把沼。

需要提供外部入口啊易,必須要在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" />
        <!-- 定義給外部調(diào)用 的scheme -->
        <data android:scheme="opencdkexample" />
    </intent-filter>
</activity>

新建一個項目饮睬,執(zhí)行外部調(diào)用代碼片斷:

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對本協(xié)議的支持
    iOS并無包名概念租谈,如果希望使用同一個URI來跳轉(zhuǎn)至同一個界面,iOS在對URI的處理時捆愁,應(yīng)當(dāng)直接過濾包名后再使用割去。
    登錄配置如下:
opencdk://1$Login?data={"username":"benniaobuguai"}

解析【host】協(xié)議時,直接取【ActionName】昼丑,忽略前面的 "1$" 即可呻逆。

  • 對于簡單的項目,Android把所有Activity放在一個包名下(不建議這么做)菩帝,也可與iOS保持URI同一處理邏輯咖城。
    URI表示如下:
opencdk://Login?data={"username":"benniaobuguai"}

PS: DA框架當(dāng)前不支持無包名的實現(xiàn)方式。

注意點

  • 避免循環(huán)對Action進(jìn)行中轉(zhuǎn)
  • 通過反射訪問目標(biāo)Activity呼奢,ActionName的大小寫敏感
  • 避免過多的包名映射宜雀,Activity所在的包不宜過多,包越多維護(hù)成本越大握础。
  • Activity不能被混淆

其他

  • 運營的靈活性辐董,增強(qiáng)運營配置的自由度
  • 界面可替換性,任意Activity可替換成H5禀综,提供快速使用H5修復(fù)BUG的能力
  • 插件訪問簡單化简烘,宿主程序是無法直接獲取插件的Activity對象,DA框架的作用尤其明顯定枷。
  • DA框架最大的問題是全局可訪問任意Activity夸研,如何保證被訪問者的安全,業(yè)務(wù)不受到影響就顯示尤為重要了
  • DA框架與傳統(tǒng)的Context.startActivity最大的區(qū)別在于:交互協(xié)議標(biāo)準(zhǔn)化依鸥、靈活、運營能力強(qiáng)悼沈,動態(tài)修復(fù)能力強(qiáng)

Q&A:

1.為什么需要制定兩個scheme?

答:隔離

2.配置from to過多時, 性能如何?

答:配置from和to時贱迟,屬于修復(fù)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>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缚俏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌忧换,老刑警劉巖恬惯,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異亚茬,居然都是意外死亡酪耳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門刹缝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碗暗,“玉大人,你說我怎么就攤上這事梢夯⊙粤疲” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵颂砸,是天一觀的道長噪奄。 經(jīng)常有香客問我,道長沾凄,這世上最難降的妖魔是什么梗醇? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮撒蟀,結(jié)果婚禮上叙谨,老公的妹妹穿的比我還像新娘。我一直安慰自己保屯,他們只是感情好手负,可當(dāng)我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著姑尺,像睡著了一般竟终。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上切蟋,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天统捶,我揣著相機(jī)與錄音,去河邊找鬼柄粹。 笑死喘鸟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的驻右。 我是一名探鬼主播什黑,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼堪夭!你這毒婦竟也來了愕把?” 一聲冷哼從身側(cè)響起拣凹,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恨豁,沒想到半個月后嚣镜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡圣絮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年祈惶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扮匠。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡捧请,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出棒搜,到底是詐尸還是另有隱情疹蛉,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布力麸,位于F島的核電站可款,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏克蚂。R本人自食惡果不足惜闺鲸,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望埃叭。 院中可真熱鬧摸恍,春花似錦、人聲如沸赤屋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽类早。三九已至媚媒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涩僻,已是汗流浹背缭召。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留逆日,地道東北人恼琼。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像屏富,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蛙卤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,078評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理狠半,服務(wù)發(fā)現(xiàn)噩死,斷路器,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 1.什么是Activity?問的不太多,說點有深度的 四大組件之一,一般的,一個用戶交互界面對應(yīng)一個activit...
    JoonyLee閱讀 5,730評論 2 51
  • 大致所有的味道除了舌頭說好吃之外已日,還得讓思想說好吃垛耳,這件事就開始變得人情味起來,飲食文化飘千,是一門很大的學(xué)問堂鲜。 舌尖...
    羅帆文集閱讀 483評論 4 4
  • 好不好你不好
    王爺天下閱讀 137評論 0 0