Android插件化开发---执行未安装apk中的Service
欢迎各位增加我的Android开发群[257053751]
假设你还不知道什么叫插件化开发。那么你应该先读一读之前写的这篇博客:Android插件化开发,初入殿堂
上一篇博客主要从总体角度分析了一下Android插件化开发的几个难点与动态载入没有被安装的apk中的Activity和资源的方法。事实上一般的插件开发主要也就是载入个Activity。读取一些资源图片之类的。可是总有遇到特殊情况的时候,比方载入Service。
要动态载入Service,有两种思路:一是通过NDK的形式。将Service通过C++执行起来(这样的方法我没有尝试,仅仅听群里的朋友说实现过);还有一种就是我使用的,具体思路和上一篇中提到载入Activity的方法一样,使用托管所的形式,因为上一篇博客没有讲清楚。这里就具体讲一下通过托管所实现载入插件中Service的方法。
下面几点是每个Android开发组肯定都知到的: 一个apk假设没有被安装的话是没有办法直接执行的。一个JAVA类的class文件是能够通过classload类载入器读取的。一个apk实际上就是一个压缩包,当中包括了一个.dex文件就是我们的代码文件。那么。接下来基本思路我们就能够明白了:apk没办法直接执行。apk中有代码文件,代码文件能够被classload读取。
在Android中有两种classload,各自是DexClassLoader、PathClassLoader。后者仅仅能载入/data/app文件夹下的apk也就是apk必需要安装才干被载入,这不是我们想要的。所以我们使用前者:DexClassLoader。
public class CJClassLoader extends DexClassLoader {
//创建一个插件载入器集合。对固定的dex使用固定的载入器能够防止多个载入器同一时候载入一个dex造成的错误。
private static final HashMap<String, CJClassLoader> pluginLoader = new HashMap<String, CJClassLoader>();
protected CJClassLoader(String dexPath, String optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(dexPath, optimizedDirectory, libraryPath, parent);
}
/**
* 返回dexPath相应的载入器
*/
public static CJClassLoader getClassLoader(String dexPath, Context cxt,
ClassLoader parent) {
CJClassLoader cjLoader = pluginLoader.get(dexPath);
if (cjLoader == null) {
// 获取到app的启动路径
final String dexOutputPath = cxt
.getDir("dex", Context.MODE_PRIVATE).getAbsolutePath();
cjLoader = new CJClassLoader(dexPath, dexOutputPath, null, parent);
pluginLoader.put(dexPath, cjLoader);
}
return cjLoader;
}
}
以上仅仅是一个開始,接着我们须要考虑一个问题,一个Service是有oncreate->onstart->ondestroy生命周期以及一些回调方法的。这些回调方法在我们正常使用的时候是由父类们(包含has...a...关系)或者说是SDK管理的,那么当我们通过类载入器载入的时候。它是没有可以管理的父类的,也就是说我们须要自己模拟SDK去管理插件Service的回调函数。那么这个去管理插件Service的类,就是之前提到的托管所。
这里是我将Service中的回调方法抽出来写成的一个接口
public interface I_CJService {
IBinder onBind(Intent intent);
void onCreate();
int onStartCommand(Intent intent, int flags, int startId);
void onDestroy();
void onConfigurationChanged(Configuration newConfig);
void onLowMemory();
void onTrimMemory(int level);
boolean onUnbind(Intent intent);
void onRebind(Intent intent);
void onTaskRemoved(Intent rootIntent);
}
//一个托管所类
class CJProxyService extends Service{
//採用包括关系
protected I_CJService mPluginService; // 插件Service对象
}
这里採用包括关系而不是採用继承(或者说实现一个接口)的方式,
是因为我们须要重写Service中的方法,而这些被重写的方法都须要用到接口对象对应的接口方法。
public class CJProxyService extends Service{
@Override
public void onConfigurationChanged(Configuration newConfig) {
mPluginService.onConfigurationChanged(newConfig);
super.onConfigurationChanged(newConfig);
} @Override
public void onLowMemory() {
mPluginService.onLowMemory();
super.onLowMemory();
} @Override
@SuppressLint("NewApi")
public void onTrimMemory(int level) {
mPluginService.onTrimMemory(level);
super.onTrimMemory(level);
} @Override
public boolean onUnbind(Intent intent) {
mPluginService.onUnbind(intent);
return super.onUnbind(intent);
} @Override
public void onRebind(Intent intent) {
mPluginService.onRebind(intent);
super.onRebind(intent);
}
}
看到这里大家应该也就明确了,托管所实际上就是一个普通的Service类,可是这个托管所是正常执行的,是由SDK管理回调函数的,我们通过这个Service的回调函数去调用插件Service中对应的回调方法,就间接的管理了插件Service的生命周期(此处能够类比Activity与Fragment的关系)
到这里为止。我们已经能够成功调起一个插件Service了,接下来的问题就是这个I_CJSrvice对象从哪里来?非常easy,通过类载入器载入一个
private void init(Intent itFromApp) {
Object instance = null;
try {
Class<?> serviceClass;
if (CJConfig.DEF_STR.equals(mDexPath)) {
serviceClass = super.getClassLoader().loadClass(mClass);
} else {
serviceClass = this.getClassLoader().loadClass(mClass);
}
Constructor<?> serviceConstructor = serviceClass
.getConstructor(new Class[] {});
instance = serviceConstructor.newInstance(new Object[] {});
} catch (Exception e) {
}
setRemoteService(instance);
mPluginService.setProxy(this, mDexPath);
}
/**
* 保留一份插件Service对象
*/
protected void setRemoteService(Object service) {
if (service instanceof I_CJService) {
mPluginService = (I_CJService) service;
} else {
throw new ClassCastException(
"plugin service must implements I_CJService");
}
}
这样就能够拿到一个I_CJSrvice对象mPluginService了,假设到此为止。还是会有问题,由于此时mPluginService中比如onStart方法还相应的是那个插件中的onStart也就是父类的onStart(这里比較绕,我不知道该怎样描写叙述)。而之前我们又说过。通过反射载入的类是没有父类的,那么假设此时强制调用那个反射对象的@Override方法是会报空指针的,由于找不到父类。那么解决的办法就是再去插件Service中重写每一个@Override的方法。
//.......篇幅有限,部分截取
public abstract class CJService extends Service implements I_CJService {
/**
* that指针指向的是当前插件的Context(因为是插件化开发。this指针绝对不能使用)
*/
protected Service that; // 替代this指针 @Override
public IBinder onBind(Intent intent) {
if (mFrom == CJConfig.FROM_PLUGIN) {
return null;
} else {
return that.onBind(intent);
}
}
}
通过代能够看到:我们使用了一个that对象来替代原本的this对象,然后我们仅仅须要通过在托管所中将这个that对象赋值为托管所的this对象,也就是插件中的全部that.xxx都相当于调用的是托管所的this.xxx,那么动态替换的目的就达到了。这样我们也就成功的载入了一个未被安装的插件apk中的Service。
有关本类中的代码。以及完整的Demo,你能够关注:Android插件式开发框架 CJFrameForAndroid
Android插件化开发---执行未安装apk中的Service的更多相关文章
- Android插件化开发
客户端开发给人的印象往往是小巧,快速奔跑.但随着产品的发展,目前产生了大量的门户型客户端.功能模块持续集成,开发人员迅速增长.不同的开发小组开发不同的功能模块,甚至还有其他客户端集成进入.能做到功能模 ...
- Android 插件化开发(四):插件化实现方案
在经过上面铺垫后,我们可以尝试整体实现一下插件化了.这里我们先介绍一下最简单的实现插件化的方案. 一.最简单的插件化实现方案 最简单的插件化实现方案,对四大组件都是适用的,技术面涉及如下: 1). 合 ...
- Android 插件化开发(一):Java 反射技术介绍
写在前面:学习插件化开发推荐书籍<Android 插件化开发指南>,本系列博客所整理知识部分内容出自此书. 在之前的项目架构的博文中,我们提到了项目插件化架构,提到插件化架构不得不提的到J ...
- Android插件化开发,初入殿堂
好久没有写博客了,这次准备写写我这几天的研究成果--Android插件化开发框架CJFrameForAndroid. 好久没有写博客了,这次准备写写我这几天的研究成果--Android插件化开发框架C ...
- 详解Android插件化开发-资源访问
动态加载技术(也叫插件化技术),当项目越来越庞大的时候,我们通过插件化开发不仅可以减轻应用的内存和CPU占用,还可以实现热插拔,即在不发布新版本的情况下更新某些模块. 通常我们把安卓资源文件制 ...
- Android 插件化开发(二):加载外部Dex文件
在学习Java反射的技术后,我们可以开始更深一步的探究插件化开发了.首先先讲一下Android App的打包流程,然后我们通过一个简单的例子 —— 实现插件化加载外部Dex来完成初级的插件化开发的探索 ...
- 《Android插件化开发指南》面世
本书在京东购买地址:https://item.jd.com/31178047689.html 本书Q群:389329264 (一)这是一本什么书 如果只把本书当作纯粹介绍Android插件化技术的书籍 ...
- 《Android插件化开发指南》勘误
一些常识: 1)全书70个代码例子中,涉及到插件的例子,请先assemble插件的项目,这会在HostApp项目中生成assets目录,并在该目录下plugin1.apk.这样,HostApp才能正常 ...
- 【我的Android进阶之旅】Android插件化开发学习资料
1.目前开源的插件开发框架大致有哪些? 1. 任玉刚 的 dynamic-load-apk Github 地址:https://github.com/singwhatiwanna/dynamic-lo ...
随机推荐
- 动态规划---最长上升子序列问题(O(nlogn),O(n^2))
LIS(Longest Increasing Subsequence)最长上升子序列 或者 最长不下降子序列.很基础的题目,有两种算法,复杂度分别为O(n*logn)和O(n^2) . ******* ...
- application(CURD)--easyui
一,效果图. 二,源代码. <!DOCTYPE html><html><head> <meta charset="UTF-8"> & ...
- bmp图片数据提取
仿照别人的程序写的bmp数据提取C代码,并将提取的数据放到txt文档中 /* date : 2014/06/24 designer :pengxiaoen version : dev4.9.9.0 f ...
- 配置系统引导启动SuperScoekt
配置系统引导启动SuperScoekt SuperSocket源码解析之启动过程 一 简介 这里主要说明从配置系统引导启动SuperScoekt作为应用程序,且以控制台程序方式启动 二 启动过程 2. ...
- android如何让service不被杀死-提高进程优先级
1.在service中重写下面的方法,这个方法有三个返回值, START_STICKY是service被kill掉后自动重写创建 @Override public int onStartCommand ...
- Ubuntu 12.04 安装JDK 8和Eclipse
Ubuntu 12.04 下安装 JDK8 方法一:(缺点是安装时附加openjdk等大量程序并无法去除,长处是安装简单) $ sudo apt-get install eclipse 方法二:(长处 ...
- 离散傅立叶变换与快速傅立叶变换(DFT与FFT)
自从去年下半年接触三维重构以来,听得最多的词就是傅立叶变换,后来了解到这个变换在图像处理里面也是重点中的重点. 本身自己基于高数知识的理解是傅立叶变换是将一个函数变为一堆正余弦函数的和的变换.而图像处 ...
- 关于C++异常机制的笔记(SEH, try-catch)
昨天晚上加班解决了一个问题,是由于无法正确的捕获到异常导致的.刚开始用try-catch,但是没法捕获到异常:后面改成SEH异常才解决.因此今天将这个问题重新梳理了一遍,关于try-catch, SE ...
- 再写KMP算法
#include<iostream> #include<string> using namespace std; void getNext(char const*T,int l ...
- C-01背包问题
[声明]:非常感谢http://blog.sina.com.cn/s/blog_6dcd26b301013810.html,给我带来的帮助. 看这个图片表示的意思: w[i]表示第i件物品的容积 ,p ...