国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

插件化開發---DroidPlugin對廣播的管理

2019-11-09 18:43:38
字體:
來源:轉載
供稿:網友

回想一下我們日常開發的時候是如何使用BroadcastReceiver的:注冊, 發送和接收;因此,要實現BroadcastReceiver的插件化就這三種操作提供支持;接下來我們將一步步完成這個過程。 我們可以注冊一個BroadcastReceiver然后接收我們感興趣的廣播,也可以給某有緣人發出某個廣播;因此,我們對源碼的分析按照兩條路線展開: 注冊過程 不論是靜態廣播還是動態廣播,在使用之前都是需要注冊的;動態廣播的注冊需要借助Context類的registerReceiver方法,而靜態廣播的注冊直接在AndroidManifest.xml中聲明即可;我們首先分析一下動態廣播的注冊過程。 Context類的registerReceiver的真正實現在ContextImpl里面,而這個方法間接調用了registerReceiverInternal,源碼如下:

@Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { return registerReceiverInternal(receiver, getUserId(), filter, broadcastPermission, scheduler, getOuterContext()); } PRivate Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context) { IIntentReceiver rd = null; if (receiver != null) { if (mPackageInfo != null && context != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } rd = mPackageInfo.getReceiverDispatcher( receiver, context, scheduler, mMainThread.getInstrumentation(), true); } else { if (scheduler == null) { scheduler = mMainThread.getHandler(); } rd = new LoadedApk.ReceiverDispatcher( receiver, context, scheduler, null, true).getIIntentReceiver(); } } try { return ActivityManagerNative.getDefault().registerReceiver( mMainThread.getapplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId); } catch (RemoteException e) { return null; } }

可以看到,BroadcastReceiver的注冊也是通過AMS完成的;在進入AMS跟蹤它的registerReceiver方法之前,我們先弄清楚這個IIntentReceiver類型的變量rd是什么。

public interface IIntentReceiver extends IInterfacepublic interface IInterface{ /** * Retrieve the Binder object associated with this interface. * You must use this instead of a plain cast, so that proxy objects * can return the correct result. */ public IBinder asBinder();}

這個類是通過AIDL工具生成的,它是一個Binder對象,因此可以用來跨進程傳輸; 由于廣播的分發過程是在AMS中進行的,而AMS所在的進程和BroadcastReceiver所在的進程不一樣,因此要把廣播分發到BroadcastReceiver具體的進程需要進行跨進程通信,這個通信的載體就是IIntentReceiver類。另外,IIntentReceiver是一個接口,從上述代碼中可以看出,它的實現類為LoadedApk.ReceiverDispatcher。

AMS類的registerReceiver方法代碼有點多,這里不一一解釋了,感興趣的話可以自行查閱;這個方法主要做了以下兩件事:

1、對發送者的身份和權限做出一定的校檢 2、把這個BroadcastReceiver以BroadcastFilter的形式存儲在AMS的mReceiverResolver變量中,供后續使用。

就這樣,被傳遞過來的BroadcastReceiver已經成功地注冊在系統之中,能夠接收特定類型的廣播了;

那么注冊在AndroidManifest.xml中的靜態廣播是如何被系統感知的呢??? 在 插件加載機制 中我們知道系統會通過PackageParser解析Apk中的AndroidManifest.xml文件,因此我們有理由認為,系統會在解析AndroidMafest.xml的標簽(也即靜態注冊的廣播)的時候保存相應的信息;而Apk的解析過程是在PMS中進行的,因此靜態注冊廣播的信息存儲在PMS中。

發送和接收過程

發送過程 就是一句context.sendBroadcast(),我們順藤摸瓜,跟蹤這個方法。前文也提到過,Context中方法的調用都會委托到ContextImpl這個類,我們直接看ContextImpl對這個方法的實現:

@Override public void sendBroadcast(Intent intent) { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false, getUserId()); } catch (RemoteException e) { } }

