Android外掛程式化(三)載入外掛程式apk中的Resource資源

來源:互聯網
上載者:User

Android外掛程式化(三)載入外掛程式apk中的Resource資源
如何載入未安裝apk中的資源檔呢?我們從android.content.res.AssetManager.java的源碼中發現,它有一個私人方法addAssetPath,只需要將apk的路徑作為參數傳入,我們就可以獲得對應的AssetsManager對象,然後我們就可以使用AssetsManager對象,建立一個Resources對象,然後就可以從Resource對象中訪問apk中的資源了。總結如下:
1.建立一個AssetManager對象 2.通過反射調用addAssetPath方法 3.以AssetsManager對象為參數,建立Resources對象即可。

代碼如下:

package net.mobctrl.hostapk;import java.io.File;import android.content.Context;import android.content.res.AssetManager;import android.content.res.Resources;/** * @Author Zheng Haibo * @PersonalWebsite http://www.mobctrl.net * @version $Id: LoaderResManager.java, v 0.1 2015年12月11日 下午7:58:59 mochuan.zhb *          Exp $ * @Description 動態載入資源的管理器 */public class BundlerResourceLoader {    private static AssetManager createAssetManager(String apkPath) {        try {            AssetManager assetManager = AssetManager.class.newInstance();            try {                AssetManager.class.getDeclaredMethod("addAssetPath", String.class).invoke(                        assetManager, apkPath);            } catch (Throwable th) {                System.out.println("debug:createAssetManager :"+th.getMessage());                th.printStackTrace();            }            return assetManager;        } catch (Throwable th) {            System.out.println("debug:createAssetManager :"+th.getMessage());            th.printStackTrace();        }        return null;    }    /**     * 擷取Bundle中的資源     * @param context     * @param apkPath     * @return     */    public static Resources getBundleResource(Context context){        AssetsManager.copyAllAssetsApk(context);        File dir = context.getDir(AssetsManager.APK_DIR, Context.MODE_PRIVATE);        String apkPath = dir.getAbsolutePath()+"/BundleApk.apk";        System.out.println("debug:apkPath = "+apkPath+",exists="+(new File(apkPath).exists()));        AssetManager assetManager = createAssetManager(apkPath);        return new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());    }}
DEMO

注意:我們使用Resources對象,擷取資源時,傳遞的ID必須是離線apk中R檔案對應的資源的ID。如果使用getIdentifier方法,第一個參數是資源名稱,第二個參數是資源類型,第三個參數是離線apk的包名,切記第三個參數。

Resources resources = BundlerResourceLoader.getBundleResource(getApplicationContext());        imageView = (ImageView)findViewById(R.id.image_view_iv);        if(resources != null){            String str = resources.getString(resources.getIdentifier("test_str", "string", "net.mobctrl.normal.apk"));            String strById = resources.getString(0x7f050001);//注意,id參照Bundle apk中的R檔案            System.out.println("debug:"+str);            Toast.makeText(getApplicationContext(),strById, Toast.LENGTH_SHORT).show();            Drawable drawable = resources.getDrawable(0x7f020000);//注意,id參照Bundle apk中的R檔案            imageView.setImageDrawable(drawable);        }

上述代碼是載入離線apk中的字串和Drawable資源,那麼layout資源呢?

問題引入

我們使用LayoutInflate對象,一般使用方法如下:

View view = LayoutInflater.from(context).inflate(R.layout.main_fragment, null);

其中,R.layout.main_fragment我們可以通過上述方法擷取其ID,那麼關鍵的一步就是如何產生一個context?直接傳入當前的context是不行的。
解決方案有2個:
- 1.建立一個自己的ContextImpl,Override其方法。
- 2.通過反射,直接替換當前context的mResources私人成員變數。<>br
當然,我們是使用第二種方案:

    @Override    protected void attachBaseContext(Context context) {        replaceContextResources(context);        super.attachBaseContext(context);    }    /**     * 使用反射的方式,使用Bundle的Resource對象,替換Context的mResources對象     * @param context     */    public void replaceContextResources(Context context){        try {            Field field = context.getClass().getDeclaredField("mResources");            field.setAccessible(true);            field.set(context, mBundleResources);            System.out.println("debug:repalceResources succ");        } catch (Exception e) {            System.out.println("debug:repalceResources error");            e.printStackTrace();        }    }

我們在Activity的attachBaseContext方法中,對Context的mResources進行替換,這樣,我們就可以載入離線apk中的布局了。

資源檔的打包過程

如果想要做到外掛程式化,需要瞭解Android資源檔的打包過程,這樣可以為每一個外掛程式進行編號,然後按照規則產生R檔案。例如,以攜程DynamicAPK為例,它將外掛程式的R檔案按照如下規則:

1.R檔案為int型,前8位代表外掛程式的Id,其中兩個特殊的Id:Host是0x7f,android系統內建的是以0x01開頭. 2.緊跟著的8位是區分資源類型的,比如layout,id,string,dimen等 3.後面16位是資源的編號

按照上述規則產生對應的外掛程式apk。然後在運行時,我們可以寫一個ResourceManager類,它繼承自Resource對象,然後所有的Activity,都將其context的mResource成員變數修改為ResourceManager類,然後Override其方法,然後在載入資源時,根據不同的id的首碼,尋找對應外掛程式的Resource即可。也就是說,用一個類做分發。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.