【Android开发学习笔记】【高级】【随笔】插件化——资源加载
前言
上一节我们针对插件最基本的原理进行了一个简单的demo实现,但是由于插件的Context对象被宿主所接管,因此无法加载插件程序的资源。那么如何解决这个问题捏?
有人提出这样的方案:将apk中的资源解压到某个目录下,然后通过读文件的方法进行资源加载,虽然理论上可以实现,但是实际操作起来难度很大,首先资源文件格式不一(xml、图片等),其次针对不同分辨率的手机,Android设备也会加载同名不同路径下的图片,因此这种方案实现起来难度非常大。也有人说,可以将插件资源复制一份到宿主工程中,然而如果这样做了,我们在插件更新的时候,必须宿主也得更新资源后更新,同时这样也会增加宿主工程安装包的大小,这与我们当时使用插件化来解决问题的思想不符,因此这种方案也不可使用。那么我们应该使用什么样的原理呢?
原理
通过看Android的源代码,我们发现Context下有这两个抽象的方法:

而Context对象获取资源就是靠这两个函数来完成的,而这两个抽象的方法在ContextImpl.java中实现是这个样子的:

于是我们需要一个Resources的对象,查阅代码之后,最终发现了AssetManager.java下的这个函数:

看英文注释,我们发现这个函数,我们可以传递给一个zip包或者是资源的路径,同时这个函数还是隐藏的函数,因此我们还是使用反射来,调用这个函数后就可以得到Resources的对象了。
实现
所以我们需要在宿主中实现一个 loadResources() 这样的函数,用来加载插件的资源:
package com.bryan.host; import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import dalvik.system.DexClassLoader; import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.os.Bundle;
import android.provider.MediaStore.Video; public class ProxyActivity extends Activity
{
...... /* 用来加载资源的*/
protected AssetManager mAssetManager;
protected Resources mResources = null;
protected Theme mTheme = null; .... /* 加载插件的指定activity*/
@SuppressLint("NewApi") protected void OpenAppointActivity(final String className)
{
.... try
{
loadResources();
... /* 反射 调用插件中的设置代理 */
... /* 反射告诉插件是被宿主调起的*/
.... } catch (Exception e)
{
e.printStackTrace();
}
} protected void loadResources()
{
try
{
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, mExtraDexPath);
mAssetManager = assetManager;
} catch (Exception e) {
e.printStackTrace();
}
Resources superResources = super.getResources();
mResources = new Resources(mAssetManager, superResources.getDisplayMetrics(), superResources.getConfiguration());
mTheme = mResources.newTheme();
mTheme.setTo(super.getTheme());
} /* 重写这两个加载资源的函数 */
@Override
public AssetManager getAssets()
{
if (mAssetManager != null)
{
return mAssetManager;
}
return super.getAssets();
} @Override
public Resources getResources() {
if (mResources != null)
{
return mResources;
}
return super.getResources();
} }
注意这个函数的位置,一定要放到反射插件的OnCreate之前,其次重写 getAssets() 和 getResources() 即可。
插件工程我们需要在BaseActivity中处理 getAssets() 和 getResources() 这两个函数,方法与之前类似,考虑插件自己启动还是被宿主加载的两种情况:
package com.bryan.plugin; import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup.LayoutParams; public class BaseActivity extends Activity
{
/* 宿主工程中的代理Activity*/
protected Activity mProxyActivity; /* 判断是被谁调起的,如果是宿主调起的为1 */
int Who = 0; public void setProxy(Activity proxyActivity)
{
mProxyActivity = proxyActivity;
} @Override
protected void onCreate(Bundle savedInstanceState)
{ if (savedInstanceState != null)
{
Who = savedInstanceState.getInt("Host", 0);
}
if (Who == 0)
{
super.onCreate(savedInstanceState);
mProxyActivity = this;
}
} ...... /* 重写几个重要的添加布局的类 */
...... /* 重写加载资源的方法 */
@Override
public Resources getResources() {
if (Who == 0)
{
return super.getResources();
}
else
{
return mProxyActivity.getResources();
}
} @Override
public AssetManager getAssets() {
if (Who == 0)
{
return super.getAssets();
}
else
{
return mProxyActivity.getAssets();
}
} }
Base中处理完成之后,我们在插件主Activity中尝试一下加载资源的代码:
package com.bryan.plugin; import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout; public class MainActivity extends BaseActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // 初始化处理布局
InitView();
} private void InitView()
{
View view = CreateView(mProxyActivity);
mProxyActivity.setContentView(view); System.out.println("MainActivity.InitView()" + "activity name is :"+mProxyActivity.getClass().getSimpleName());
} private View CreateView(final Context context)
{
LinearLayout linearLayout = new LinearLayout(context); linearLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
linearLayout.setBackgroundColor(Color.parseColor("#F4F4D6"));
Button button = new Button(context);
button.setText("plugin button");
linearLayout.addView(button, LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
ImageView image = new ImageView(context);
image.setImageBitmap(new BitmapDrawable( getResources().openRawResource(R.drawable.qq)).getBitmap());
linearLayout.addView(image, LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
StartActivityByProxy("com.bryan.plugin.TestActivity");
}
});
return linearLayout;
}
}
我们在插件的资源中插入一张qq的图片,资源名字命名为qq,然后在加载资源的地方加入41-43行的代码,这通过这样的方式来进行加载资源。
结果
首先运行插件工程:

其次运行宿主工程:

可以看到在宿主工程中成功加载了插件工程的资源。
看样子我们已经解决了一个很棘手的问题,还剩一个Activity生命周期函数失效的问题,下一节我们来讲这个。
【Android开发学习笔记】【高级】【随笔】插件化——资源加载的更多相关文章
- 驱动开发学习笔记. 0.07 Uboot链接地址 加载地址 和 链接脚本地址
驱动开发学习笔记. 0.07 Uboot链接地址 加载地址 和 链接脚本地址 最近重新看了乾龙_Heron的<ARM 上电启动及 Uboot 代码分析>(下简称<代码分析>) ...
- android开发学习笔记000
使用书籍:<疯狂android讲义>——李刚著,2011年7月出版 虽然现在已2014,可我挑来跳去,还是以这本书开始我的android之旅吧. “疯狂源自梦想,技术成就辉煌.” 让我这个 ...
- 【Android开发学习笔记】【高级】【随笔】插件化——Activity生命周期
前言 如同第一章我们说的,宿主程序通过 dexclassloader 将插件的类加载进来,然后通过反射去调用它的方法,这样Activity就被当成了一个普通的类来执行了,因此系统不再接管它的生命周期, ...
- android开发学习笔记系列(1)-android起航
前言 在学习安卓的过程中,我觉得非常有必要将自己所学的东西进行整理,因为每每当我知道我应该是如何去实现功能的时候,有许多细节问题我总是会遗漏,因此我也萌生了写一系列博客来描述自己学习的路线,让我的an ...
- 【转】Android开发学习笔记(一)——初识Android
对于一名程序员来说,“自顶向下”虽然是一种最普通不过的分析问题和解决问题的方式,但其却是简单且较为有效的一种.所以,将其应用到Android的学习中来,不至于将自己的冲动演变为一种盲目和不知所措. 根 ...
- Android开发学习笔记DDMS的使用
打开DDMS DDMS 的全称是Dalvik Debug Monitor Service,是 Android 开发环境中的Dalvik虚拟机调试监控服务. DDMS里面包含了:Device(设备) F ...
- 【转】Android开发学习笔记:5大布局方式详解
Android中常用的5大布局方式有以下几种: 线性布局(LinearLayout):按照垂直或者水平方向布局的组件. 帧布局(FrameLayout):组件从屏幕左上方布局组件. 表格布局(Tabl ...
- 【Android开发学习笔记之一】5大布局方式详解
Android中常用的5大布局方式有以下几种: 线性布局(LinearLayout):按照垂直或者水平方向布局的组件. 帧布局(FrameLayout):组件从屏幕左上方布局组件. 表格布局(Tabl ...
- Android开发学习笔记(二)——编译和运行原理(1)
http://www.cnblogs.com/Pickuper/archive/2011/06/14/2078969.html 接着上一篇的内容,继续从全局了解Android.在清楚了Android的 ...
随机推荐
- SU sufdmod2命令学习
- struts2总结四:Action与Form表单的交互
struts2 Action获取表单数据的方式有三种:1.通过属性驱动的方式.2.模型驱动方式.3.使用多个model对象的属性. 1.通过属性驱动式 首先在jsp里面编写form表单的代码如下: & ...
- 【Linux下进程机制】从一道面试题谈linux下fork的运行机制
今天一位朋友去一个不错的外企面试linux开发职位,面试官出了一个如下的题目: 给出如下C程序,在linux下使用gcc编译: #include "stdio.h" #includ ...
- Ada语言基础
前天是程序员节,CodeForces上举办了一场比赛,要求必须使用ADA语言提交.顿时虐爆了我这个只知道pascal,C,BASIC这3种语系的菜.今天闲着看了看这种语言,一看才知道竟然是美国军方专用 ...
- Assets/Sciprts/GameSciprt.js(97,46): BCE0044: expecting :, found ','.
function BuildDeck() { var totalRobots:int=4; var card:Object; var i:int; for(i=0;i<totalRobots;i ...
- javascript继承扩展类方法实现
javascript没有原生的继承语法,这确实很让人困惑,但是广大人民群从的智慧是无穷的.最近呢,正尝到一点从源码中学习的甜头,不分享一下实在难以平复激动的心情.前不久改造视频播放插件的时候,找到了v ...
- java定时器的使用
定时器类Timer在java.util包中.使用时,先实例化,然后使用实例的schedule(TimerTask task, long delay)方法,设定指定的任务task在指定的延迟delay后 ...
- IIS7 Appcmd.exe 使用
如果您运行的是 64 位 Windows,请从 %windir%\system32\inetsrv 目录而不是 %windir%\syswow64\inetsrv 目录中使用 Appcmd.exe. ...
- windows2003下安装apache
php建议在http://www.apachelounge.com/ 下载apache的二进制版本,因为提供了不同VC版本.不懂,照做. 因为是在虚拟机2003上面搞的,需要装VC2010版本. 下载 ...
- CodeForces 577B 模和vecto
上一年的最后一次训练赛被卡了AK... 一开始天真的认为每个数字都进行取模 然后认为它是一个vol为取模后的数 val为0的01背包 去计算dp[m]能否被装满 只想到了其中的一点..简直天真 后来看 ...