嗯,發送廣播也是通過AMS進行的,我們直接查看ActivityManagerService類的broadcastIntent方法,這個方法僅僅是調用了broadcastIntentLocked方法, 某個廣播被發送之后,AMS會找出所有注冊過的BroadcastReceiver中與這個廣播匹配的接收者,然后將這個廣播分發給相應的接收者處理。

匹配過程 某一條廣播被發出之后,并不是阿貓阿狗都能接收它并處理的;BroadcastReceiver可能只對某些類型的廣播感興趣,因此它也只能接收和處理這種特定類型的廣播;在broadcastIntentLocked方法內部有如下代碼:

receivers = collectReceiverComponents(intent, resolvedType, callingUid, users); registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false, userId);

讀者可以自行跟蹤這兩個方法的代碼,過程比較簡單,我這里直接給出結論:

receivers是對這個廣播感興趣的靜態BroadcastReceiver列表;collectReceiverComponents 通過PackageManager獲取了與這個廣播匹配的靜態BroadcastReceiver信息;這里也證實了我們在分析BroadcasrReceiver注冊過程中的推論——靜態BroadcastReceiver的注冊過程的確實在PMS中進行的。mReceiverResolver存儲了動態注冊的BroadcastReceiver的信息;還記得這個mReceiverResolver嗎?我們在分析動態廣播的注冊過程中發現,動態注冊的BroadcastReceiver的相關信息最終存儲在此對象之中;在這里,通過mReceiverResolver對象匹配出了對應的BroadcastReceiver供進一步使用。

現在系統通過PMS拿到了所有符合要求的靜態BroadcastReceiver,然后從AMS中獲取了符合要求的動態BroadcastReceiver;因此接下來的工作非常簡單:喚醒這些廣播接受者。簡單來說就是回調它們的onReceive方法。

接收過程 通過上文的分析過程我們知道,在AMS的broadcastIntentLocked方法中找出了符合要求的所有BroadcastReceiver;接下來就需要把這個廣播分發到這些接收者之中。在broadcastIntentLocked方法的后半部分有如下代碼:

BroadcastQueue queue = broadcastQueueForIntent(intent);BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId);boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);if (!replaced) { queue.enqueueOrderedBroadcastLocked(r); queue.scheduleBroadcastsLocked();}

首先創建了一個BroadcastRecord代表此次發送的這條廣播,然后把它丟進一個隊列,最后通過scheduleBroadcastsLocked通知隊列對廣播進行處理。

在BroadcastQueue中通過Handle調度了對于廣播處理的消息,調度過程由processNextBroadcast方法完成,而這個方法通過performReceiveLocked最終調用了IIntentReceiver的performReceive方法。

這個IIntentReceiver正是在廣播注冊過程中由App進程提供給AMS進程的Binder對象,現在AMS通過這個Binder對象進行ipC調用通知廣播接受者所在進程完成余下操作。在上文我們分析廣播的注冊過程中提到過,這個IItentReceiver的實現是LoadedApk.ReceiverDispatcher;我們查看這個對象的performReceive方法,源碼如下:

public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { Args args = new Args(intent, resultCode, data, extras, ordered, sticky, sendingUser); if (!mActivityThread.post(args)) { if (mRegistered && ordered) { IActivityManager mgr = ActivityManagerNative.getDefault(); args.sendFinished(mgr); } }}

這個方法創建了一個Args對象,然后把它post到了mActivityThread這個Handler中;我們查看Args類的run方法: 我們看到了相應BroadcastReceiver的onReceive回調;因此,廣播的工作原理到這里就水落石出了;

