Android插件化開發(fā)---運(yùn)行未安裝apk中的Service
來源:程序員人生 發(fā)布時(shí)間:2014-11-15 00:28:15 閱讀次數(shù):2838次
如果你還不知道甚么叫插件化開發(fā),那末你應(yīng)當(dāng)先讀1讀之前寫的這篇博客:Android插件化開發(fā),初入殿堂
上1篇博客主要從整體角度分析了1下Android插件化開發(fā)的幾個(gè)難點(diǎn)與動態(tài)加載沒有被安裝的apk中的Activity和資源的方法。其實(shí)1般的插件開發(fā)主要也就是加載個(gè)Activity,讀取1些資源圖片之類的。但是總有遇到特殊情況的時(shí)候,比如加載Service。
要動態(tài)加載Service,有兩種思路:1是通過NDK的情勢,將Service通過C++運(yùn)行起來(這類方法我沒有嘗試,只聽群里的朋友說實(shí)現(xiàn)過);另外一種就是我使用的,具體思路和上1篇中提到加載Activity的方法1樣,使用托管所的情勢,由于上1篇博客沒有講清楚,這里就詳細(xì)講1下通過托管所實(shí)現(xiàn)加載插件中Service的方法。
以下幾點(diǎn)是每個(gè)Android開發(fā)組肯定都知到的: 1個(gè)apk如果沒有被安裝的話是沒有辦法直接運(yùn)行的。1個(gè)JAVA類的class文件是可以通過classload類加載器讀取的。1個(gè)apk實(shí)際上就是1個(gè)緊縮包,其中包括了1個(gè).dex文件就是我們的代碼文件。那末,接下來基本思路我們就能夠明確了:apk沒辦法直接運(yùn)行,apk中有代碼文件,代碼文件可以被classload讀取。
在Android中有兩種classload,分別是DexClassLoader、PathClassLoader。后者只能加載/data/app目錄下的apk也就是apk必須要安裝才能被加載,這不是我們想要的,所以我們使用前者:DexClassLoader。
public class CJClassLoader extends DexClassLoader {
//創(chuàng)建1個(gè)插件加載器集合,對固定的dex使用固定的加載器可以避免多個(gè)加載器同時(shí)加載1個(gè)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對應(yīng)的加載器
*/
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;
}
}
以上只是1個(gè)開始,接著我們需要斟酌1個(gè)問題,1個(gè)Service是有oncreate->onstart->ondestroy生命周期和1些回調(diào)方法的,這些回調(diào)方法在我們正常使用的時(shí)候是由父類們(包括has...a...關(guān)系)或說是SDK管理的,那末當(dāng)我們通過類加載器加載的時(shí)候,它是沒有能夠管理的父類的,也就是說我們需要自己摹擬SDK去管理插件Service的回調(diào)函數(shù)。那末這個(gè)去管理插件Service的類,就是之條件到的托管所。
這里是我將Service中的回調(diào)方法抽出來寫成的1個(gè)接口
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);
}
//1個(gè)托管所類
class CJProxyService extends Service{
//采取包括關(guān)系
protected I_CJService mPluginService; // 插件Service對象
}
這里采取包括關(guān)系而不是采取繼承(或說實(shí)現(xiàn)1個(gè)接口)的方式,
是由于我們需要重寫Service中的方法,而這些被重寫的方法都需要用到接口對象相應(yīng)的接口方法。
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);
}
}
看到這里大家應(yīng)當(dāng)也就明白了,托管所實(shí)際上就是1個(gè)普通的Service類,但是這個(gè)托管所是正常運(yùn)行的,是由SDK管理回調(diào)函數(shù)的,我們通過這個(gè)Service的回調(diào)函數(shù)去調(diào)用插件Service中相應(yīng)的回調(diào)方法,就間接的管理了插件Service的生命周期(此處可以類比Activity與Fragment的關(guān)系)
到這里為止,我們已可以成功調(diào)起1個(gè)插件Service了,接下來的問題就是這個(gè)I_CJSrvice對象從哪里來?很簡單,通過類加載器加載1個(gè)
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);
}
/**
* 保存1份插件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");
}
}
這樣就能夠拿到1個(gè)I_CJSrvice對象mPluginService了,如果到此為止,還是會有問題,由于此時(shí)mPluginService中例如onStart方法還對應(yīng)的是那個(gè)插件中的onStart也就是父類的onStart(這里比較繞,我不知道該如何描寫),而之前我們又說過,通過反射加載的類是沒有父類的,那末如果此時(shí)強(qiáng)迫調(diào)用那個(gè)反射對象的@Override方法是會報(bào)空指針的,由于找不到父類。那末解決的辦法就是再去插件Service中重寫每一個(gè)@Override的方法。
//.......篇幅有限,部份截取
public abstract class CJService extends Service implements I_CJService {
/**
* that指針指向的是當(dāng)前插件的Context(由因而插件化開發(fā),this指針絕對不能使用)
*/
protected Service that; // 替換this指針
@Override
public IBinder onBind(Intent intent) {
if (mFrom == CJConfig.FROM_PLUGIN) {
return null;
} else {
return that.onBind(intent);
}
}
}
通過代可以看到:我們使用了1個(gè)that對象來替換本來的this對象,然后我們只需要通過在托管所中將這個(gè)that對象賦值為托管所的this對象,也就是插件中的所有that.xxx都相當(dāng)于調(diào)用的是托管所的this.xxx,那末動態(tài)替換的目的就到達(dá)了,這樣我們也就成功的加載了1個(gè)未被安裝的插件apk中的Service。
有關(guān)本類中的代碼,和完全的Demo,你可以關(guān)注:Android插件式開發(fā)框架 CJFrameForAndroid
生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