您的位置:首页 > 移动开发 > Android开发

【Android】Android插件开发 —— 打开插件的Activity(代理方式)

2015-12-28 00:18 579 查看

Android插件开发 —— 打开插件的Activity(代理方式)

前言

在写这篇之前,还有两篇关于插件开发的博客:

1. 【Android】Android插件开发 —— 基础入门篇

2. 【Android】Android插件开发 —— 打开插件的Activity(预注册方式)

如果读者对这篇提到的一些东西不太清楚,可以在之前的这两篇中查看。

3. 本文地址:http://blog.csdn.net/H28496/article/details/50414873 转载请注明。

1. 用代理的方式打开插件Activity的整体思想

插件中的Activity由于没有在宿主的AndroidManifest.xml中注册,因此不能直接由宿主程序打开。但是,我们仍然可以通过DexClassLoader去获取到插件中的Activity,并且执行它的各种方法,只不过这时的Activity就只是一个普通的类了,没有了各种生命周期,无法被当作Activity启动。

那无法被当作正常的Activity启动,是否可以模拟出是正常Activity的效果呢?这是可以的。

当要启动插件Activity的时候,启动一个宿主的Activity作为代理。当生命周期执行到onCreate() 、onStart()的时候,系统会调用宿主Activity的onCreate()、onStart()方法,再由宿主的onCreate()方法去调用插件的onCreate()方法。这就模拟出了插件Activity在执行的效果。

2. 插件的Activity

1. 关于插件Activity的Context

由于插件Activity只是一个普通的类,并没有上下文Context,所以插件的Activity在需要用到(Context)this的地方会遇到问题。比如:

startActivity(new Intent(this, xxxx.Class));


这样是无法打开Activity的,怎么解决呢?

插件Activity需要持有对宿主Activity的引用。

在需要用到上下文Context的地方,不再使用this关键字,而是使用getActivity()代替,其中proxyActivity是宿主Activity:

public Context getActivity(){
return proxyActivity;
}


为了让插件能够独立运行,也就是说插件既可以独立的安装运行,又可以作为插件给宿主运行。可以在getActivity()中加一个判断,当被作为插件使用时才返回proxyActivity,正常情况下返回this:

public Context getActivity(){
if(isProxyMode){
return proxyActivity;
}else{
return this;
}
}


这样创建Intent时就可以这样写了:

Intent intent = new Intent(getActivity(), XXXX.class);


那插件Activity怎么获得宿主Activity的引用呢?通过setProxy()方法,其中BaseActivity是插件Activity:

public class BaseActivity extends Activity{
/**
* 宿主的Activity
*/
protected Activity proxyActivity;

/**
* 是否作为插件运行
*/
public boolean isProxyMode = false;

public void setProxy(Activity activity){
this.proxyActivity = activity;
isProxyMode = true;
}
}


setProxy(Activity activity)是由宿主Activity调用的。具体怎么调用待会再讲。

2. 关于生命周期的方法

我们知道,Activity关于生命周期的方法都会调用super.onXXXX()的。由于插件Activity并不是由系统通过正常方式打开的,在插件Activity中调用super.onXXXX()方法会报错。并且,插件的生命周期已经交给宿主Activity去执行了,所以插件的生命周期中不再需要执行super.onXXX()方法。所以我们需要重写插件Activity中关于生命周期的方法。同时考虑到我们的插件不仅可以作为插件使用,还可以作为正常的apk安装使用,使得正常安装时会调用super.onXXX(),作为插件时不再调用:

@Override
public void onCreate(Bundle savedInstanceState) {
if(!isProxyMode){
super.onCreate(savedInstanceState);
}
// 这里执行插件自己的代码
}


其它周期的方法类似。

3. 关于其它方法

和生命周期的方法类似,插件Activity的其他方法也不能使用super.xxxx()方法了。但和生命周期的方法不同的是:其他方法并没有通过宿主Activity代理。所以还需要我们手动调用宿主Activity执行,重写这些方法,例如:

@Override
public void setContentView(int layoutResID) {
if(isProxyMode){
proxyActivity.setContentView(layoutResID);
}else{
super.setContentView(layoutResID);
}
}

@Override
public void startActivity(Intent intent) {
if(isProxyMode){
proxyActivity.startActivity(intent);
}else{
super.startActivity(intent);
}
}


我们把上面这些重写的代码放到一个基类BaseActivity中,插件中的其他Activity类继承BaseActivity。这样我们在使用的时候就能够和普通Activity一样使用了,侵入性很低:

public class MainActivity extends BaseActivity {

@Override
public void onCreate(Bundle savedInstanceState) {
TextView tv = new TextView(this);
tv.setText("这是插件的Activity");
setContentView(tv);
}
}


当然,如果很懒,不想去重写一堆方法,也可以这样写:

getActivity().setContentView(tv);


2. 宿主的Activity

1. 初始化工作

根据传入的插件的类名,从外部apk中加载对应的类并实例化出一个对象。

这一步在前两篇博客中已有详细描述,这里只贴代码:

/**
* 初始化classLoader
*/
private void initClassLoader() {
// 插件放在sd卡的根目录下
String apkPath = getIntent().getStringExtra(EXTRA_APK_PATH);

// dex文件的释放目录
File releasePath = getDir("dexs", 0);

// 类加载器
classLoader = new DexClassLoader(apkPath, releasePath.getAbsolutePath(), null, getClassLoader());

// 注入到原生的ClassLoader中
ClassInject inject = new ClassInject();
inject.inject((PathClassLoader) getClassLoader(), classLoader);
}

/**
* 加载被代理Activity的信息
*/
public void loadProxiedActivity(){
// 实例化被代理类
String className = getIntent().getStringExtra(PROXIED_CLASS_NAME);
android.util.Log.i("郑海鹏", "ProxyActivity#initProxiedActivity(): " + "传入的类名为:\n" + className);
try {
Class<?> clazz = classLoader.loadClass(className);
proxiedActivity = clazz.newInstance();
// 使得插件的Activity持有宿主Activity的引用
Method method = proxiedActivity.getClass().getMethod("setProxy", Activity.class);
method.setAccessible(true);
method.invoke(proxiedActivity, this);
} catch (Exception e) {
e.printStackTrace();
}
}


注意插件Activity的setProxy()方法就是在这类调用的。

2. 代理操作

在系统回调生命周期时,执行插件Activity对应的方法

例如在onPause()中执行插件的onPause()方法:

@Override
protected void onPause() {
if(proxiedActivity != null){
try {
Method method = proxiedActivity.getClass().getMethod("onPause", new Class[]{});
method.setAccessible(true);
method.invoke(proxiedActivity, new Object[]{});
} catch (Exception e) {
e.printStackTrace();
}
}
super.onPause();
}


proxiedActivity是插件Activity。这样就实现了对插件的代理。

3. 源码下载

http://download.csdn.net/detail/h28496/9379695
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息