動態(tài)加載系列--插件化加載資源

動態(tài)加載座泳?哇惠昔,好高大上的名字,一看到是不是就點(diǎn)膽怯了挑势?但如果你肯花點(diǎn)時間瞅一眼舰罚,你就不會這么想了。

因?yàn)樗娴牟荒敲措y薛耻。想想我們經(jīng)常碰到過JAVA中的一個經(jīng)典的設(shè)計(jì)模式:動態(tài)代理营罢。什么?沒聽說過饼齿,年輕人饲漾,你需要補(bǔ)補(bǔ)你的基礎(chǔ)知識了。

動態(tài)代理的原理不就是反射么缕溉?所以考传, 我今天講的這個動態(tài)加載,也是一樣证鸥。

首先僚楞,我們要做什么,要了解插件化的含義是什么枉层?它需要什么必要條件泉褐?簡單形容,你可以把它看成一個可拆卸重裝的活動板房鸟蜡。那么活動板房需要什么必要的存在膜赃?我們得有框架對吧?然后可以移動的門窗揉忘,那么我插件化跳座,就需要一個宿主(框架)端铛,一個插件(門窗)。插件可以隨時拆卸更新而會影響宿主的正常運(yùn)行疲眷。

插件化的目的:隨著應(yīng)用在市場使用禾蚕,業(yè)務(wù)的拓展,功能越來越多狂丝,那么代碼就也會越來越多换淆,需要效果也越來越復(fù)雜,第一存在的問題就是android應(yīng)用的65535的問題(65K)美侦,當(dāng)然你會說google提供了dex分包支持。好吧魂奥,第二個問題菠剩,發(fā)包大小呢?是不是個大問題耻煤。從一開始的幾M到幾十M甚至幾百M(fèi)具壮,你可以忍受,客戶可無法忍受哈蝇。所以我們的插件化是非常有效的解決這些問題棺妓。

好了,啰嗦這么多炮赦,我們來看看具體怎么做:
1怜跑、新建一個宿主程序,下面是主要代碼:
1.1 選擇皮膚(一個未安裝的apk文件):為了簡單吠勘,我直接放在sdcard下面了性芬,實(shí)際上業(yè)務(wù)我們需要放在服務(wù)端然后下載到本地。

 private void switchSkin() {
        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "skin.apk";
        String pkgName = getSkinApk(this, path);
        loadSkinApk(path, pkgName);
 }

1.2 根據(jù)apk的路徑獲取包名

private String getSkinApk(Context context, String apkFilePath) {
        PackageManager pm = context.getPackageManager();
        PackageInfo packageInfo = pm.getPackageArchiveInfo(apkFilePath, PackageManager.GET_ACTIVITIES);
        if (packageInfo != null) {
            ApplicationInfo appInfo = packageInfo.applicationInfo;
            return appInfo.packageName;
        }
        return "";
 }

1.3 加載方法剧防,通過resouces本身的getIdentifier可以植锉。

private void loadSkinApk(String apkFilePath, String apkPackageName) {
        Resources resources = createResources(apkFilePath);
        Drawable drawable = resources.getDrawable(resources.getIdentifier("skin", "drawable", apkPackageName));
        findViewById(R.id.main).setBackground(drawable);
 }

我這里還介紹另外一種方法,即DexClassLoader 插隊(duì)方法也可以實(shí)現(xiàn):

private void loadSkinApk(String apkFilePath, String apkPackageName) {
        Resources resources = createResources(apkFilePath);
        int id = getSkinId(apkFilePath, "skin", apkPackageName);
        findViewById(R.id.main).setBackground(resources.getDrawable(id));
 }

 private int getSkinId(String apkPath, String skinName, String apkPackageName) {
        int id = 0;
        try {
            /**使用DexClassLoader可以加載未安裝的apk中的dex*/
            DexClassLoader dexClassLoader = new DexClassLoader(apkPath, this.getDir("dex", 
             Context.MODE_PRIVATE).getAbsolutePath(), null, this.getClassLoader());
            //通過使用apk自己的類加載器峭拘,反射出R類中相應(yīng)的內(nèi)部類進(jìn)而獲取我們需要的資源id
            Class<?> clazz = dexClassLoader.loadClass(apkPackageName + ".R$drawable");
            Field field = null;
            try {
                field = clazz.getDeclaredField(skinName);
                //得到圖片id
                id = field.getInt(R.id.class);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return id;
    }

1.4 最關(guān)鍵的兩個方法

 //這個Resources就可以加載非宿主apk中的資源
 private Resources createResources(String pFilePath) {
        final AssetManager assetManager = createAssetManager(pFilePath);
        Resources superRes = this.getResources();
        return new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
  }

    //通過反射的方式拿到assetManager俊庇。
    private AssetManager createAssetManager(String pFilePath) {
        try {
            final AssetManager assetManager = AssetManager.class.newInstance();
            AssetManager.class.getDeclaredMethod("addAssetPath", String.class).invoke(
                    assetManager, pFilePath);
            return assetManager;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

2、新建一個插件程序,命名為skin.apk放到測試的手機(jī)sdcard中鸡挠。
3辉饱、效果圖:


換膚.gif

最后小提示,記得加權(quán)限<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拣展,一起剝皮案震驚了整個濱河市鞋囊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瞎惫,老刑警劉巖溜腐,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件译株,死亡現(xiàn)場離奇詭異,居然都是意外死亡挺益,警方通過查閱死者的電腦和手機(jī)歉糜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來望众,“玉大人匪补,你說我怎么就攤上這事±煤玻” “怎么了夯缺?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長甘耿。 經(jīng)常有香客問我踊兜,道長,這世上最難降的妖魔是什么佳恬? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任捏境,我火速辦了婚禮,結(jié)果婚禮上毁葱,老公的妹妹穿的比我還像新娘垫言。我一直安慰自己,他們只是感情好倾剿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布筷频。 她就那樣靜靜地躺著,像睡著了一般前痘。 火紅的嫁衣襯著肌膚如雪截驮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天际度,我揣著相機(jī)與錄音葵袭,去河邊找鬼。 笑死乖菱,一個胖子當(dāng)著我的面吹牛坡锡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播窒所,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼鹉勒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吵取?” 一聲冷哼從身側(cè)響起禽额,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后脯倒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體实辑,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年藻丢,在試婚紗的時候發(fā)現(xiàn)自己被綠了剪撬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡悠反,死狀恐怖残黑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斋否,我是刑警寧澤梨水,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站茵臭,受9級特大地震影響疫诽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜笼恰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一踊沸、第九天 我趴在偏房一處隱蔽的房頂上張望歇终。 院中可真熱鬧社证,春花似錦、人聲如沸评凝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奕短。三九已至宜肉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間翎碑,已是汗流浹背谬返。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留日杈,地道東北人遣铝。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像莉擒,于是被迫代替她去往敵國和親酿炸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

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