博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android插件化(三)载入插件apk中的Resource资源
阅读量:7229 次
发布时间:2019-06-29

本文共 4784 字,大约阅读时间需要 15 分钟。

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就可以。也就是说,用一个类做分发。

Android插件化相关资料

  • 1.Android动态载入基础 ClassLoader工作机制
  • 2.Android动态载入黑科技 动态创建Activity模式
  • 3.Android插件化框架Github总结
  • 4.携程动态载入框架源代码
  • 5.携程Android App插件化和动态载入实践
  • 6.dex分包变形记
  • 7.各大热补丁方案分析和比較
  • 8.Android App 线上热修复方案
  • 9.Android 热补丁动态修复框架小结
  • 10.Android热更新实现原理
  • 11.【新技能get】让App像Web一样公布新版本号
  • 12.Android动态载入技术 系列索引
  • 13.Android对第三方类库执行时载入
  • 14.关于Android怎样动态载入res
  • 15.Android应用程序资源的编译和打包过程分析
  • 16.Android应用程序资源管理器(Asset Manager)的创建过程分析
  • 17.Android 自己主动编译、打包生成apk文件 1 - 命令行方式

转载地址:http://qndfm.baihongyu.com/

你可能感兴趣的文章
终于找到解决方案了,Qt的Model/View Framework解析
查看>>
线程信息的获取和设置
查看>>
Databricks Scala 编程风格指南
查看>>
Tkinter,label内容随多选框变化
查看>>
PHP开发中的数据类型 ( 第3篇 ) :Heaps
查看>>
网络七层协议
查看>>
4种删除Word空白页的小技巧,都是你需要用到的!
查看>>
单服务器MySQL主从复制实践
查看>>
CentOS 7 root口令恢复
查看>>
| 刘知远:让计算机听懂人话
查看>>
苹果收购初创公司Tueo Health,哮喘监测或将应用到Apple Watch
查看>>
CLR存储过程
查看>>
初级运维(一)
查看>>
C语言字符串常用函数学习(一)
查看>>
Lync Server 2010部署与应用(三)---拓扑生成与发布
查看>>
安全摘记1:关于安全与黑客
查看>>
我的友情链接
查看>>
tbox中vector容器的使用
查看>>
一个简单的PHP笔试题
查看>>
firebug重新载入页面获取源码
查看>>