BroadcastReceiver插件化解決方案
- 2019 年 10 月 7 日
- 筆記
1.靜態廣播和動態廣播僅區別於註冊方式的不同。靜態廣播的註冊資訊保存在PMS中,動態廣播的註冊資訊保存在AMS中
2.發送廣播,也就是Context的sendBroadcast方法,最終會調用AMN.getDefault().broadcastIntent,把要發送的廣播告訴AMS;
AMS在收到上述資訊後,搜索AMS和PMS中保存的廣播,看哪些廣播符合條件,然後通知App進程啟動這些廣播,也就是調用這些廣播的onReceive方法
3.無論發送廣播還是接受廣播,都攜帶一個篩選條件:intent-filter。
<receiver android:name=".MyReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="baobao2"/> </receiver>
MyReceiver myReceiver = new MyReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("baobao2"); registerReceiver(myReceiver,intentFilter);
***動態廣播的插件化解決方案***
使用前面介紹的dex合併技術,插件中的動態廣播就可以被宿主App正常調用了
***靜態廣播的插件化解決方案***
1)PMS只能讀取宿主App的AndroidManifest文件,讀取其中的靜態廣播並註冊。我們可以通過反射,手動控制PMS讀取插件的AndroidManifest中聲明的靜態廣播列表
2)遍歷這個靜態廣播列表。使用插件的classLoader載入列表中的每個廣播類,實例化成一個對象,然後作為動態廣播註冊到AMS中
public final class ReceiverHelper { private static final String TAG = "ReceiverHelper"; /** * 解析插件Apk文件中的 <receiver>, 並存儲起來 * * @param apkFile * @throws Exception */ public static void preLoadReceiver(Context context, File apkFile) { // 首先調用parsePackage獲取到apk對象對應的Package對象 Object packageParser = RefInvoke.createObject("android.content.pm.PackageParser"); Class[] p1 = {File.class, int.class}; Object[] v1 = {apkFile, PackageManager.GET_RECEIVERS}; Object packageObj = RefInvoke.invokeInstanceMethod(packageParser, "parsePackage", p1, v1); // 讀取Package對象裡面的receivers欄位,注意這是一個 List<Activity> (沒錯,底層把<receiver>當作<activity>處理) // 接下來要做的就是根據這個List<Activity> 獲取到Receiver對應的 ActivityInfo (依然是把receiver資訊用activity處理了) List receivers = (List) RefInvoke.getFieldObject(packageObj, "receivers"); for (Object receiver : receivers) { registerDynamicReceiver(context, receiver); } } // 解析出 receiver以及對應的 intentFilter // 手動註冊Receiver public static void registerDynamicReceiver(Context context, Object receiver) { //取出receiver的intents欄位 List<? extends IntentFilter> filters = (List<? extends IntentFilter>) RefInvoke.getFieldObject( "android.content.pm.PackageParser$Component", receiver, "intents"); try { // 把解析出來的每一個靜態Receiver都註冊為動態的 for (IntentFilter intentFilter : filters) { ActivityInfo receiverInfo = (ActivityInfo) RefInvoke.getFieldObject(receiver, "info"); BroadcastReceiver broadcastReceiver = (BroadcastReceiver) RefInvoke.createObject(receiverInfo.name); context.registerReceiver(broadcastReceiver, intentFilter); } } catch (Exception e) { e.printStackTrace(); } } }
***不啟動App和插件中的靜態廣播通訊***
在宿主的androidmanifest中註冊佔位StubReceiver
<application android:name="jianqiang.com.receiverhook.UPFApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".StubReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="jianqiang1" /> </intent-filter> <intent-filter> <action android:name="jianqiang2" /> </intent-filter> <intent-filter> <action android:name="jianqiang3" /> </intent-filter> <intent-filter> <action android:name="jianqiang4" /> </intent-filter> <intent-filter> <action android:name="jianqiang5" /> </intent-filter> <intent-filter> <action android:name="jianqiang6" /> </intent-filter> <intent-filter> <action android:name="jianqiang7" /> </intent-filter> <intent-filter> <action android:name="jianqiang8" /> </intent-filter> <intent-filter> <action android:name="jianqiang9" /> </intent-filter> <intent-filter> <action android:name="jianqiang10" /> </intent-filter> </receiver> </application>
插件的androidmanifest中註冊
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <receiver android:name=".MyReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="baobao" /> </intent-filter> <meta-data android:name="oldAction" android:value="jianqiang1"></meta-data> </receiver> <receiver android:name=".MyReceiver2" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="baobao2" /> </intent-filter> <meta-data android:name="oldAction" android:value="jianqiang2"></meta-data> </receiver> </application>
具體流程是宿主清單文件中查找jianqiang1,然後到插件的清單文件中查找jianqiang1,最後找到jianqiang1對應的baobao,這才是真正要註冊的廣播
實現邏輯如下
public final class ReceiverHelper { private static final String TAG = "ReceiverHelper"; /** * 解析插件Apk文件中的 <receiver>, 並存儲起來 * * @param apkFile * @throws Exception */ public static void preLoadReceiver(Context context, File apkFile) { // 首先調用parsePackage獲取到apk對象對應的Package對象 Object packageParser = RefInvoke.createObject("android.content.pm.PackageParser"); Class[] p1 = {File.class, int.class}; Object[] v1 = {apkFile, PackageManager.GET_RECEIVERS}; Object packageObj = RefInvoke.invokeInstanceMethod(packageParser, "parsePackage", p1, v1); String packageName = (String)RefInvoke.getFieldObject(packageObj, "packageName"); // 讀取Package對象裡面的receivers欄位,注意這是一個 List<Activity> (沒錯,底層把<receiver>當作<activity>處理) // 接下來要做的就是根據這個List<Activity> 獲取到Receiver對應的 ActivityInfo (依然是把receiver資訊用activity處理了) List receivers = (List) RefInvoke.getFieldObject(packageObj, "receivers"); try { for (Object receiver : receivers) { Bundle metadata = (Bundle)RefInvoke.getFieldObject( "android.content.pm.PackageParser$Component", receiver, "metaData"); String oldAction = metadata.getString("oldAction"); // 解析出 receiver以及對應的 intentFilter List<? extends IntentFilter> filters = (List<? extends IntentFilter>) RefInvoke.getFieldObject( "android.content.pm.PackageParser$Component", receiver, "intents"); // 把解析出來的每一個靜態Receiver都註冊為動態的 for (IntentFilter intentFilter : filters) { ActivityInfo receiverInfo = (ActivityInfo) RefInvoke.getFieldObject(receiver, "info"); BroadcastReceiver broadcastReceiver = (BroadcastReceiver) RefInvoke.createObject(receiverInfo.name); context.registerReceiver(broadcastReceiver, intentFilter); String newAction = intentFilter.getAction(0); ReceiverManager.pluginReceiverMappings.put(oldAction, newAction); } } } catch (Exception e) { e.printStackTrace(); } } } public class StubReceiver extends BroadcastReceiver { public StubReceiver() { } @Override public void onReceive(Context context, Intent intent) { String newAction = intent.getAction(); if(ReceiverManager.pluginReceiverMappings.containsKey(newAction)) { String oldAction = ReceiverManager.pluginReceiverMappings.get(newAction); context.sendBroadcast(new Intent(oldAction)); } } }
缺點是要為StubReceiver配置幾百個Action,無法避免
–摘自《android插件化開發指南》