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

来源:互联网 发布:淘宝加盟1880是真的吗 编辑:程序博客网 时间:2024/06/15 02:48

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


前言

在写这篇之前,还有两篇关于插件开发的博客:
1. 【Android】Android插件开发 —— 基础入门篇
2. 【Android】Android插件开发 —— 打开插件的Activity(预注册方式)
如果读者对这篇提到的一些东西不太清楚,可以在之前的这两篇中查看。
3. 本文地址:http://blog.csdn.net/H28496/article/details/50414873 转载请注明。


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

  1. 插件中的Activity由于没有在宿主的AndroidManifest.xml中注册,因此不能直接由宿主程序打开。但是,我们仍然可以通过DexClassLoader去获取到插件中的Activity,并且执行它的各种方法,只不过这时的Activity就只是一个普通的类了,没有了各种生命周期,无法被当作Activity启动。
  2. 那无法被当作正常的Activity启动,是否可以模拟出是正常Activity的效果呢?这是可以的。
  3. 当要启动插件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(),作为插件时不再调用:

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

其它周期的方法类似。

3. 关于其它方法

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

@Overridepublic void setContentView(int layoutResID) {    if(isProxyMode){        proxyActivity.setContentView(layoutResID);    }else{        super.setContentView(layoutResID);    }}@Overridepublic 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()方法:

@Overrideprotected 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

4 0
原创粉丝点击