2019 android 插件化,Android 插件化开发(三):资源插件化

在前面的文章中我们成功的加载了外部的Dex(Apk)并执行了插件的Bean代码。这时我们会想,能不能加载并运行插件Apk的Activity。答案当然是能,否则后续我们的研究就没意义了,但是想实现Activity的插件化运行,我们必须要解决一个问题——如何使用插件中的资源。

本文我们就讲一下插件的资源加载机制,并讲述一下如何实现资源的插件化。

一、资源的加载机制

Android的资源文件分为两类:

第一类是res目录下存放的可编辑的资源文件,这类文件在编译时系统会自动在R文件中生成资源文件的16进制值。

例如:

public final classR {public static final classanim {public static final int abc_fade_in=0x7f050000;public static final int abc_fade_in=0x7f050000;

...

}

}

我们在平时的开发时,访问这类资源比较简单,使用Context的getResource方法即可得到res下的各种资源。如下面代码所示:

String content = mContext.getResource().getString(R.string.content);

第二类是assets目录下存放的原始资源文件。apk在编译时不会编译assets下的文件,我们不能使用R.的方式访问,只能使用AssetsManager类的open方法来获取assets目录下的文件资源。

而AssetsManager又源于Resources类的getAssets方法,如下面代码所示:

Resource resource =getResource();

AssetsManager am=getResource().getAssets();

InputStream is= getResource().getAssets().open("filename");

通过上面的分析,我们可以初步做出一个结论:我们能使用Resources类是一个很重要的类,通过此类提供的相关API,我们能操作资源的加载。

二、资源插件化的解决方案

谈及资源插件化,我们不得不对AssetsManager的API多说一些。

AssetsManager中有一个addAssetsPath(String Path)方法,App启动的时候就会将当前的apk路径传进去,接下来AssetsManager和Resources就能访问当前apk的所有资源了。

AssetsManager的addAssetsPath方法不对外,但是我们可以通过反射的方式,把插件apk的路径传到这个方法,这样就把插件的资源添加到一个资源池中了。App有几个插件,我们就调用几次addAssetsPath方法,把插件的资源都塞到池子里。

这里我们以加载插件Apk里面的字符串资源为目标,实战一下资源插件化:

首先我们在插件app的string.xml里面定义字符串资源:

Hello Plugin

然后我们在宿主app编写如下代码:

public class MainActivity extendsActivity {privateAssetManager mAssetManager;privateResources mResources;privateResources.Theme mTheme;private String dexPath = null; //apk文件地址

private File fileRelease = null; //释放目录

protected DexClassLoader classLoader = null;private String pluginName = "plugin1.apk";

TextView mTextView;

@Overrideprotected voidattachBaseContext(Context newBase) {super.attachBaseContext(newBase);

extractAssets(newBase, pluginName);

}

@SuppressLint("NewApi")

@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

File extractFile= this.getFileStreamPath(pluginName);

dexPath=extractFile.getPath();

fileRelease= getDir("dex", 0);

classLoader= new DexClassLoader(dexPath, fileRelease.getAbsolutePath(), null, getClassLoader());

mTextView=findViewById(R.id.tv);//带资源文件的调用

findViewById(R.id.btn_6).setOnClickListener(newView.OnClickListener() {

@Overridepublic voidonClick(View arg0) {

loadResources();try{

Class mLoadClassDynamic= classLoader.loadClass("com.plugin1.Dynamic");

Object dynamicObject=mLoadClassDynamic.newInstance();

IDynamic dynamic=(IDynamic) dynamicObject;

String content= dynamic.getStringForResId(MainActivity.this);

mTextView.setText(content);

Toast.makeText(getApplicationContext(), content+ "", Toast.LENGTH_LONG).show();

}catch(Exception e) {

e.printStackTrace();

}

}

});

}protected voidloadResources() {try{

AssetManager assetManager= AssetManager.class.newInstance();

Method addAssetPath= assetManager.getClass().getMethod("addAssetPath", String.class);

addAssetPath.invoke(assetManager, dexPath);

mAssetManager=assetManager;

}catch(Exception e) {

e.printStackTrace();

}

Resources superRes= super.getResources();

superRes.getDisplayMetrics();

superRes.getConfiguration();

mResources= newResources(mAssetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());

mTheme=mResources.newTheme();

mTheme.setTo(super.getTheme());

}

@OverridepublicAssetManager getAssets() {return mAssetManager == null ? super.getAssets() : mAssetManager;

}

@OverridepublicResources getResources() {return mResources == null ? super.getResources() : mResources;

}

@OverridepublicResources.Theme getTheme() {return mTheme == null ? super.getTheme() : mTheme;

}/*** 把Assets里面得文件复制到 /data/data/files 目录下

*

*@paramcontext

*@paramsourceName*/

public static voidextractAssets(Context context, String sourceName) {

AssetManager am=context.getAssets();

InputStream is= null;

FileOutputStream fos= null;try{

is=am.open(sourceName);

File extractFile=context.getFileStreamPath(sourceName);

fos= newFileOutputStream(extractFile);byte[] buffer = new byte[1024];int count = 0;while ((count = is.read(buffer)) > 0) {

fos.write(buffer,0, count);

}

fos.flush();

}catch(IOException e) {

e.printStackTrace();

}finally{

closeSilently(is);

closeSilently(fos);

}

}

private static voidcloseSilently(Closeable closeable) {if (closeable == null) {return;

}try{

closeable.close();

}catch(Throwable e) {

e.printStackTrace();

}

}

}

以上代码分为四个逻辑部分:

1).  loadResources方法。通过反射创建AssetManager对象,调用addAssetPath方法,把插件Plugin1路径添加到这个AssetManager对象中。从此这个AssetManager就只为插件Plugin1服务了。在这个AssetManager对象的基础上,创建对应的Resource和Theme对象。

2). 重写Activity的getAsset,getResource和getTheme方法。重写逻辑见上面的代码。

3). 加载外部的插件,生成这个插件对应的ClassLoader。

4). 通过反射,获取插件中的类,构造出插件类的对象,然后就可以让插件类读取插件中的资源了。

基于这个思路,我们可以尝试使用插件资源替换当前显示的内容,实现换肤效果,核心思想是一样的,这里就不过多赘述了。

原文:https://www.cnblogs.com/renhui/p/11907493.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值