BroadcastReceiver有一個IntentFilter的概念,也就是說,每一個BroadcastReceiver只對特定的Broadcast感興趣;而且,AMS在進行廣播分發的時候,也會對這些BroadcastReceiver與發出的廣播進行匹配,只有Intent匹配的Receiver才能收到廣播;在分析源碼的時候也提到了這個匹配過程。如果我們嘗試用替身Receiver解決靜態注冊的問題,那么它的IntentFilter該寫什么?我們無法預料插件中靜態注冊的Receiver會使用什么類型的IntentFilter,就算我們在AndroidManifest.xml中聲明替身也沒有用——我們壓根兒收不到與我們的IntentFilter不匹配的廣播。其實,我們對于Activity的處理方式也有這個問題;如果你嘗試用IntentFilter的方式啟動Activity,這并不能成功;

可以把靜態廣播當作動態廣播處理

既然都是廣播,它們的功能都是訂閱一個特定的消息然后執行某個特定的操作,我們完全可以把插件中的靜態廣播全部注冊為動態廣播,這樣就解決了靜態廣播的問題。

靜態廣播非靜態的實現 要把插件中的靜態BroadcastReceiver當作動態BroadcastReceiver處理,我們首先得知道插件中到底注冊了哪些廣播;這個過程歸根結底就是獲取AndroidManifest.xml中的標簽下面的內容,我們可以選擇手動解析xml文件;這里我們選擇使用系統的 PackageParser 幫助解析,這種方式在之前的 [插件加載過程][] 中也用到過,如果忘記了可以溫習一下,我們要解析apk的的信息,可以使用PackageParser的generateActivityInfo方法。

知道這一點之后,代碼就比較簡單了;使用反射調用相應的隱藏接口,并且在必要的時候構造相應參數的方式我們在插件化系列文章中已經講述過很多,相信讀者已經熟練,這里就不贅述,直接貼代碼:

注冊 我們已經解析得到了插件中靜態注冊的BroadcastReceiver的信息,現在我們只需要把這些靜態廣播動態注冊一遍就可以了;但是,由于BroadcastReceiver的實現類存在于插件之后,我們需要手動用ClassLoader來加載它;這一點在 插件加載機制 已有講述,不啰嗦了。

、、、、、、、、、、、、、、、、、、、、代碼區、、、、、、、、、、、、、、、、、、、、、、、 首先是注冊2個靜態廣播的apk MainActivity什么都沒有做,只有一個空的布局文件

package com.wang.myapplication;import android.app.Activity;import android.content.Context;import android.content.Intent;import android.net.Uri;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } // 這個方法比onCreate調用早; 在這里Hook比較好. @Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(newBase); }}

Receiver1

package com.wang.myapplication;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.widget.Toast;public class Receiver1 extends BroadcastReceiver { static final String ACTION = "com.weishu.upf.demo.app2.PLUGIN_ACTION"; public void onReceive(Context paramContext, Intent paramIntent) { Toast.makeText(paramContext, "我是插件, 主程序收到請回答!", 0).show(); paramContext.sendBroadcast(new Intent("com.weishu.upf.demo.app2.PLUGIN_ACTION")); }}

配置文件如下

<receiver android:name="com.wang.myapplication.Receiver1"> <intent-filter> <action android:name="com.wang.myapplication.Receiver1"></action> </intent-filter> </receiver> <receiver android:name="com.wang.myapplication.Receiver2"></receiver>

然后將其打包成apk放到到另一個項目的資源文件下,我們繼續看另外一個項目的結構 這里寫圖片描述

MainActivity

