Android外掛化研究代ACTIVITY註冊
最近在研究Android應用的外掛化開發,看了好幾個相關的開源專案。外掛化都是在解決以下幾個問題:
如何把外掛apk中的程式碼和資源載入到當前虛擬機器。
如何把外掛apk中的四大元件註冊到程式中。
如何防止外掛apk中的資源和宿主apk中的資源引用衝突。
在上篇文章中我研究了的問題(文字、圖片、佈局等),前面兩篇文章解決了外掛化研究的第一個問題。本篇文章開始研究第二個問題:“註冊”外掛中的四大元件。
在安裝apk的時候,應用管理服務PackageManagerService會解析apk,解析應用程式配置檔案AndroidManifest.xml,並從裡面得到得到應用得到應用程式的元件Activity、Service、Broadcast Receiver和Content Provider等資訊,對應用的每個元件“登記”,“登記”之後,在啟動某個Activity過程在ASM執行時對比“登記”然後“查有此人”允許後續的啟動行為。詳細過程可以參考。然而,外掛apk並沒有進行安裝,自然apk中定義的四大元件也沒有進行“登記”,那麼問題來了:以Activity為例,如何啟動外掛中的Acivity?大體兩種思路。
一、 代理方式實現。
宿主端實現一個 PluginProxyActivity,使用這個Activity代理外掛中的Activity的重要事務,例如生命週期呼叫、contentview設定、Activity跳轉等事務。PluginProxyActivity註冊在宿主中,啟動外掛中的Activity實際就是啟動PluginProxyActivity,只是載入的佈局和方法邏輯不一樣而已。百度的外掛框架就是使用的這種方式。
二、“佔坑”方式實現。
啟動Activity是一個複雜的過程,有很多環節:Activity.startActivity()->Activity.startActivityForResult()->Instrument.excuteStartActivity()->ASM.startActivity()。大概又這麼幾個環節,詳細瞭解可以參考文章:。 所謂“佔坑”在宿主端的AndroidManifest.xml註冊一個不存在的Activity,可以取名為StubActivity,同樣啟動外掛的Activity都是啟動StubActivity,然後在啟動Activity的某個環節,我們找個“臨時”演員來代替StubActivity,這個臨時演員就是外掛中定義的Activity,這叫“瞞天過海”。如何找“臨時”演員,這個過程又有很多種實現手段,DroidPlugin、dwarf等框架實現手段各有不同,詳細後續文章再討論。
簡單解釋了兩種思路,本文先用demo來說說如何實現第一種思路(後續文章研究下第二思路)。
PluginProxyActivity的實現
一、重寫setContentView(int layoutResID)方法,使用外掛的AssertManager載入佈局資源。該方法提供給外掛Activity呼叫。
@Override public void setContentView(int layoutResID) { // do something plugin need Resources resources = PluginManager.getInstace().getResources(); XmlPullParser xmlResourceParser = resources.getLayout(layoutResID); View viewFromPlugin = LayoutInflater.from(this).inflate(xmlResourceParser, null); setContentView(viewFromPlugin); }
二、 重寫onCreate(Bundle savedInstanceState)方法,在這個方法中透過外掛的Activity的類名,利用反射例項化外掛Activity物件,並呼叫其onCreate方法。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String className = getIntent().getStringExtra("class"); initPluginInstance(className); invokePluginOnCreate(savedInstanceState); } private void initPluginInstance(String className) { try { pluginClass = PluginManager.getInstace().getCloassLoader().loadClass(className); Constructor> localConstructor = pluginClass.getConstructor(new Class[]{}); pluginInstance = localConstructor.newInstance(new Object[] {}); // 把當前的代理Activity注入到外掛中 Method setProxy = pluginClass.getMethod("setProxy", new Class[]{PluginProxyActivity.class}); setProxy.setAccessible(true); setProxy.invoke(pluginInstance, new Object[] { this }); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); } } private void invokePluginOnCreate(Bundle savedInstanceState) { try { Method onCreate = pluginClass.getDeclaredMethod("onCreate", new Class[]{Bundle.class}); onCreate.setAccessible(true); onCreate.invoke(pluginInstance, new Object[] { savedInstanceState }); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } }
三、 重寫其他生命週期函式並利用反射呼叫外掛Activity的對應的生命週期函式,例如onPause方法。
@Override protected void onPause() { super.onPause(); invokePluginOnPause(); } private void invokePluginOnPause() { try { Method onPause = pluginClass.getDeclaredMethod("onPause", new Class[]{}); onPause.setAccessible(true); onPause.invoke(pluginInstance, new Object[] {}); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } }
四、 過載startAcivity(String className)方法,也就是使用使用定製的startActivity方法來啟動外掛Activity啦。
public void startActivity(String className) { // do something plugin need Intent intent = new Intent(this,PluginProxyActivity.class); intent.putExtra("class",className); startActivity(intent); }
約定外掛Activity,外掛Activity基類:BasePluginActivity的實現
一、 提供public void setProxy(PluginProxyActivity proxyPluginAct)方法,以獲得PluginProxyActivity的引用。
/** * * @param proxyPluginAct * provided this method to invoke by reflect . inject proxy */ public void setProxy(PluginProxyActivity proxyPluginAct){ mProxy = proxyPluginAct; }
二、重寫setContentView(int layoutResID),呼叫proxy的setContentView(int layoutResID)方法。
/** * set layout to proxyActivity * @param layoutResID */ @Override public void setContentView(int layoutResID) { if (mProxy != null){ mProxy.setContentView(layoutResID); } else { super.setContentView(layoutResID); } }
三、 定製startActivity(String className)方法來啟動Activity,呼叫proxy的startActivity方法。
public void startActivity(String className) { mProxy.startActivity(className); }
最重要的demo
demo實現啦一個外掛的框架的最基本雛形,地址:,如果你有興趣,一定要star,日後研究研究。代理方式實現起來比較簡單,也比較好理解,但是有很多缺陷:一、外掛Activity不能使用this關鍵字,比如this.finish()方法是無效的,真正掌管生命週期的是proxy應該呼叫proxy.finish(),所以百度開源框架 dynamic-load-apk使用that指向proxy,約定外掛中使用that來代替this。二、 外掛Activity無法深度演繹真正的Activity元件,可能有些高階特性無法使用。
總之,不夠透明,外掛開發需要定義自己的規範。既然如此,有沒有更好的方案?當然有,後續文章繼續研究外掛化如何註冊元件的第二類思路:“佔坑”方式實現外掛Activity的註冊。.
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/151/viewspace-2814716/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Android外掛化原理(一)Activity外掛化Android
- Activity的外掛化(三)
- Activity的外掛化(一)
- Activity的外掛化(二)
- apisix~lua外掛開發與外掛註冊API
- Android進階(九)Activity外掛化和VirtualApk分析AndroidAPK
- Flutter在Android端註冊外掛流程原始碼解析FlutterAndroid原始碼
- C# 免註冊呼叫大漠外掛C#
- Android外掛化技術之旅 1 開篇 - 實現啟動外掛與呼叫外掛中的Activity和ServiceAndroid
- Android黑科技:如何啟動未註冊的ActivityAndroid
- k8s CSI 外掛註冊原理K8S
- Android免清單註冊啟動Activity Hook技術AndroidHook
- Activity外掛化原理第二種方案:Hook IActivityManagerHook
- Activity不用註冊?那就來Hook吧Hook
- Activity外掛化原理第一種方案:Hook InstrumentationHook
- Android外掛化開篇Android
- 淺析Android外掛化Android
- pr人像磨皮美容外掛:Beauty Box +註冊碼
- Android外掛化——VirtualAPK外掛框架接入專案AndroidAPK框架
- Deep Glow外掛下載 AE模擬真實輝光效果外掛 附註冊碼
- [Android]AAB外掛化架構Android架構
- [Android] 元件化 & 模組化 & 外掛化演進Android元件化
- [Android元件化]AAB外掛化架構Android元件化架構
- Drill官網文件翻譯六:儲存外掛的註冊
- hook 系統api啟動未註冊ActivityHookAPI
- 外掛代練何罪之有?
- vim外掛的安裝方式 -- vim註釋外掛和doxygen函式註釋生成外掛-ctrlp外掛-tabular等號對齊 外掛...函式
- Android 外掛化 動態升級Android
- Android外掛化原理解析——概要Android
- Android外掛化的一種方案Android
- Android外掛化(三):載入外掛apk中的Resource資源AndroidAPK
- PHP外掛系統的實現(四):實現註冊動作PHP
- cordova列印外掛備註
- laravel Modules外掛內定時任務執行,自定義命令註冊,外掛內資源釋出Laravel
- Android 外掛化原理入門筆記Android筆記
- Android 外掛化原理解析(1):概要Android
- 一種優雅的Golang的庫外掛註冊載入機制Golang
- Android 外掛化框架 DynamicLoadApk 原始碼分析Android框架APK原始碼