Android插件化架構(gòu)設(shè)計(jì)之加載資源文件

開篇介紹

現(xiàn)在項(xiàng)目比較大 資源比較多卿吐,但是若希望動(dòng)態(tài)來加載資源文件屯阀,可以有以下幾種方式:

  1. 通過下載資源文件zip包然后解壓來加載
  2. 通過插件開發(fā)
    本文通過插件開發(fā)來實(shí)現(xiàn)加載插件中的資源文件.

程序演示

程序演示

可以打開鏈接 [效果演示](http://weibo.com/tv/v/EzNwkq0oP?fid=1034:8c610fcd0d501a61a67ddf56da6e4225"optional title")
打開后顯示2個(gè)動(dòng)畫鲫趁,上面的動(dòng)畫是加載的本地動(dòng)畫踱葛,下面的動(dòng)畫是從插件里面加載的土陪。

代碼介紹

如圖所示:


程序結(jié)構(gòu)

工程app作為宿主程序煤蹭,plugin作為插件程序笔喉,資源文件也在plugin里面,需要實(shí)現(xiàn)的是啟動(dòng)app來加載插件plugin里面的資源文件

這里實(shí)現(xiàn)思路大致畫一下硝皂,需要在主程序里面加載插件程序的資源文件常挚,我們需要拿到插件程序里面的Context, 獲取資源文件也就是AssetManager,這里需要用到j(luò)ava的反射機(jī)制


思路

這里給出核心代碼部分
PluginResources 類如下

package com.cayden.plugin;

import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.DisplayMetrics;

import java.io.File;
import java.lang.reflect.Method;

/**
 * Created by cuiran
 * Time  17/3/14 17:37
 * Email cuiran2001@163.com
 * Description
 */

public class PluginResources extends Resources {

    public PluginResources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
        super(assets, metrics, config);
    }

    public static PluginResources getPluginResources(Resources resources,AssetManager assets){

        PluginResources pluginResources=new PluginResources(assets,resources.getDisplayMetrics(),resources.getConfiguration());

        return pluginResources;
    }

    public static AssetManager getPluginAssetManager(File apk) throws ClassNotFoundException{
      //需要通過反射獲取AssetManager

       Class<?> forName=  Class.forName("android.content.res.AssetManager");

       Method[] methods= forName.getDeclaredMethods();
        for(Method method:methods){
            if(method.getName().equals("addAssetPath")){
                try{
                    AssetManager assetManager=AssetManager.class.newInstance();
                    method.invoke(assetManager,apk.getAbsolutePath());

                    return assetManager;
                }catch (InstantiationException e){
                    e.printStackTrace();
                }catch (Exception e){
                    e.printStackTrace();
                }

            }
        }
        return null;

    }
}

啟動(dòng)MainActivity類如下

package com.cayden.plugin;

import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;

import dalvik.system.DexClassLoader;


public class MainActivity extends Activity implements View.OnClickListener{
    private static final String TAG=MainActivity.class.getSimpleName();
    private ImageView imageSource,imageCloud;
    private final static String SOURCE_TAG="source";
    private final static String CLOUD_TAG="cloud";
    private final static String CLOUD_ANIM="animation1";

