【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相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories