android-plugmgr源代碼分析
android-plugmgr是一個Android插件加載框架,它最大的特點就是對插件不需要進行任何約束。關於這個類庫的介紹見作者博客,市麵上也有一些插件加載框架,但是感覺沒有這個好。在這篇文章中,我們將不僅止於原理,對源代碼的具體實現進行分析。文章中涉及的代碼可從https://github.com/kissazi2/AndroidDemo/tree/master/PlugLoadDemo下載,基於Android Studio 1.2編譯。
在正式開始分析源代碼之前,我們首先需要有一些動態加載Apk的基礎知識。
- 總的說來,為了調用另一個apk中的類,我們就需要用Android中提供的ClassLoader,將不屬於宿主Apk中的類加載進來。由於Android中不允許沒有在AndroidManifest中聲明的Activity被運行,所以我們需要在AndroidManifest中聲明一個代理Activity(ProxyActivity).這篇文章中的思路是,在插件的Activity中將這個ProxyActivity傳進去,這樣代碼執行的邏輯是插件Activity的,同時插件Activity也因為ProxyActivity擁有了部分係統資源的訪問能力。這裏不是指Resource的訪問而是Context所擁有的訪問其他組件、與服務交互的能力。當然,插件Activity也存在生命周期函數沒法被係統調用、無法訪問Resource的問題。
-
《 Android apk動態加載機製的研究(二):資源加載和activity生命周期管理》.
這篇文章解決資源訪問的思路是,通過構造Resource對象及其依賴的AssetManager,將資源重定向到插件Apk,然後Override插件中getResource方法。解決生命周期函數無法調用的方法是在ProxyActivity的每一個生命周期函數調用的時候同時調用插件Activity的對應函數。
從這兩篇文章中,我們大致了解了插件動態加載的流程。我們將要分析的android_plugmgr的思路與上麵文章中的思路完全不同,它通過將繼承關係反轉,也就是說宿主Activity中的ProxyActivity去繼承插件中的Activity。在startActivity(...)啟動這個Activity之前,通過自己實現的ClassLoader去加載這個臨時被構造出來的Activity。這個框架不像上麵文章介紹的插件加載框架有那麼多的限製。
1.先用起來
這個框架隻需要在如下麵截圖中的指定文件夾(一般是sdcard中的Download文件夾)中放入需要動態加載的APK文件,選擇對應的圖標就可以實現動態加載了.
1、加載插件信息
@Override
public void onClick(View v) {
final String dirText = pluginDirTxt.getText().toString().trim();
//省略不必要的代碼
new Thread(new Runnable() {
@Override
public void run() {
Collection<PlugInfo> plugs = plugMgr
.loadPlugin(new File(dirText));
setPlugins(plugs);
}
}).start();
}
private void setPlugins(final Collection<PlugInfo> plugs) {
if (plugs == null || plugs.isEmpty()) {
return;
}
final ListAdapter adapter = new PlugListViewAdapter(this, plugs);
runOnUiThread(new Runnable() {
public void run() {
pluglistView.setAdapter(adapter);
}
});
}
從上麵我們看到插件的信息通過文件地址dirText傳入,然後由 plugMgr.loadPlugin(new File(dirText))進行apk信息的加載,最終將信息保存在plugInfo的集合。plugMgr是一個PluginManager類型的對象,它定義在android_plugmgr類庫中。PlugListViewAdapter(繼承自BaseAdapter)就是一個帶有PlugInfo的Adatpter而已。
2、點擊每一項,加載對應的插件
private void plugItemClick(int position) {
PlugInfo plug = (PlugInfo) pluglistView.getItemAtPosition(position);
plugMgr.startMainActivity(this, plug.getPackageName());
}
2.類庫分析
在了解基本的使用之後,我們看看android_plugmgr中包含什麼具體的類。截圖中的類所在的包與原作者的類庫中的包略有不同,因為要在Android Studio中編譯做的小修改。下麵羅列出這些類,隻為給大家一個模煳的關於這個類庫結構,可以嚐試著理清他們之間的關係,看不懂就跳過。
- ActivityClassGenerator: 動態生成 插件Activity子類的工具類
- ActivityOverider :提供公共方法供自動生成的Activity調用
- FrameworkClassLoader 框架類加載器(Application 的 classLoder被替換成此類的實例),它主要是對插件、宿主的apk進行分別的處理。
- LayoutInflaterWrapper :LayoutInflater 包裝器,用來替換某些係統布局
- PluginActivityLifeCycleCallback :插件Activity的生命周期方法回調
- PluginActivityWrapper :插件Activity的包裝類
- PluginClassLoader :插件類加載器,實現dex文件生成、加載
- PluginContextWrapper :插件Context包裝類
- PlugInfo :插件信息實體類
- PluginManager :插件管理類,提供給外部初始化插件apk、啟動Activity的公共借口。
- PluginManifestUtil :插件Manifest信息提取類,除提取Manifest信息外,也包括提取插件apk中lib的類庫)
- PluginPackageManager :這個類庫自定義的PackageManager,對PackageManager做了些包裝
- ReflectionUtils :反射工具類,提供一些常用的反射操作。
- XmlManifestReader : AndroidManifest.xml讀取類
3.代碼分析
3.1 插件apk信息的加載
從前麵的分析中,我們知道要加載插件apk中的信息,我們需要加載插件apk中的組件(Activity、service、brocastreceiver)、用DexClassLoader加載Dex文件中信息、修改Resource類的資源指向。要完成以上操作,我們需要讀取插件apk包中的AndroidManifest信息,取出一些必要的內容(不單單是從AndroidManifest.xml,還有插件apk本身)。
之前我們忽略了PluginManager的初始化。它的初始化是在宿主APK的MainActivity中進行的。初始化通過PluginManager.getInstance()→PluginManager.init()進行調用。getInstance()所做的就是獲取ApplicationContext然後傳給init().
private void init(Context ctx) {
Log.i(tag, "init()...");
context = ctx;
//plugsout是讓DexClassLoader存放它Dex文件的地方,可以先放過
File optimizedDexPath = ctx.getDir("plugsout", Context.MODE_PRIVATE);
if (!optimizedDexPath.exists()) {
optimizedDexPath.mkdirs();
}
dexOutputPath = optimizedDexPath.getAbsolutePath();
dexInternalStoragePath = context
.getDir("plugins", Context.MODE_PRIVATE);
//創建plugins用來存放插件apk,以及插件apk中的lib包
dexInternalStoragePath.mkdirs();
// 改變classLoader,以便根據是插件apk、還是宿主apk中的類進行特殊處理
try {
Object mPackageInfo = ReflectionUtils.getFieldValue(ctx,
"mBase.mPackageInfo", true);
frameworkClassLoader = new FrameworkClassLoader(
ctx.getClassLoader());
// set Application's classLoader to FrameworkClassLoader
ReflectionUtils.setFieldValue(mPackageInfo, "mClassLoader",
frameworkClassLoader, true);
} catch (Exception e) {
e.printStackTrace();
}
hasInit = true;
}
我們來模擬計算機一步步地執行看看。從"先用起來"那一節,我們知道加載類庫會調用androidx.pluginmgr.PluginManager.loadPlugin(...)方法.在這一步裏麵主要對後麵用的到的信息進行加載.從代碼裏麵不管單個還是多個插件都進入PluginManager.buildPlugInfo(...)。
public Collection<PlugInfo> loadPlugin(final File pluginSrcDirFile)
throws Exception {
checkInit();
if (pluginSrcDirFile == null || !pluginSrcDirFile.exists()) {
Log.e(tag, "invalidate plugin file or Directory :"
+ pluginSrcDirFile);
return null;
}
if (pluginSrcDirFile.isFile()) {
// 如果是文件則嚐試加載單個插件,暫不檢查文件類型,除apk外,以後可能支持加載其他類型文件,如jar
PlugInfo one = loadPluginWithId(pluginSrcDirFile, null, null);
return Collections.singletonList(one);
}
// clear all first
synchronized (this) {
pluginPkgToInfoMap.clear();
pluginIdToInfoMap.clear();
}
File[] pluginApks = pluginSrcDirFile.listFiles(this);
if (pluginApks == null || pluginApks.length < 1) {
throw new FileNotFoundException("could not find plugins in:"
+ pluginSrcDirFile);
}
for (File pluginApk : pluginApks) {
PlugInfo plugInfo = buildPlugInfo(pluginApk, null, null);
if (plugInfo != null) {
savePluginToMap(plugInfo);
}
}
return pluginIdToInfoMap.values();
}
/**
* 初始化插件apk的必要信息
* @param pluginApk 要加載的APK路徑
* @param pluginId 插件apk的名字(一般是xxx.apk),nullable
* @param targetFileName 插件apk的名稱,nullable
* @return
* @throws Exception
*/
private PlugInfo buildPlugInfo(File pluginApk, String pluginId,
String targetFileName) throws Exception {
PlugInfo info = new PlugInfo();
info.setId(pluginId == null ? pluginApk.getName() : pluginId);
File privateFile = new File(dexInternalStoragePath,
targetFileName == null ? pluginApk.getName() : targetFileName);
info.setFilePath(privateFile.getAbsolutePath());
//1.把插件apk複製到app_plugins目錄下,防止因為意外情況被破壞
if (!pluginApk.getAbsolutePath().equals(privateFile.getAbsolutePath())) {
copyApkToPrivatePath(pluginApk, privateFile);
}
//2.讀取AndroidManifest中的信息
String dexPath = privateFile.getAbsolutePath();
PluginManifestUtil.setManifestInfo(context, dexPath, info);
//3.設置插件加載器
PluginClassLoader loader = new PluginClassLoader(dexPath,
dexOutputPath, frameworkClassLoader, info);
info.setClassLoader(loader);
//4.重定向Resource對象的資源指向
try {
AssetManager am = (AssetManager) AssetManager.class.newInstance();
am.getClass().getMethod("addAssetPath", String.class)
.invoke(am, dexPath);
info.setAssetManager(am);
Resources ctxres = context.getResources();
Resources res = new Resources(am, ctxres.getDisplayMetrics(),
ctxres.getConfiguration());
info.setResources(res);
} catch (Exception e) {
e.printStackTrace();
}
//5.初始化插件的application,調用它的onCreate()
if (actFrom != null) {
initPluginApplication(info, actFrom, true);
}
// createPluginActivityProxyDexes(info);
Log.i(tag, "buildPlugInfo: " + info);
return info;
}
這段函數的思路還是很清晰的,其中我們最為感興趣的是,加載插件apk究竟需要AndroidManifest什麼信息?可以預想到Android四大組件肯定是必須的,權限不是必須的。插件apk的所擁有的權限隻能宿主apk中AndroidManifest中的,這是Android的安全機製所保證的。進入PluginManifestUtil.setManifestInfo(...)。
/**
* 設置跟Manifest有關的信息
* @param context MainActivity的實例
* @param apkPath 插件apk的路徑
* @param info 設置了插件apk的Id、FilePath的PlugInfo
* @throws XmlPullParserException
* @throws IOException
*/
static void setManifestInfo(Context context, String apkPath, PlugInfo info)
throws XmlPullParserException, IOException {
//1.讀取壓縮包裏麵的信息,將AndroidManifest的值讀取出來
ZipFile zipFile = new ZipFile(new File(apkPath), ZipFile.OPEN_READ);
ZipEntry manifestXmlEntry = zipFile.getEntry(XmlManifestReader.DEFAULT_XML);
String manifestXML = XmlManifestReader.getManifestXMLFromAPK(zipFile,
manifestXmlEntry);
PackageInfo pkgInfo = context.getPackageManager()
.getPackageArchiveInfo(
apkPath,
PackageManager.GET_ACTIVITIES
| PackageManager.GET_RECEIVERS//
| PackageManager.GET_PROVIDERS//
| PackageManager.GET_META_DATA//
| PackageManager.GET_SHARED_LIBRARY_FILES//
);
//2.將插件apk中libs目錄下的所有文件複製到app_plugins/插件Id-dir-lib目錄下
File libdir = ActivityOverider.getPluginLibDir(info.getId());
try {
if(extractLibFile(zipFile, libdir)){
pkgInfo.applicationInfo.nativeLibraryDir=libdir.getAbsolutePath();
}
} finally {
zipFile.close();
}
//3.設置插件中activity、receiver、service、application等信息
setAttrs(info, manifestXML);
}
從這個函數中,我們就完成了對插件apk所需要的信息的加載,需要注意的是,android-plugmgr這版的源代碼暫時隻支持Activity,Service、Receiver都還是不支持的。
3.2 插件apk中Activity的加載
這部分是這個類庫的核心。這個類庫之所以能夠無約束地調用插件apk中的Activity,依賴的就是依賴倒置,讓宿主apk中的ProxyActivity去繼承插件apk中特定的Activity。它的做法是利用dexmaker在運行時動態生成代碼實現繼承,然後將這個子類輸出成Dex文件放到/data/data/androidx.plmgrdemo/app_plugsout。最後它就利用DexClassLoader進行加載。
從點擊主界麵點擊每一個插件開始,
private void plugItemClick(int position) {
PlugInfo plug = (PlugInfo) pluglistView.getItemAtPosition(position);
plugMgr.startMainActivity(this, plug.getPackageName());
}
public boolean startMainActivity(Context context, String pkgOrId) {
Log.d(tag, "startMainActivity by:" + pkgOrId);
//1.根據包名或Apk命,獲取plugInfo
PlugInfo plug = preparePlugForStartActivity(context, pkgOrId);
if (frameworkClassLoader == null) {
Log.e(tag, "startMainActivity: frameworkClassLoader == null!");
return false;
}
if (plug.getMainActivity() == null) {
Log.e(tag, "startMainActivity: plug.getMainActivity() == null!");
return false;
}
if (plug.getMainActivity().activityInfo == null) {
Log.e(tag,
"startMainActivity: plug.getMainActivity().activityInfo == null!");
return false;
}
//2.這裏很關鍵,三個參數分別是插件的包名、將要加載的插件Activity的名稱,這是為了後麵能正確地根據
//Activity或其他普通類是宿主的還是插件的,進行分別處理
String className = frameworkClassLoader.newActivityClassName(
plug.getId(), plug.getMainActivity().activityInfo.name);
//還記得之前貼的代碼中(3.1中buildPlugInfo函數),我們提到的application中的classLoader被替換
//成FrameworkClassloader
context.startActivity(new Intent().setComponent(new ComponentName(
context, className)));
return true;
}
上麵代碼就是插件加載Activity的整個過程,然而我們漏掉了插件Activity的加載流程,以及特定的Activity怎麼被ProxyActivity繼承的過程。接下來分析這個類庫中自定義的FrameworkClassloader加載特定類的過程。在上麵代碼context.startActivity(...)中會對將要start的Activity進行加載,也就是調用ClassLoader進行加載,由於我們已經將宿主apk中application的默認Classloader替換成了FrameworkClassloader,所以在context.startActivity(...)的過程中會調用FrameworkClassLoader.loadClass(...)。上一個流程中調用了FrameworkClassloader.newActivityClassName(...),主要為現在插件apk中Activity的加載埋下伏筆。
/**
* 1.用actName記錄真正要加載的插件apk中的Activity
* 2.返回ActivityOverider.targetClassName來指示後麵的處理流程,要加載的是插件Apk中的Activity
* @param plugId
* @param actName
* @return
*/
String newActivityClassName(String plugId, String actName) {
this.plugId = plugId;
this.actName = actName;
return ActivityOverider.targetClassName;
}
protected Class<?> loadClass(String className, boolean resolv)
throws ClassNotFoundException {
Log.i("cl", "loadClass: " + className);
if (plugId != null) {
String pluginId = plugId;
PlugInfo plugin = PluginManager.getInstance().getPluginById(
pluginId);
Log.i("cl", "plugin = " + plugin);
if (plugin != null) {
try {
//這裏判斷要加載的Activity是否插件apk中的Activity
if (className.equals(ActivityOverider.targetClassName)) {
// Thread.dumpStack();
String actClassName = actName;
//這裏的Classloader是PluginClassLoader
return plugin.getClassLoader().loadActivityClass(
actClassName);
} else {
return plugin.getClassLoader().loadClass(className);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
return super.loadClass(className, resolv);
}
}
看來frameworkClassloader也就是對類的加載進行預處理,真正的處理還在PluginClassLoader.loadActivityClass(...)。
/**
* 加載Activity類
* @param actClassName 將要加載的特定Activity(帶包名)
* @return
* @throws ClassNotFoundException
*/
Class<?> loadActivityClass(final String actClassName) throws ClassNotFoundException {
Log.d(tag, "loadActivityClass: " + actClassName);
// 在類加載之前檢查創建代理的Activity dex文件,以免調用者忘記生成此文件
File dexSavePath = ActivityOverider.createProxyDex(thisPlugin, actClassName, true);
ClassLoader actLoader = proxyActivityLoaderMap.get(actClassName);
if (actLoader == null) {
actLoader = new DexClassLoader(dexSavePath.getAbsolutePath(), optimizedDirectory,libraryPath, this){
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Log.d("PlugActClassLoader("+ actClassName+")", "loadClass: " + name);
if (ActivityOverider.targetClassName.equals(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
Log.d("PlugActClassLoader("+ actClassName+")", "findClass");
c = findClass(name);
}
if (resolve) {
resolveClass(c);
}
return c;
}
return super.loadClass(name, resolve);
}
};
proxyActivityLoaderMap.put(actClassName, actLoader);
}
return actLoader.loadClass(ActivityOverider.targetClassName);
}
這段函數裏麵重點就是ActivityOverider.createProxyDex(...)函數。另外,關於為什麼 new DexClassLoader(...)的時候要將this(也就是PluginClassLoader)作為parent傳入,參考Java ClassLoader基礎及加載不同依賴 Jar 中的公共類。
static File createProxyDex(PlugInfo plugin, String activity, boolean lazy) {
//這裏savePath = /data/data/androidx.plmgrdemo/app_plugins/app名稱.apk-dir/activities/app包名.XXXXActivity.dex
File savePath = getPorxyActivityDexPath(plugin.getId(), activity);
createProxyDex(plugin, activity, savePath, lazy);
return savePath;
}
private static void createProxyDex(PlugInfo plugin, String activity, File saveDir,
boolean lazy) {
// Log.d(tag + ":createProxyDex", "plugin=" + plugin + "\n, activity="
// + activity);
if (lazy && saveDir.exists()) {
// Log.d(tag, "dex alreay exists: " + saveDir);
// 已經存在就不創建了,直接返回
return;
}
// Log.d(tag, "actName=" + actName + ", saveDir=" + saveDir);
try {
String pkgName = plugin.getPackageName();
//這裏是ProxyActivity繼承插件中特定Activity的具體處理
ActivityClassGenerator.createActivityDex(activity, targetClassName,
saveDir, plugin.getId(), pkgName);
} catch (Throwable e) {
Log.e(tag, Log.getStackTraceString(e));
}
}
public static void createActivityDex(String superClassName,
String targetClassName, File saveTo, String pluginId, String pkgName)
throws IOException {
byte[] dex = createActivityDex(superClassName, targetClassName,
pluginId, pkgName);
if (saveTo.getName().endsWith(".dex")) {
FileUtil.writeToFile(dex, saveTo);
} else {
JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(
saveTo));
jarOut.putNextEntry(new JarEntry(DexFormat.DEX_IN_JAR_NAME));
jarOut.write(dex);
jarOut.closeEntry();
jarOut.close();
}
}
可以看出ActivityClassGenerator.createActivityDex(...)是重點,它利用dexmaker在運行時動態生成代碼讓ProxyActivity繼承插件apk中特定的Activity,當然這個Activity中的生命周期函數肯定要進行一些修改。在看接下來的代碼前,你可能需要先了解dexmarker的語法.
/**
* 生成Activity的Dex文件
* @param superClassName
* @param targetClassName
* @param pluginId
* @param pkgName
* @return
*/
public static <S, D extends S> byte[] createActivityDex(
final String superClassName, final String targetClassName,
final String pluginId, String pkgName) {
DexMaker dexMaker = new DexMaker();
TypeId<D> generatedType = TypeId.get('L' + targetClassName.replace('.',
'/') + ';');
TypeId<S> superType = TypeId
.get('L' + superClassName.replace('.', '/') + ';');
// 聲明類
dexMaker.declare(generatedType, "", PUBLIC | FINAL, superType);
// 定義字段
//private static final String _pluginId = @param{pluginId};
// private AssetManager asm;
// private Resources res;
declareFields(dexMaker, generatedType, superType, pluginId,pkgName);
// 聲明 默認構造方法
declare_constructor(dexMaker, generatedType, superType);
// 聲明 方法:onCreate
declareMethod_onCreate(dexMaker, generatedType, superType);
// 聲明 方法:public AssetManager getAssets()
declareMethod_getAssets(dexMaker, generatedType, superType);
// 聲明 方法:public Resources getResources()
declareMethod_getResources(dexMaker, generatedType, superType);
/*
* 聲明 方法:startActivityForResult(Intent intent, int requestCode, Bundle
* options)
*/
declareMethod_startActivityForResult(dexMaker, generatedType,superType);
// 聲明 方法:public void onBackPressed()
declareMethod_onBackPressed(dexMaker, generatedType, superType);
declareMethod_startService(dexMaker, generatedType, superType);
declareMethod_bindService(dexMaker, generatedType, superType);
declareMethod_unbindService(dexMaker, generatedType, superType);
declareMethod_stopService(dexMaker, generatedType, superType);
// Create life Cycle methods
declareLifeCyleMethod(dexMaker, generatedType, superType, "onResume");
declareLifeCyleMethod(dexMaker, generatedType, superType, "onStart");
declareLifeCyleMethod(dexMaker, generatedType, superType, "onRestart");
declareLifeCyleMethod(dexMaker, generatedType, superType, "onPause");
declareLifeCyleMethod(dexMaker, generatedType, superType, "onStop");
declareLifeCyleMethod(dexMaker, generatedType, superType, "onDestroy");
declareMethod_attachBaseContext(dexMaker, generatedType, superType);
declareMethod_getComponentName(dexMaker, generatedType, superType, superClassName);
declareMethod_getPackageName(dexMaker, generatedType, pkgName);
declareMethod_getIntent(dexMaker, generatedType, superType);
declareMethod_setTheme(dexMaker, generatedType, superType);
// Create the dex Content
byte[] dex = dexMaker.generate();
return dex;
}
傳入這個函數的值(以Android-PullToRefresh說明)
- superClassName = "com.handmark.pulltorefresh.samples.LauncherActivity"
- targetClassName = "androidx.pluginmgr.PluginActivity"
- pluginId = "sample-3.0.0.apk"
- pkgName = "com.handmark.pulltorefresh.samples"
別看這段函數這麼長,它也就做了下麵的事情(你可以看一下原作者博客看這段生成的代碼究竟是怎麼樣的):
- 讓一個ProxyActivity繼承插件Apk中特定的Activity
- 聲明字段
- 對重要的生命周期方法進行聲明,並添加添加ActivityOverride對應的靜態方法
- override訪問資源的函數getResource(...)等Activity自身要提供的公共接口
我們以declareMethod_onCreate(dexMaker, generatedType, superType)進行分析
private static <S, D extends S> void declareMethod_onCreate(
DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType) {
//
// 聲明 方法:onCreate
TypeId<Bundle> Bundle = TypeId.get(Bundle.class);
TypeId<ActivityOverider> ActivityOverider = TypeId
.get(ActivityOverider.class);
MethodId<D, Void> method = generatedType.getMethod(TypeId.VOID,
"onCreate", Bundle);
Code methodCode = dexMaker.declare(method, PROTECTED);
// locals
Local<D> localThis = methodCode.getThis(generatedType);
Local<Bundle> lcoalBundle = methodCode.getParameter(0, Bundle);
Local<Boolean> lcoalCreated = methodCode.newLocal(TypeId.BOOLEAN);
Local<String> pluginId = get_pluginId(generatedType, methodCode);
// this.mOnCreated = true;
FieldId<D, Boolean> beforeOnCreate = generatedType.getField(TypeId.BOOLEAN, FIELD_mOnCreated);
methodCode.loadConstant(lcoalCreated, true);
methodCode.iput(beforeOnCreate, localThis, lcoalCreated);
//ActivityOverider.callback_onCreate(str, this);
MethodId<ActivityOverider, Void> method_call_onCreate = ActivityOverider
.getMethod(TypeId.VOID, "callback_onCreate", TypeId.STRING,
TypeId.get(Activity.class));
methodCode
.invokeStatic(method_call_onCreate, null, pluginId, localThis);
// super.onCreate()
MethodId<S, Void> superMethod = superType.getMethod(TypeId.VOID, "onCreate",
Bundle);
methodCode.invokeSuper(superMethod, null, localThis, lcoalBundle);
methodCode.returnVoid();
}
這段函數所做的事情可以用下麵的java代碼表示
protected void onCreate(Bundle paramBundle)
{
String str = _pluginId;
this.mOnCreated = true;
ActivityOverider.callback_onCreate(str, this);
super.onCreate(paramBundle);
}
其他函數的聲明與此相似,這個類庫關於生命周期函數的處理就不如表麵看的如此簡單,它在實現原有Activity功能的時候,同時提供了用ActivityOverride的回調函數。細細地考慮一下,這點給了我們一個拓展的接口。另外如果什麼東西都用dexmark寫,那就太煩了。
寫在最後
我們的分析就到這裏。這個類庫目前也就支持Activity、receiver(最新的實驗分支支持)而已,對於Service還是不支持的。
拓展閱讀
作者:kissazi2
出處:https://www.cnblogs.com/kissazi2/
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
轉載:https://www.cnblogs.com/kissazi2/p/4526055.html
最後更新:2017-11-22 00:34:17