package com.weishu.upf.receiver_management.app;import java.io.File;import android.app.Activity;import android.app.ActivityManagerNative;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.Toast;public class MainActivity extends Activity { // 發送廣播到插件之后, 插件如果受到, 那么會回傳一個ACTION 為這個值的廣播; static final String ACTION = "com.weishu.upf.demo.app2.PLUGIN_ACTION"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Button t = new Button(this); setContentView(t); t.setText("send broadcast to plugin: demo"); t.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "插件插件!收到請回答!!", Toast.LENGTH_SHORT).show(); sendBroadcast(new Intent("com.wang.myapplication.Receiver1")); } }); Utils.extractAssets(this, "test.apk"); File testPlugin = getFileStreamPath("test.apk"); try { ReceiverHelper.preLoadReceiver(this, testPlugin); Log.i(getClass().getSimpleName(), "hook success"); } catch (Exception e) { throw new RuntimeException("receiver load failed", e); } // 注冊插件收到我們發送的廣播之后, 回傳的廣播 registerReceiver(mReceiver, new IntentFilter(ACTION)); } BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "插件插件,我是主程序,握手完成!", Toast.LENGTH_SHORT).show(); } }; @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(mReceiver); }}

ReceiverHelper

package com.weishu.upf.receiver_management.app;import java.io.File;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.HashMap;import java.util.List;import java.util.Map;import android.content.BroadcastReceiver;import android.content.Context;import android.content.IntentFilter;import android.content.pm.ActivityInfo;import android.content.pm.PackageManager;import android.util.Log;public final class ReceiverHelper { private static final String TAG = "ReceiverHelper"; public static Map<ActivityInfo, List<? extends IntentFilter>> sCache = new HashMap<ActivityInfo, List<? extends IntentFilter>>(); /** * 解析Apk文件中的 <receiver>, 并存儲起來 * * @param apkFile * @throws Exception */ private static void parserReceivers(File apkFile) throws Exception { Class<?> packageParserClass = Class.forName("android.content.pm.PackageParser"); Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage", File.class, int.class); Object packageParser = packageParserClass.newInstance(); // 首先調用parsePackage獲取到apk對象對應的Package對象 Object packageObj = parsePackageMethod.invoke(packageParser, apkFile, PackageManager.GET_RECEIVERS); // 讀取Package對象里面的receivers字段,注意這是一個 List<Activity> (沒錯,底層把<receiver>當作<activity>處理) // 接下來要做的就是根據這個List<Activity> 獲取到Receiver對應的 ActivityInfo (依然是把receiver信息用activity處理了) Field receiversField = packageObj.getClass().getDeclaredField("receivers"); List receivers = (List) receiversField.get(packageObj); // 調用generateActivityInfo 方法, 把PackageParser.Activity 轉換成 Class<?> packageParser$ActivityClass = Class.forName("android.content.pm.PackageParser$Activity"); Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState"); Class<?> userHandler = Class.forName("android.os.UserHandle"); Method getCallingUserIdMethod = userHandler.getDeclaredMethod("getCallingUserId"); int userId = (Integer) getCallingUserIdMethod.invoke(null); Object defaultUserState = packageUserStateClass.newInstance(); Class<?> componentClass = Class.forName("android.content.pm.PackageParser$Component"); Field intentsField = componentClass.getDeclaredField("intents"); // 需要調用 android.content.pm.PackageParser#generateActivityInfo(android.content.pm.ActivityInfo, int, android.content.pm.PackageUserState, int) Method generateReceiverInfo = packageParserClass.getDeclaredMethod("generateActivityInfo", packageParser$ActivityClass, int.class, packageUserStateClass, int.class); // 解析出 receiver以及對應的 intentFilter for (Object receiver : receivers) { ActivityInfo info = (ActivityInfo) generateReceiverInfo.invoke(packageParser, receiver, 0, defaultUserState, userId); List<? extends IntentFilter> filters = (List<? extends IntentFilter>) intentsField.get(receiver); sCache.put(info, filters); } } public static void preLoadReceiver(Context context, File apk) throws Exception { parserReceivers(apk); ClassLoader cl = null; for (ActivityInfo activityInfo : ReceiverHelper.sCache.keySet()) { Log.i(TAG, "preload receiver:" + activityInfo.name); List<? extends IntentFilter> intentFilters = ReceiverHelper.sCache.get(activityInfo); if (cl == null) { cl = CustomClassLoader.getPluginClassLoader(apk, activityInfo.packageName); } // 把解析出來的每一個靜態Receiver都注冊為動態的 for (IntentFilter intentFilter : intentFilters) { BroadcastReceiver receiver = (BroadcastReceiver) cl.loadClass(activityInfo.name).newInstance(); context.registerReceiver(receiver, intentFilter); } } }}

CustomClassLoader

package com.weishu.upf.receiver_management.app;import java.io.File;import java.io.IOException;import dalvik.system.DexClassLoader;public class CustomClassLoader extends DexClassLoader { public CustomClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) { super(dexPath, optimizedDirectory, libraryPath, parent); } /** * 便利方法: 獲取插件的ClassLoader, 能夠加載指定的插件中的類 * * @param plugin * @param packageName * @return * @throws IOException */ public static CustomClassLoader getPluginClassLoader(File plugin, String packageName) throws IOException { return new CustomClassLoader(plugin.getPath(), Utils.getPluginOptDexDir(packageName).getPath(), Utils.getPluginLibDir(packageName).getPath(), UPFApplication.getContext().getClassLoader()); }}

Utils

package com.weishu.upf.receiver_management.app;import java.io.Closeable;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import android.content.Context;import android.content.res.AssetManager;public class Utils { /** * 把Assets里面得文件復制到 /data/data/files 目錄下 * * @param context * @param sourceName */ public static void extractAssets(Context context, String sourceName) { AssetManager am = context.getAssets(); InputStream is = null; FileOutputStream fos = null; try { is = am.open(sourceName); File extractFile = context.getFileStreamPath(sourceName); fos = new FileOutputStream(extractFile); byte[] buffer = new byte[1024]; int count = 0; while ((count = is.read(buffer)) > 0) { fos.write(buffer, 0, count); } fos.flush(); } catch (IOException e) { e.printStackTrace(); } finally { closeSilently(is); closeSilently(fos); } } /** * 待加載插件經過opt優化之后存放odex得路徑 */ public static File getPluginOptDexDir(String packageName) { return enforceDirExists(new File(getPluginBaseDir(packageName), "odex")); } /** * 插件得lib庫路徑, 這個demo里面沒有用 */ public static File getPluginLibDir(String packageName) { return enforceDirExists(new File(getPluginBaseDir(packageName), "lib")); } // -------------------------------------------------------------------------- private static void closeSilently(Closeable closeable) { if (closeable == null) { return; } try { closeable.close(); } catch (Throwable e) { // ignore } } private static File sBaseDir; // 需要加載得插件得基本目錄 /data/data/<package>/files/plugin/ private static File getPluginBaseDir(String packageName) { if (sBaseDir == null) { sBaseDir = UPFApplication.getContext().getFileStreamPath("plugin"); enforceDirExists(sBaseDir); } return enforceDirExists(new File(sBaseDir, packageName)); } private static synchronized File enforceDirExists(File sBaseDir) { if (!sBaseDir.exists()) { boolean ret = sBaseDir.mkdir(); if (!ret) { throw new RuntimeException("create dir " + sBaseDir + "failed"); } } return sBaseDir; }}

UPFApplication

package com.weishu.upf.receiver_management.app;import android.app.Application;import android.content.Context;public class UPFApplication extends Application { private static Context sContext; @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); sContext = base; } public static Context getContext() { return sContext; }}

最后看這個項目在data/data/目錄下的結構 這里寫圖片描述


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 克什克腾旗| 南宫市| 化州市| 盱眙县| 洛宁县| 专栏| 阿瓦提县| 石城县| 寻甸| 瑞安市| 城口县| 新巴尔虎左旗| 永清县| 扬中市| 贺州市| 阿瓦提县| 麦盖提县| 东兰县| 义乌市| 伊金霍洛旗| 鸡东县| 澄江县| 莆田市| 墨江| 涟源市| 松阳县| 聊城市| 融水| 绥芬河市| 神木县| 铜陵市| 高雄县| 调兵山市| 韶山市| 石门县| 昌宁县| 拜城县| 上杭县| 永昌县| 依兰县| 高州市|