    private final static String PLUGIN_NAME="plugin.apk";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        initView();

    }

    private void initView(){
        imageSource=(ImageView)findViewById(R.id.imageSource);

        imageCloud=(ImageView)findViewById(R.id.imageCloud);

        imageSource.setTag(SOURCE_TAG);
        imageCloud.setTag(CLOUD_TAG);

        imageSource.setOnClickListener(this);
        imageCloud.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        if(view.getTag().equals(SOURCE_TAG)){

            handleAnim(view);

        }else{

            //插件動(dòng)畫
            //是否加載
            String fileName=PLUGIN_NAME;
            String filePath=this.getCacheDir()+ File.separator+fileName;
            String packageName="com.cayden.pluginb";
            File apkFile=new File(filePath);
            if(apkFile.exists()){
               Drawable background= view.getBackground();
                if(background instanceof AnimationDrawable){
                    //執(zhí)行動(dòng)畫
                    handleAnim(view);
                }else{
                    //執(zhí)行插件
                    try{
                        AssetManager assetManager=PluginResources.getPluginAssetManager(apkFile);
                        PluginResources resources=  PluginResources.getPluginResources(getResources(),assetManager);

                        //反射文件
                        DexClassLoader classLoader=new DexClassLoader(apkFile.getAbsolutePath(),this.getDir(fileName, Context.MODE_PRIVATE).getAbsolutePath(),null,this.getClassLoader());

                        Class<?> loadClass= classLoader.loadClass(packageName+".R$drawable");
                        Field[] fields=  loadClass.getDeclaredFields();
                        for(Field field :fields){
                            if(field.getName().equals(CLOUD_ANIM)){
                                int animId=field.getInt(R.drawable.class);
                                Drawable drawable=resources.getDrawable(animId);
                                ((ImageView) view).setBackgroundDrawable(drawable);
                                handleAnim(view);
                            }
                        }

                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }else{
                //需要從服務(wù)端下載稽物,測(cè)試就放入assets目錄下
                try{
                    InputStream is=this.getAssets().open(fileName);
                    FileOutputStream os=new FileOutputStream(filePath);
                    int len=0;
                    byte [] buffer=new byte[1024];
                    while((len=is.read(buffer))!=-1){
                        os.write(buffer,0,len);
                    }
                    os.close();
                    is.close();
                    Log.d(TAG,"file ok");
                    Toast.makeText(this,"file ok",Toast.LENGTH_SHORT).show();

                }catch (Exception e){
                    e.printStackTrace();
                }
            }



        }
    }

    private void handleAnim(View v){
        AnimationDrawable background=(AnimationDrawable)v.getBackground();
        if(background!=null){
            if(background.isRunning() ){
                background.stop();
            }else{
                background.stop();
                background.start();
            }
        }
    }

}

為了演示方便 我們把plugin.apk放在主程序app工程的assets目錄下奄毡。

最后給出項(xiàng)目的源碼地址:https://github.com/cayden/PluginProject

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市贝或,隨后出現(xiàn)的幾起案子吼过,更是在濱河造成了極大的恐慌,老刑警劉巖咪奖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盗忱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡羊赵,警方通過查閱死者的電腦和手機(jī)趟佃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人揖闸,你說我怎么就攤上這事揍堕。” “怎么了汤纸?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵衩茸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我贮泞,道長(zhǎng)楞慈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任啃擦,我火速辦了婚禮囊蓝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘令蛉。我一直安慰自己聚霜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布珠叔。 她就那樣靜靜地躺著蝎宇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪祷安。 梳的紋絲不亂的頭發(fā)上姥芥,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音汇鞭,去河邊找鬼凉唐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛霍骄,可吹牛的內(nèi)容都是我干的台囱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼腕巡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼玄坦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绘沉,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤煎楣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后车伞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體择懂,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年另玖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了困曙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片表伦。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖慷丽,靈堂內(nèi)的尸體忽然破棺而出蹦哼,到底是詐尸還是另有隱情,我是刑警寧澤要糊,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布纲熏,位于F島的核電站,受9級(jí)特大地震影響锄俄,放射性物質(zhì)發(fā)生泄漏局劲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一奶赠、第九天 我趴在偏房一處隱蔽的房頂上張望鱼填。 院中可真熱鬧,春花似錦毅戈、人聲如沸苹丸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谈跛。三九已至羊苟,卻和暖如春塑陵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜡励。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工令花, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凉倚。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓兼都,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親稽寒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扮碧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,085評(píng)論 25 707
  • WebSocket-Swift Starscream的使用 WebSocket 是 HTML5 一種新的協(xié)議。它實(shí)...
    香橙柚子閱讀 23,848評(píng)論 8 183
  • 1. 所有好好喜歡防彈的人不會(huì)討厭南俊的杏糙,他照顧成員們也和他們友好地開玩笑慎王,顯示了他是多么可靠的隊(duì)長(zhǎng)。音樂和心理方...
    Pancake613閱讀 234評(píng)論 0 0
  • 目標(biāo):不停追求(卓)宏侍、不斷翱翔(菲) 晨讀半個(gè)小時(shí)赖淤。 做飯。 中午看小說谅河。 有點(diǎn)煩咱旱,我的眼鏡又跑了确丢。 下午練習(xí)乒乓...
    逆風(fēng)追夢(mèng)人閱讀 95評(píng)論 0 0
  • 《隱藏在背后的交互設(shè)計(jì)》中提到——加載過程的關(guān)鍵可以總結(jié)為: 1.讓用戶感知產(chǎn)品正在努力為他運(yùn)作 2.讓用戶有基本...
    大鴿子Z閱讀 4,195評(píng)論 1 18