<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android-Plugin-FrameworkAndroid 插件框架

          聯(lián)合創(chuàng)作 · 2023-09-25 03:58

          此項(xiàng)目是Android插件框架完整源碼以及實(shí)例。用來(lái)開發(fā)Android插件APK,并通過動(dòng)態(tài)加載的方式在宿主程序中運(yùn)行。

          若插件APK是完全獨(dú)立的APK,那么插件apk也可獨(dú)立安裝運(yùn)行。 若插件APK不是完全獨(dú)立的apk,比如和插件宿主程序共用一些依賴庫(kù),那么插件apk只能在宿主程序中運(yùn)行。不可獨(dú)立運(yùn)行。 因?yàn)榇藭r(shí)插件apk的代碼是不完整的。

          目錄結(jié)構(gòu)說明:

          PluginCore工程是插件庫(kù)核心工程。用于提供對(duì)插件功能的支持。

          PluginMain是用來(lái)測(cè)試的插件宿主程序Demo工程。

          PluginShareLib是用來(lái)測(cè)試的插件宿主程序的依賴庫(kù)Demo工程

          PluginTest是用來(lái)測(cè)試的插件Demo工程。此工程下有用來(lái)編譯插件的ant腳本。

          宿主程序工程可以通過ant編譯或者導(dǎo)入eclipse后直接點(diǎn)擊Run菜單進(jìn)行安裝。

          插件Demo工程需要通過插件ant腳本編譯。編譯命令為 “ant clean debug” 原因是Demo中引用了宿主程序的依賴庫(kù)。需要在編譯時(shí)對(duì)共享庫(kù)進(jìn)行排除。 插件編譯出來(lái)以后,可以將插件復(fù)制到sdcard,然后在宿主程序中調(diào)用PluginLoader.installPlugin("插件apk絕對(duì)路徑")進(jìn)行安裝

          還有一種簡(jiǎn)易的安裝方式,是使用編譯命令為 “ant clean debug install” 直接將插件apk安裝到系統(tǒng)中,PluginMain工程會(huì)監(jiān)聽系統(tǒng)的應(yīng)用安裝廣播,監(jiān)聽到插件apk安裝廣播后, 再自動(dòng)調(diào)用PluginLoader.installPlugin("/data/app/插件apk文件.apk")進(jìn)行插件安裝。免去復(fù)制到sdcard的過程。

          (雖然沒有用過apkplug、以及另外一個(gè)插件框架作者singwhatiwanna寫的DL框架,但是看過他們的一些介紹文檔,感覺自己的這份實(shí)現(xiàn)應(yīng)該是更簡(jiǎn)單易用更完善。。。哈哈,是不是有王婆賣瓜的嫌疑。)

          已支持的功能:

          1、插件apk無(wú)需安裝,由宿主程序動(dòng)態(tài)加載運(yùn)行。

          2、插件形式支持fragment和activity代理。 這兩種形式是插件開發(fā)中的兩種主要形式。

          3、插件支持activity非代理模式,真正實(shí)現(xiàn)Activity無(wú)需注冊(cè),既不用反射,也不用代理,實(shí)現(xiàn)Activity完整生命周期。

          4、插件庫(kù)apk可無(wú)任何特殊代碼。如插件中的fragment,activity等無(wú)需繼承任何特定基類或接口。完全和普通app代碼沒有區(qū)別.

          5、支持插件共用宿主程序依賴庫(kù)提供的自定義控件

          6、支持插件中使用自定義控件

          7、支持插件資源和宿主資源混合使用。意即支持如下場(chǎng)景:

          插件程序和宿主程序共用依賴庫(kù)時(shí)插件中的布局文件中使用了宿主程序中的自定義控件,而宿主程序中的自定義控件中又使用
           了宿主程序中的布局文件。
           插件代碼中無(wú)需做任何特殊處理,即可支持這種布局文件。

          8、插件中的各種資源、布局、R、以及宿主程序中的各種資源、布局、R等可隨意使用、也無(wú)任何特殊代碼。

          10、支持插件使用宿主程序主題和系統(tǒng)主題

          11、解決資源id沖突問題。嘗試做過插件開發(fā)的同學(xué)應(yīng)該都遇到,插件資源id和宿主程序資源id可能相同,導(dǎo)致獲取的資源不是想要的資源。 此問題其實(shí)在android提供的編譯器aapt中早已提供了支持。

          12、需要關(guān)注PluginTest工程的ant.properties文件和project.properties文件以及custom_rules.xml文件,插件使用宿主程序共享庫(kù),以及共享庫(kù)R引用,和編譯時(shí)排除的功能,都在這3個(gè)配置文件中體現(xiàn)

          暫不支持的功能:

          1、暫不支持使用插件中自定義主題,

          2、支持在插件中通過R文件使用宿主程序中的資源,暫不支持插件資源文件中直接使用宿主程序中的資源。但是支持間接使用。 例如在上述“已支持的功能”6中描述的,實(shí)際就是間接使用。

          后續(xù)需要解決的問題:

          1、使支持插件自定義主題

          2、使插件中對(duì)activity的支持更穩(wěn)定。

          由于此框架沒有實(shí)際的項(xiàng)目應(yīng)用,目前對(duì)activity的提供標(biāo)準(zhǔn)API的測(cè)試還不夠完全,可能在其他開發(fā)場(chǎng)景中,activity的部分標(biāo)準(zhǔn)API可能會(huì)出現(xiàn)問題。畢竟這里使用了很多反射,會(huì)涉及到多機(jī)型多系統(tǒng)版本的兼容問題。后續(xù)還需要持續(xù)測(cè)試和完善
          
          
           上述第2點(diǎn)問題已解決、請(qǐng)看已支持的功能第3條。

          3、使支持插件資源文件中直接引用依賴庫(kù)中的資源。目測(cè)可能需要重寫android自帶的aapt程序。

          實(shí)現(xiàn)原理簡(jiǎn)介:

          1、插件apk的class

          通過構(gòu)造插件apk的Dexclassloader來(lái)加載插件apk中的類。DexClassLoader的parent設(shè)置為宿主程序的classloader,即可將主程序和插件程序的class貫通

          2、插件apk的資源

          通過構(gòu)造插件apk的AssetManager和Resouce類即可。
           本項(xiàng)目最關(guān)鍵一點(diǎn)功能、也是和網(wǎng)上其他插件項(xiàng)目不同的地方之一,在于
           通過addAssetsPath方法添加資源的時(shí)候,同時(shí)添加了插件程序的資源文件和宿主程序的資源。這樣就     可以做到插件資源合并。很多資源文件都迎刃而解。

          3、插件apk中的資源id

          完成上述第二點(diǎn)以后,還有需要解決的難題,是宿主程序資源id和插件程序id重復(fù)的問題。
          這個(gè)問題解決辦法也很簡(jiǎn)單
          我們知道,資源id是在編譯時(shí)生成的,其生成的規(guī)則是0xPPTTNNNN
          PP段,是用來(lái)標(biāo)記apk的,默認(rèn)情況下系統(tǒng)資源PP是01,應(yīng)用程序的PP是07
          TT段,是用來(lái)標(biāo)記資源類型的,比如圖標(biāo)、布局等,相同的類型TT值相同,但是同一個(gè)TT值不代表同一種資源,例如這次編譯的時(shí)候可能使用03作為layout的TT,那下次編譯的時(shí)候可能會(huì)使用06作為TT的值,具體使用那個(gè)值,實(shí)際上和當(dāng)前APP使用的資源類型的個(gè)數(shù)是相關(guān)聯(lián)的。
          NNNN則是某種資源類型的資源id,默認(rèn)從1開始,依次累加。
          
          那么我們要解決資源id問題,就可從TT的值開始入手,只要將每次編譯時(shí)的TT值固定,即可是資源id達(dá)到分組的效果,從而避免重復(fù)。
          例如將宿主程序的layout資源的TT固定為03,將插件程序資源的layout的TT值固定為23,即可解決資源id重復(fù)的問題了。
          
          固定資源idTT值的版本也非常簡(jiǎn)單,提供一份public.xml,在public.xml中指定什么資源類型以什么TT值開頭即可。
          具體public.xml如何編寫,可參考PluginMain/res/values/public.xml 以及 PluginTest/res/values/public.xml倆個(gè)文件,它們是分別用來(lái)固定宿主程序和插件程序資源id的范圍的。

          4、插件apk的Context

          構(gòu)造一個(gè)Context即可,具體的Context實(shí)現(xiàn)請(qǐng)參考PluginCore/src/com/plugin/core/PluginContextTheme.java
          關(guān)鍵是要重寫幾個(gè)獲取資源、主題的方法,以及重寫getClassLoader方法

          5、插件中的LayoutInfalter

          通過第4步構(gòu)造出來(lái)的Context獲取LayoutInfater即可

          6、如何實(shí)現(xiàn)插件代碼不依賴任何特殊代碼,如繼承特定的基類、接口等。

          要做到這一點(diǎn),最主要的是實(shí)現(xiàn)上述第4步,構(gòu)造出插件的Context后,所有問題都可以得到解決。

          7、插件中Activity無(wú)需注冊(cè),擁有完整生命周期是如何實(shí)現(xiàn)的。

          眾所周知、Activity是系統(tǒng)組件,必須在manifest中注冊(cè)才能被系統(tǒng)喚起并擁有完整生命周期,通過Activity代理和放射的方式實(shí)現(xiàn)的
          實(shí)際是偽生命周期。并非完整生命周期。那么如果才能實(shí)現(xiàn)activity免注冊(cè),而且擁有完整的生命周期呢,這要從activity的啟動(dòng)流程中
          入手。
          
          App安裝時(shí),系統(tǒng)會(huì)掃描app的Manifest并緩存到一個(gè)xml中,activity啟動(dòng)時(shí),系統(tǒng)會(huì)現(xiàn)在查找緩存的xml,如果查到了,再通過classLoad去load這個(gè)class,并構(gòu)造一個(gè)activity實(shí)例。那么我們只需要將classload加載這個(gè)class的時(shí)候做一個(gè)簡(jiǎn)單的映射,讓系統(tǒng)以為加載的是A class,而實(shí)際上加載的是B class,達(dá)到掛羊頭買狗肉的效果,即可將預(yù)注冊(cè)的Aclass替換為未注冊(cè)的activity,從而實(shí)現(xiàn)插件中的Activity
          完全被系統(tǒng)接管,而擁有完整生命周期。
          
          在PluginMain和PluginTest已經(jīng)添加了這種實(shí)現(xiàn)方式的測(cè)試實(shí)例。

          8、通過activity代理方式實(shí)現(xiàn)加載插件中的activity是如何實(shí)現(xiàn)的

          要實(shí)現(xiàn)這一點(diǎn),同樣是基于上述第4點(diǎn),構(gòu)造出插件的Context后,通過attachBaseContext的方式,替換代理Activiyt的context即可。
           另外還需要在獲得插件Activity對(duì)象后,通過反射給Activity的attach()方法中attach的成員變量賦值。
           這樣可解決另外一個(gè)插件框架作者singwhatiwanna實(shí)現(xiàn)的代碼中所謂this和that的問題。也是可以使插件Activity不需要繼承任何特定基類或者接口的原因。

          9、activity代理實(shí)現(xiàn)后,其他組件,如service等,如法炮制即可。

          10、插件編譯問題。

          如果插件和宿主共享依賴庫(kù),那邊編譯插件的時(shí)候不可將共享庫(kù)編譯到插件當(dāng)中,包括共享庫(kù)的代碼以及R文件,但是需要在編譯時(shí)添加到classpath中,且插件中如果要使用共享依賴庫(kù)中的資源,需要使用共享庫(kù)的R文件來(lái)進(jìn)行引用。這幾點(diǎn)在PluginTest示例工程中有體現(xiàn)。

          11、插件開發(fā)模式 插件UI可通過fragment或者activity來(lái)實(shí)現(xiàn) 如果是activity實(shí)現(xiàn)的插件,則最終會(huì)在PluginProxyActivity中運(yùn)行

          如果是fragment實(shí)現(xiàn)的插件,又分為兩種
          1種是fragment運(yùn)行在任意支持fragment的activity中,這種方式,在開發(fā)fragment的時(shí)候,fragmeng中凡是要使用context的地方,都需要使用通過PluginLoader.getPluginContext()獲取的context,那么這種fragment對(duì)其運(yùn)行容器沒有特殊要求
          
          還有1種是,fragment運(yùn)行在PluginCore提供的PluginSpecDisplayer中,這種方式,由于其運(yùn)行容器PluginSpecDisplayer的Context已經(jīng)被PluginLoader.getPluginContext獲取的context替換,因此這種fragment的代碼和普通非插件開發(fā)時(shí)開發(fā)的fragment的代碼沒有任何區(qū)別。

          需要注意的問題

          項(xiàng)目插件開發(fā)后,特別需要注意的是宿主程序混淆問題。宿主程序混淆后,可能會(huì)導(dǎo)致插件程序運(yùn)行時(shí)出現(xiàn)classnotfound

          瀏覽 26
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          編輯 分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          編輯 分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  秘 看片黄全部免费 | 午夜精品久久久99热蜜桃的推荐系统 | 丁香狠狠色婷婷久久 | 久久最新网址 | 美国一级av |