转载请注明本文出自maplejaw的博客(http://blog.youkuaiyun.com/maplejaw_)
【Android插件化探索(一)类加载器DexClassLoader】
【Android插件化探索(二)资源加载】
【 Android插件化探索(三)免安装运行Activity(上)】
在上一篇中,我们介绍了两种免安装启动Activity的方法。但是那两种方法都有缺陷,必须在AndroidManifest.xml中注册。那么今天,我们来探索其它几种不需要在清单文件中注册的启动方式。
静态代理启动activity
通过前几篇的探索我们知道,通过DexClassLoader可以加载类,通过AsserManager可以加载资源。但是Activity确有一个令人苦恼的问题——生命周期。
我们知道宿主中的Activity都有生命周期,那我们可不可以借助宿主Activity来借尸还魂?
首先我们在宿主中定义一个Activity,取名为ProxyActivity,并且在宿主清单中注册用来占坑。这个时候ProxyActivity是有生命周期的,这点毋容置疑。那么我们现在只需把插件中的Activity当做一个普通的类反射调用即可,既然是一个普通类那么setContentView,findViewById自然也没有效果了,所以需要调用ProxyActivity的setContentView。也就是说,其实我们每次启动的Activity是ProxyActivity,布局也是加载到ProxyActivity,findViewById也是从ProxyActivity中寻找,然后在ProxyActivity的各个生命周期被调用的时候反射调用插件中的相应方法。
说的可能有点抽象,还是直接上代码吧,ProxyActivity的代码如下。
public class ProxyActivity extends Activity {
public static final String EXTRA_DEX_PATH = "extra_dex_path";
public static final String EXTRA_ACTIVITY_NAME = "extra_activity_name";
private String mClass;
private String mDexPath;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取插件dex路径
mDexPath = getIntent().getStringExtra(EXTRA_DEX_PATH);
//要启动的Activity的完整类名
mClass = getIntent().getStringExtra(EXTRA_ACTIVITY_NAME);
//加载资源
loadResources(mDexPath);
//启动插件Activity
performLaunchActivity(savedInstanceState);
}
protected void performLaunchActivity(Bundle savedInstanceState) {
File dexOutputDir = this.getDir("dex", Context.MODE_PRIVATE);
//初始化classloader
DexClassLoader dexClassLoader = new DexClassLoader(mDexPath,
dexOutputDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());
//注意:以下只是把插件中的Activity当作一个普通的类进行反射调用
try {
Class<?> localClass = dexClassLoader.loadClass(mClass);
Constructor<?> localConstructor = localClass
.getConstructor();
Object instance = localConstructor.newInstance();//初始化插件Acitivity对象。
//获取插件Activity的setProxy方法,这个方法是我们事先在插件中约定好的
Method setProxy = localClass.getMethod("setProxy",
Activity.class);
setProxy.setAccessible(true);
//调用插件Activity的setProxy方法
setProxy.invoke(instance, this);//将ProxyActivity对象传给插件Activity,用于setContentView等等
//获取插件Activity中的onCreate方法。
Method onCreate = localClass.getDeclaredMethod("onCreate", Bundle.class);
onCreate.setAccessible(true);
//调用插件Activity中的onCreate方法。
onCreate.invoke(instance, savedInstanceState);//将savedInstanceState传给插件
} catch (Exception e) {
e.printStackTrace();
}
}
//替换资源。
private AssetManager mAssetManager;
private Resources.Theme mTheme;
protected void loadResources(String dexPath) {
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();
mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(),superRes.getConfiguration());
mTheme = mResources.newTheme();
mTheme.setTo(super.getTheme());
}
private Resources mResources;
@Override
public AssetManager getAssets() {
return mAssetManager == null ? super.getAssets() : mAssetManager;
}
@Override
public Resources getResources() {
return mResources == null ? super.getResources() : mResources;
}
}
然后在插件中定义一个BaseActivity。让其他Acitivity实现它即可。
public class BaseActivity extends Activity {
public static final String EXTRA_DEX_PATH = "extra_dex_path";
public static final String EXTRA_ACTIVITY_NAME = "extra_activity_name";
protected Activity that; //指向插件Activity
/**
* 将代理Activity传给插件Activity
* @param proxyActivity
*/
public void setProxy(Activity proxyActivity) {
that = proxyActivity;
}

最低0.47元/天 解锁文章
7703

被折叠的 条评论
为什么被折叠?



