【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的 ...
随机推荐
- iOS Simulator功能介绍关于Xamarin IOS开发
iOS Simulator功能介绍关于Xamarin IOS开发 iOS Simulator功能介绍 在图1.38所示的运行效果中,所见到的类似于手机的模型就是iOS Simulator.在没有iPh ...
- 贪心 Codeforces Round #300 A Cutting Banner
题目传送门 /* 贪心水题:首先,最少的个数为n最大的一位数字mx,因为需要用1累加得到mx, 接下来mx次循环,若是0,输出0:若是1,输出1,s[j]--: 注意:之前的0的要忽略 */ #inc ...
- POJ2699 The Maximum Number of Strong Kings(最大流)
枚举所有Strong King的状态(最多1024种左右),然后判断是否合法. 判定合法用网络流,源点-比赛-人-汇点,这样连边. 源点向每场比赛连容量为1的边: 如果一场比赛,A和B,A是Stron ...
- Sql不区分大小写查询
select a.* from Pair_User a where 1=1 and UPPER(a.UserID) like 'EMH1001%' collate Chinese_PRC_C ...
- How far away[HDU2586]
How far away ? Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T ...
- 【BZOJ】2209: [Jsoi2011]括号序列(splay)
http://www.lydsy.com/JudgeOnline/problem.php?id=2209 splay又犯逗........upd1那里的sum忘记赋值反............. 本题 ...
- BZOJ3994: [SDOI2015]约数个数和
Description 设d(x)为x的约数个数,给定N.M,求 Input 输入文件包含多组测试数据. 第一行,一个整数T,表示测试数据的组数. 接下来的T行,每行两个整数N.M. O ...
- Sublime Text 2的快速入门和常用技巧
1. 安装扩展器包管理器Package Control组件 点击菜单 View -> Show Console 调出控制台或者按快捷键 “Ctrl + `”(1左边的符号,可能和QQ拼音输入法和 ...
- Matlab Delete Row or Col 删除矩阵的行或列
Matlab中,我们有时候要删除矩阵中的某行某列,可以采用下列方法进行删除: a = [ ]; a(,:) = []; % Delete row a(:,) = []; % Delete col
- javascript 数据类型 变量 类型转换运算符
数据类型: 1.字符串(被双引号所包含的内容),小数,整数,日期时间,布尔型等. 2.变量: 都是通用类型的var, 定义一个变量格式:var a: 3.类型转换: 分为自动转换和强制转换,一般 ...