Android应用程序插件化研究之AssetManager

转载请注明:Android开发中文站 » Android应用程序插件化研究之AssetManager

最近在研究Android应用的插件化开发,看了好几个相关的开源项目。插件化都是在解决以下几个问题:

就这几个问题,我开始研究插件化开发实现的相关技术。在上篇文章中我讲了如何把插件apk中的class加载到当前进程的问题,本篇文章主要讲第一点的第二点:如何加载另一个apk中的资源到当前应用中。

AssetManager介绍

当我们在组件中获取资源时使用getResource获得Resource对象,通过这个对象我们可以访问相关资源,比如文本、图片、颜色等。通过跟踪源码发现,其实 getResource 方法是Context的一个抽象方法, getResource的实现是在ContextImp中实现的。获取的Resource对象是应用的全局变量,然后继续跟踪源码,发现 Resource 中有一个AssertManager的全局变量,在Resource的构造函数中传入的,所以最终获取资源都是通过 AssertManager 获取的,于是我们把注意力放到AssertManager上。我们要解决下面两个问题。

一、如何获取 AssertManager 对象。

二、如何通过 AssertManager 对象获取插件中apk的资源。

通过对 AssertManager 的相关源码跟踪,我们找到答案。

一、AssertManager 的构造函数没有对 api 公开,不能使用 new 创建;context .getAssets() 可用获取当前上下文环境的 AssertManager;利用反射 AssetManager.class.newInstance() 这样可用获取对象。

二、如何获取插件 apk 中的资源。我们发现 AssetManager 中有个重要的方法。

/** Add an additional set of assets to the asset manager. * This can be either a directory or ZIP file.
* Not for use by applications. Returns the cookie of the added asset, * or 0 on failure.
*@{hide}
*/
public native final int addAssetPath(String path);

我们可以把一个包含资源的文件包添加到assets中。这就是AssertManager查找资源的第一个路径。这个方法是一个隐藏方法,我们可以通过反射调用。

AssetManager assetManager = AssetManager.class.newInstance() ; // context .getAssets()?
AssetManager.class.getDeclaredMethod("addAssetPath", String.class).invoke(assetManager, apkPath);

上文提到,我们可以获取当前上下文的 AssetManager,也可以通过反射创建一个 AssetManager。我们这里本文分析的是后一种。第一种能不能呢,这个问题留给读者先去思考,后续文章会单独讨论。 详细了解可以参考老罗的文章《Android应用程序资源管理器(Asset Manager)的创建过程分析》。下面我们来写一个demo:获取插件的文本资源和图片资源。

创建一个插件apk工程module:apkbeloaded

我把插件的包名命名为:laodresource.demo.com.loadresourcedemo。

第一步:

这个工程中我们可以不用写任何代码。在drawable目录下放一张图片:icon_be_load.png。

在string中定义字符串:<string name=”text_beload”>I am from apkBeLoaded</string>。

第二步:

打包生成apk:apkbeloaded-debug.apk。

拷贝到测试机文件路径下:$ adb push <你的路径>/apkbeloaded-debug.apk sdcard/

创建一个宿主apk工程

在布局文件中定义一个文本控件和一个图片控件,分别用来显示插件中获取的文本和图片。

1
2
3
4
5
6
7
8
9
10
11
12
< ImageView android:layout_width = "wrap_content"
            android:src = "@mipmap/ic_launcher"
            android:id = "@+id/icon"
            android:layout_height = "wrap_content" />
< TextView
     android:layout_width = "wrap_content"
     android:layout_height = "wrap_content"
     android:id = "@+id/text"
     android:text = "Hello World!"
     android:layout_below = "@+id/icon"
     android:layout_centerInParent = "true"
     />

MainActivity.java

onCrease:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Override
protected void onCreate(Bundle savedInstanceState) {
     super .onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     ImageView imageView = (ImageView) findViewById(R.id.icon);
     TextView textView = (TextView) findViewById(R.id.text);
 
     /**
      *  插件apk路径
      */
     String apkPath = Environment.getExternalStorageDirectory()+ "/apkbeloaded-debug.apk" ;
     /**
      *  插件资源对象
      */
     Resources resources = getBundleResource( this ,apkPath);
     /**
      *获取图片资源
      */
     Drawable drawable = resources.getDrawable(resources.getIdentifier( "icon_be_load" "drawable" ,
             "laodresource.demo.com.apkbeloaded" ));
     /**
      *  获取文本资源
      */
     String text = resources.getString(resources.getIdentifier( "text_beload" , "string" ,
             "laodresource.demo.com.apkbeloaded" ));
 
     imageView.setImageDrawable(drawable);
     textView.setText(text);
 
}

创建Resource对象:

1
2
3
4
public Resources getBundleResource(Context context, String apkPath){
     AssetManager assetManager = createAssetManager(apkPath);
     return new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
}

创建AssetManager对象:

1
2
3
4
5
6
7
8
9
10
11
private AssetManager createAssetManager(String apkPath) {
     try {
         AssetManager assetManager = AssetManager. class .newInstance();
         AssetManager. class .getDeclaredMethod( "addAssetPath" , String. class ).invoke(
                 assetManager, apkPath);
         return assetManager;
     catch (Throwable th) {
         th.printStackTrace();
     }
     return null ;
}

Demo源码:https://github.com/liuguangli/LoadResourceDemo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值