初始化部分
PluginManager.getInstance(base).init();
保證單例
private static volatile PluginManager sInstance = null;
public static PluginManager getInstance(Context base) {
if (sInstance == null) {
synchronized (PluginManager.class) {
if (sInstance == null)
sInstance = new PluginManager(base);
}
}
return sInstance;
}
private PluginManager(Context context) {
Context app = context.getApplicationContext();
if (app == null) {
this.mContext = context;
} else {
this.mContext = ((Application)app).getBaseContext();
}
prepare();
}
下面仔細(xì)按照這個方法的步驟分析初始化過程做了哪些事情
private void prepare() {
Systems.sHostContext = getHostContext();
this.hookInstrumentationAndHandler();//對Instrumentation和H類進(jìn)行hook
this.hookSystemServices();//對AMS代理也就是AMP對象進(jìn)行hook
hookDataBindingUtil();
}
private void hookInstrumentationAndHandler() {
try {
Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext);//[1]從Context開始hook
if (baseInstrumentation.getClass().getName().contains("lbe")) {
System.exit(0);
}
//[2]
final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation);
Object activityThread = ReflectUtil.getActivityThread(this.mContext);
ReflectUtil.setInstrumentation(activityThread, instrumentation);
ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
this.mInstrumentation = instrumentation;
} catch (Exception e) {
e.printStackTrace();
}
}
其中g(shù)etActivityThread這個方法中對ActivityThread進(jìn)行了獲取(通過反射),然后從ActivityThread中通過getInstrumentation方法得到Instrumentation對象,這個對象會跟蹤應(yīng)用內(nèi)application和activity生命周期,該對象的創(chuàng)建在ActivityThread::handleBindApplication函數(shù)中:
if (data.instrumentationName !=null) {
...
java.lang.ClassLoadercl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();
...
} else {
mInstrumentation =newInstrumentation();
}
[1]
public static Instrumentation getInstrumentation(Context base) {
if (getActivityThread(base) != null) {
try {
sInstrumentation = (Instrumentation) ReflectUtil.invoke(
sActivityThread.getClass(), sActivityThread, "getInstrumentation");
} catch (Exception e) {
e.printStackTrace();
}
}
return sInstrumentation;
}
[2]
這一部分代碼通過自定義一個Instrumentation將此類hook進(jìn)ActivityThread中從而達(dá)到跟蹤目的宜肉,同時下面代碼的參與是將mH類改變,mH類是AMS操作完成之后通知mH對當(dāng)前應(yīng)用進(jìn)程進(jìn)行改變的類蚁孔,比如說Activity的實例產(chǎn)生就要通過改mH進(jìn)行執(zhí)行。
//ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
public static void setHandlerCallback(Context base, Handler.Callback callback) {
try {
Object activityThread = getActivityThread(base);
//activityThread對象調(diào)用getHandler得到mH對象引用final H mH = new H();
Handler mainHandler = (Handler) ReflectUtil.invoke(activityThread.getClass(), activityThread, "getHandler", (Object[])null);
//將Handler中的mCallback進(jìn)行替換,替換的邏輯處理在VAInstrumentation中
ReflectUtil.setField(Handler.class, mainHandler, "mCallback", callback);
} catch (Exception e) {
e.printStackTrace();
}
}
我們此時要看一看Handler的mCallback起到什么作用
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
//一般情況下要執(zhí)行到mCallback的handleMessage方法完了再執(zhí)行handleMessage方法
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
也就是對應(yīng):
獲取下啟動的Activity科盛,然后判斷如果是插件的抱环,則對應(yīng)下主題
@Override
public boolean handleMessage(Message msg) {
//當(dāng)消息是LAUNCH_ACTIVITY的時候機(jī)型一些操作
if (msg.what == LAUNCH_ACTIVITY) {
// ActivityClientRecord r
Object r = msg.obj;//獲取ActivityClientRecord
try {
Intent intent = (Intent) ReflectUtil.getField(r.getClass(), r, "intent");//得到啟動意圖
intent.setExtrasClassLoader(VAInstrumentation.class.getClassLoader());//得到對應(yīng)類加載器
//得到ActivityInfo
ActivityInfo activityInfo = (ActivityInfo) ReflectUtil.getField(r.getClass(), r, "activityInfo");
//如果從插件啟動則進(jìn)行下面更換主題
if (PluginUtil.isIntentFromPlugin(intent)) {
int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
if (theme != 0) {
activityInfo.theme = theme;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
當(dāng)真正執(zhí)行LAUNCH_ACTIVITY的時候是這樣的
H.java
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.loadedApk = getLoadedApkNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
hookSystemServices()
這里是對IActivityManager進(jìn)行hook壳快,替換的對象是ActivityManagerProxy(插件內(nèi)的)
private void hookSystemServices() {
try {
Singleton<IActivityManager> defaultSingleton;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//得到ActivityManager中的IActivityManagerSingleton對象
defaultSingleton = (Singleton<IActivityManager>) ReflectUtil.getField(ActivityManager.class, null, "IActivityManagerSingleton");
} else {
defaultSingleton = (Singleton<IActivityManager>) ReflectUtil.getField(ActivityManagerNative.class, null, "gDefault");
}
//使用動態(tài)代理將動態(tài)代理產(chǎn)生的代理對象賦值給activityManagerProxy
IActivityManager activityManagerProxy = ActivityManagerProxy.newInstance(this, defaultSingleton.get());
// Hook IActivityManager from ActivityManagerNative
ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(), defaultSingleton, "mInstance", activityManagerProxy);
if (defaultSingleton.get() == activityManagerProxy) {
this.mActivityManager = activityManagerProxy;
}
} catch (Exception e) {
e.printStackTrace();
}
}
替換的原理是:對ActivityManager進(jìn)行hook途样,IActivityManagerSingleton.get()得到的就是IActivityManager的對象然后使用動態(tài)代理對其中的方法進(jìn)行攔截
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
上面這一部分主要是將當(dāng)前進(jìn)程通過startActivity,startService等等與AMS進(jìn)行通信的渠道進(jìn)行攔截濒憋,所以后面的startService等這些操作都需要經(jīng)過ActivityManagerProxy(插件框架中)進(jìn)行操作何暇。
下面進(jìn)入init方法,沒啥操作
public void init() {
mComponentsHandler = new ComponentsHandler(this);
RunUtil.getThreadPool().execute(new Runnable() {
@Override
public void run() {
doInWorkThread();
}
});
}
private void doInWorkThread() {
}
進(jìn)行加載插件
當(dāng)在MainActivity中執(zhí)行
this.loadPlugin(this);
private void loadPlugin(Context base) {
PluginManager pluginManager = PluginManager.getInstance(base);
File apk = new File(Environment.getExternalStorageDirectory(), "Test.apk");
if (apk.exists()) {
try {
pluginManager.loadPlugin(apk);
Log.i(TAG, "Loaded plugin from apk: " + apk);
} catch (Exception e) {
e.printStackTrace();
}
} else {
...
}
}
通過pluginManager.loadPlugin(apk);進(jìn)行裝載插件
public void loadPlugin(File apk) throws Exception {
//如果apk存在的話,創(chuàng)建加載插件類
LoadedPlugin plugin = LoadedPlugin.create(this, this.mContext, apk);
if (null != plugin) {
this.mPlugins.put(plugin.getPackageName(), plugin);
synchronized (mCallbacks) {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onAddedLoadedPlugin(plugin);
}
}
plugin.invokeApplication();
} else {
throw new RuntimeException("Can't load plugin which is invalid: " + apk.getAbsolutePath());
}
}
進(jìn)行創(chuàng)建
LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception {
this.mPluginManager = pluginManager;
this.mHostContext = context;
this.mLocation = apk.getAbsolutePath();//得到apk的路徑
//將APK進(jìn)行解析并執(zhí)行collectCertificates進(jìn)行證書操作
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;//meta-data對應(yīng)的操作
this.mPackageInfo = new PackageInfo();
this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo;
this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath();
if (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0) { // Android P Preview
this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures;
} else {
this.mPackageInfo.signatures = this.mPackage.mSignatures;
}
this.mPackageInfo.packageName = this.mPackage.packageName;
if (pluginManager.getLoadedPlugin(mPackageInfo.packageName) != null) {
throw new RuntimeException("plugin has already been loaded : " + mPackageInfo.packageName);
}
this.mPackageInfo.versionCode = this.mPackage.mVersionCode;
this.mPackageInfo.versionName = this.mPackage.mVersionName;
this.mPackageInfo.permissions = new PermissionInfo[0];
this.mPackageManager = new PluginPackageManager();
this.mPluginContext = new PluginContext(this);
this.mNativeLibDir = context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE);
this.mResources = createResources(context, apk);//生成了新的Resources對象凛驮,這個要單獨分析
//創(chuàng)建類加載器裆站,也要單獨分析
this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());
tryToCopyNativeLib(apk);//賦值native的lib庫
// Cache instrumentations
Map<ComponentName, InstrumentationInfo> instrumentations = new HashMap<ComponentName, InstrumentationInfo>();
for (PackageParser.Instrumentation instrumentation : this.mPackage.instrumentation) {
instrumentations.put(instrumentation.getComponentName(), instrumentation.info);
}
this.mInstrumentationInfos = Collections.unmodifiableMap(instrumentations);
this.mPackageInfo.instrumentation = instrumentations.values().toArray(new InstrumentationInfo[instrumentations.size()]);
// Cache activities
Map<ComponentName, ActivityInfo> activityInfos = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity activity : this.mPackage.activities) {
activityInfos.put(activity.getComponentName(), activity.info);
}
this.mActivityInfos = Collections.unmodifiableMap(activityInfos);
this.mPackageInfo.activities = activityInfos.values().toArray(new ActivityInfo[activityInfos.size()]);
// Cache services
Map<ComponentName, ServiceInfo> serviceInfos = new HashMap<ComponentName, ServiceInfo>();
for (PackageParser.Service service : this.mPackage.services) {
serviceInfos.put(service.getComponentName(), service.info);
}
this.mServiceInfos = Collections.unmodifiableMap(serviceInfos);
this.mPackageInfo.services = serviceInfos.values().toArray(new ServiceInfo[serviceInfos.size()]);
// Cache providers
Map<String, ProviderInfo> providers = new HashMap<String, ProviderInfo>();
Map<ComponentName, ProviderInfo> providerInfos = new HashMap<ComponentName, ProviderInfo>();
for (PackageParser.Provider provider : this.mPackage.providers) {
providers.put(provider.info.authority, provider.info);
providerInfos.put(provider.getComponentName(), provider.info);
}
this.mProviders = Collections.unmodifiableMap(providers);
this.mProviderInfos = Collections.unmodifiableMap(providerInfos);
this.mPackageInfo.providers = providerInfos.values().toArray(new ProviderInfo[providerInfos.size()]);
// Register broadcast receivers dynamically
Map<ComponentName, ActivityInfo> receivers = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity receiver : this.mPackage.receivers) {
receivers.put(receiver.getComponentName(), receiver.info);
BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
this.mHostContext.registerReceiver(br, aii);
}
}
this.mReceiverInfos = Collections.unmodifiableMap(receivers);
this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]);
}
上面這個構(gòu)造很重要,很重要很重要黔夭,這個負(fù)責(zé)解析apk里面內(nèi)容我們一步步分析
第一步:
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
public static final PackageParser.Package parsePackage(final Context context, final File apk, final int flags) throws PackageParser.PackageParserException {
if (Build.VERSION.SDK_INT >= 24) {
if (Build.VERSION.PREVIEW_SDK_INT == 0) {
return PackageParserV24.parsePackage(context, apk, flags);
} else {
return PackageParserPPreview.parsePackage(context, apk, flags);
}
} else if (Build.VERSION.SDK_INT >= 21) {
return PackageParserLollipop.parsePackage(context, apk, flags);
} else {
return PackageParserLegacy.parsePackage(context, apk, flags);
}
}
主要進(jìn)去一個宏胯,就知道原理
static final PackageParser.Package parsePackage(Context context, File apk, int flags) throws PackageParser.PackageParserException {
PackageParser parser = new PackageParser();
PackageParser.Package pkg = parser.parsePackage(apk, flags);
ReflectUtil.invokeNoException(PackageParser.class, null, "collectCertificates",
new Class[]{PackageParser.Package.class, int.class}, pkg, flags);
return pkg;
}
當(dāng)你點擊進(jìn)去PackageParser的構(gòu)造你會發(fā)現(xiàn)拋出一個異常,其實這里只是使用一個假的PackageParser本姥,只是包名和類名與系統(tǒng)中的一樣而已肩袍,所以當(dāng)apk運(yùn)行在系統(tǒng)中時候由于類的加載機(jī)制是雙親委托模型,優(yōu)先使用父節(jié)點的加載器婚惫,所以優(yōu)先加載系統(tǒng)中的PackageParser氛赐。這里調(diào)用parsePackage主要目的是解析AndroidManifest.xml,最后通過collectCertificates方法獲取應(yīng)用的簽名信息先舷,最后將解析完成的PackageParser.Package返回
第二步:
this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;//meta-data
this.mPackageInfo = new PackageInfo();
this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo;
this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath();
if (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0) { // Android P Preview
this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures;
} else {
this.mPackageInfo.signatures = this.mPackage.mSignatures;
}
this.mPackageInfo.packageName = this.mPackage.packageName;
//此插件一定沒加載過才可以
if (pluginManager.getLoadedPlugin(mPackageInfo.packageName) != null) {
throw new RuntimeException("plugin has already been loaded : " + mPackageInfo.packageName);
}
this.mPackageInfo.versionCode = this.mPackage.mVersionCode;
this.mPackageInfo.versionName = this.mPackage.mVersionName;
this.mPackageInfo.permissions = new PermissionInfo[0];
復(fù)制各種標(biāo)簽的信息艰管,有meta-data,版本號蒋川,版本名稱等等
第三步:
this.mPackageManager = new PluginPackageManager();
this.mPluginContext = new PluginContext(this);
this.mNativeLibDir = context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE);
this.mResources = createResources(context, apk);
this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());
tryToCopyNativeLib(apk);
@WorkerThread
private static Resources createResources(Context context, File apk) {
if (Constants.COMBINE_RESOURCES) {
//創(chuàng)建Resources對象
Resources resources = ResourcesManager.createResources(context, apk.getAbsolutePath());
ResourcesManager.hookResources(context, resources);
return resources;
} else {
Resources hostResources = context.getResources();
AssetManager assetManager = createAssetManager(context, apk);
return new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());
}
}
public static synchronized Resources createResources(Context hostContext, String apk) {
Resources hostResources = hostContext.getResources();//得到應(yīng)用原來的Resources
Resources newResources = null;
AssetManager assetManager;
try {
//得到AssetManager
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
assetManager = AssetManager.class.newInstance();
ReflectUtil.invoke(AssetManager.class, assetManager, "addAssetPath", hostContext.getApplicationInfo().sourceDir);
} else {
assetManager = hostResources.getAssets();
}
//將當(dāng)前apk執(zhí)行AssetManager的addAssetPath添加到搜索資源的路徑中
ReflectUtil.invoke(AssetManager.class, assetManager, "addAssetPath", apk);
List<LoadedPlugin> pluginList = PluginManager.getInstance(hostContext).getAllLoadedPlugins();
for (LoadedPlugin plugin : pluginList) {
ReflectUtil.invoke(AssetManager.class, assetManager, "addAssetPath", plugin.getLocation());
}
//創(chuàng)建一個新的Resources
if (isMiUi(hostResources)) {
newResources = MiUiResourcesCompat.createResources(hostResources, assetManager);
} else if (isVivo(hostResources)) {
newResources = VivoResourcesCompat.createResources(hostContext, hostResources, assetManager);
} else if (isNubia(hostResources)) {
newResources = NubiaResourcesCompat.createResources(hostResources, assetManager);
} else if (isNotRawResources(hostResources)) {
newResources = AdaptationResourcesCompat.createResources(hostResources, assetManager);
} else {
// is raw android resources
newResources = new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());
}
// lastly, sync all LoadedPlugin to newResources
for (LoadedPlugin plugin : pluginList) {
plugin.updateResources(newResources);
}
} catch (Exception e) {
e.printStackTrace();
}
return newResources;
}
要想獲得資源文件必須得到一個Resource對象牲芋,想要獲得插件的資源文件,必須得到一個插件的Resource對象捺球,在android.content.res.AssetManager.java中包含一個私有方法addAssetPath缸浦。只需要將apk的路徑作為參數(shù)傳入,就可以獲得對應(yīng)的AssetsManager對象氮兵,從而創(chuàng)建一個Resources對象裂逐,然后就可以從Resource對象中訪問apk中的資源了。
既然現(xiàn)在可以加載插件中資源的Resource對象得到了胆剧,下一步就是需要將Resource對象hook進(jìn)去絮姆。看代碼
public static void hookResources(Context base, Resources resources) {
try {
ReflectUtil.setField(base.getClass(), base, "mResources", resources);
Object loadedApk = ReflectUtil.getPackageInfo(base);
ReflectUtil.setField(loadedApk.getClass(), loadedApk, "mResources", resources);
Object activityThread = ReflectUtil.getActivityThread(base);
Object resManager = ReflectUtil.getField(activityThread.getClass(), activityThread, "mResourcesManager");
if (Build.VERSION.SDK_INT < 24) {
Map<Object, WeakReference<Resources>> map = (Map<Object, WeakReference<Resources>>) ReflectUtil.getField(resManager.getClass(), resManager, "mActiveResources");
Object key = map.keySet().iterator().next();
map.put(key, new WeakReference<>(resources));
} else {
// still hook Android N Resources, even though it's unnecessary, then nobody will be strange.
Map map = (Map) ReflectUtil.getFieldNoException(resManager.getClass(), resManager, "mResourceImpls");
Object key = map.keySet().iterator().next();
Object resourcesImpl = ReflectUtil.getFieldNoException(Resources.class, resources, "mResourcesImpl");
map.put(key, new WeakReference<>(resourcesImpl));
}
} catch (Exception e) {
e.printStackTrace();
}
}
代碼可以看出來需要hook的有兩個地方一個是Context中的mResources(定義在ContextImpl中的private @NonNull Resources mResources;)一個是PackageInfo中的mResources秩霍,但是這里我們設(shè)置PackageInfo這個有問題篙悯,老版本中ContextImpl中是有這個的,但是8.0之后沒有這個屬性铃绒。取而代之的是:
final @NonNull LoadedApk mLoadedApk;
所以這兩句代碼應(yīng)該加以區(qū)分版本
Object loadedApk = ReflectUtil.getPackageInfo(base);
ReflectUtil.setField(loadedApk.getClass(), loadedApk, "mResources", resources);
之后就是把mResourcesImpl也hook進(jìn)Resources去
第四步
this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());
private static ClassLoader createClassLoader(Context context, File apk, File libsDir, ClassLoader parent) {
//獲取dex目錄
File dexOutputDir = context.getDir(Constants.OPTIMIZE_DIR, Context.MODE_PRIVATE);
//得到絕對路徑
String dexOutputPath = dexOutputDir.getAbsolutePath();
DexClassLoader loader = new DexClassLoader(apk.getAbsolutePath(), dexOutputPath, libsDir.getAbsolutePath(), parent);
if (Constants.COMBINE_CLASSLOADER) {
try {
DexUtil.insertDex(loader);
} catch (Exception e) {
e.printStackTrace();
}
}
return loader;
}
DexClassLoader先解釋下參數(shù):
- dexpath為jar或apk文件目錄鸽照。
- optimizedDirectory為優(yōu)化dex緩存目錄。
- libraryPath包含native lib的目錄路徑颠悬。
- parent父類加載器矮燎。
不熟悉類加載器的定血,這里我從源碼角度分析一下類DexClassLoader:
public classDexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
}
public BaseDexClassLoader(String dexPath, File optimizedDirectory,String libraryPath, ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
protected ClassLoader(ClassLoader parentLoader) {
this(parentLoader, false);
}
ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
if (parentLoader == null && !nullAllowed) {
throw new NullPointerException(“parentLoader == null && !nullAllowed”);
}
parent = parentLoader;//將父parent保存
}
繼續(xù)觀察this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) {
this.definingContext = definingContext;
ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
this.dexElements = makePathElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);
this.nativeLibraryDirectories = splitPaths(libraryPath, false);
this.systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path"), true);
List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories, null, suppressedExceptions);
if (suppressedExceptions.size() > 0) {
this.dexElementsSuppressedExceptions = suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
} else {
dexElementsSuppressedExceptions = null;
}
}
主要是makeDexElements這個方法,這個方法就是得到一個裝有dex文件的數(shù)組Element[]诞外,每個Element對象里面包含一個DexFile對象成員澜沟,它對應(yīng)的就是dex文件
private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
ArrayList<IOException> suppressedExceptions) {
ArrayList<Element> elements = new ArrayList<Element>();
for (File file : files) {
File zip = null;
DexFile dex = null;
String name = file.getName();
// 如果是一個dex文件
if (name.endsWith(DEX_SUFFIX)) {
// Raw dex file (not inside a zip/jar).
try {
dex = loadDexFile(file, optimizedDirectory);
} catch (IOException ex) {
System.logE("Unable to load dex file: " + file, ex);
}
// 如果是一個apk或者jar或者zip文件
} else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
|| name.endsWith(ZIP_SUFFIX)) {
zip = file;
try {
dex = loadDexFile(file, optimizedDirectory);
} catch (IOException suppressed) {
suppressedExceptions.add(suppressed);
}
} else if (file.isDirectory()) {
elements.add(new Element(file, true, null, null));
} else {
System.logW("Unknown file type for: " + file);
}
//最后封裝成Element
if ((zip != null) || (dex != null)) {
elements.add(new Element(file, false, zip, dex));
}
}
return elements.toArray(new Element[elements.size()]);
}
對應(yīng)的數(shù)據(jù)結(jié)構(gòu):
static class Element {
private final File file; // 它對應(yīng)的就是需要加載的apk/dex/jar文件
private final boolean isDirectory; // 第一個參數(shù)file是否為一個目錄,一般為false峡谊,因為我們傳入的是要加載的文件
private final File zip; // 如果加載的是一個apk或者jar或者zip文件茫虽,該對象對應(yīng)的就是該apk或者jar或者zip文件
private final DexFile dexFile; // 它是得到的dex文件
......
}
目前我們也只能是說將dex封裝到Element并有個數(shù)組dexElements存起來,但是必須知道類加載的使用才能知道這些存起來有什么既们,所以我們繼續(xù)看類加載的使用
public Class<?> loadClass(String className) throws ClassNotFoundException {
return loadClass(className, false);
}
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className);
if (clazz == null) {
ClassNotFoundException suppressed = null;
try {
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
suppressed = e;
}
if (clazz == null) {
try {
clazz = findClass(className);
} catch (ClassNotFoundException e) {
e.addSuppressed(suppressed);
throw e;
}
}
}
return clazz;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
pathList就是前面創(chuàng)建的DexPathList對象濒析,從上面我們知道,我們加載的dex文件都存放在它的exElements成員變量上面啥纸,dexElements就是Element[]數(shù)組号杏,所以可以看到BaseDexClassLoader的findClass方法調(diào)用的是pathList的findClass方法
BaseDexClassLoader
public Class findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;
if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
可以看到它就是遍歷dexElements數(shù)組,從每個Element對象中拿到DexFile類型的dex文件斯棒,然后就是從dex去加載所需要的class文件盾致,直到找到為止。
一個ClassLoader可以包含多個dex文件名船,每個dex文件是一個Element绰上,多個dex文件排列成一個有序的數(shù)組dexElements,當(dāng)找類的時候渠驼,會按順序遍歷dex文件,然后從當(dāng)前遍歷的dex文件中找類鉴腻,如果找類則返回迷扇,如果找不到從下一個dex文件繼續(xù)查找。
所以那些插件化的類如何加載到內(nèi)存中呢爽哎?
基本原理:
1蜓席、除了第一個dex文件(即正常apk包唯一包含的Dex文件),其它dex文件都以資源的方式放在安裝包中课锌。所以我們需要將其他dex文件并在Application的onCreate回調(diào)中注入到系統(tǒng)的ClassLoader厨内。并且對于那些在注入之前已經(jīng)引用到的類(以及它們所在的jar),必須放入第一個Dex文件中。
2渺贤、PathClassLoader作為默認(rèn)的類加載器雏胃,在打開應(yīng)用程序的時候PathClassLoader就去加載指定的apk(解壓成dex,然后在優(yōu)化成odex)志鞍,也就是第一個dex文件是PathClassLoader自動加載的瞭亮。所以,我們需要做的就是將其他的dex文件注入到這個PathClassLoader中去固棚。
3统翩、因為PathClassLoader和DexClassLoader的原理基本一致仙蚜,從前面的分析來看,我們知道PathClassLoader里面的dex文件是放在一個Element數(shù)組里面厂汗,可以包含多個dex文件委粉,每個dex文件是一個Element,所以我們只需要將其他的dex文件放到這個數(shù)組中去就可以了娶桦。
實現(xiàn):
1艳丛、通過反射獲取PathClassLoader中的DexPathList中的Element數(shù)組(已加載了第一個dex包,由系統(tǒng)加載)
2趟紊、通過反射獲取DexClassLoader中的DexPathList中的Element數(shù)組(將第二個dex包加載進(jìn)去)
3氮双、將兩個Element數(shù)組合并之后,再將其賦值給PathClassLoader的Element數(shù)組
我們回歸滴滴的插件中如何實現(xiàn)的:
DexClassLoader loader = new DexClassLoader(apk.getAbsolutePath(), dexOutputPath, libsDir.getAbsolutePath(), parent);
if (Constants.COMBINE_CLASSLOADER) {
try {
DexUtil.insertDex(loader);
} catch (Exception e) {
e.printStackTrace();
}
}
所以實現(xiàn)方法在于DexUtil.insertDex(loader);
public static void insertDex(DexClassLoader dexClassLoader) throws Exception {
Object baseDexElements = getDexElements(getPathList(getPathClassLoader()));
Object newDexElements = getDexElements(getPathList(dexClassLoader));
Object allDexElements = combineArray(baseDexElements, newDexElements);
Object pathList = getPathList(getPathClassLoader());
ReflectUtil.setField(pathList.getClass(), pathList, "dexElements", allDexElements);
insertNativeLibrary(dexClassLoader);
}
將主dex中的類加載器中的pathList得到霎匈,然后得到其中的dexElements
```java
private static Object getPathList(Object baseDexClassLoader) throws Exception {
return ReflectUtil.getField(Class.forName("dalvik.system.BaseDexClassLoader"), baseDexClassLoader, "pathList");
}
private static Object getDexElements(Object pathList) throws Exception {
return ReflectUtil.getField(pathList.getClass(), pathList, "dexElements");
}
Object newDexElements = getDexElements(getPathList(dexClassLoader));
然后拿到新的類加載器中的dex戴差,因為這個里面裝了插件apk的dex。下一步將兩個進(jìn)行混合
Object allDexElements = combineArray(baseDexElements, newDexElements);
private static Object combineArray(Object firstArray, Object secondArray) {
Class<?> localClass = firstArray.getClass().getComponentType();
int firstArrayLength = Array.getLength(firstArray);
int allLength = firstArrayLength + Array.getLength(secondArray);
Object result = Array.newInstance(localClass, allLength);
for (int k = 0; k < allLength; ++k) {
if (k < firstArrayLength) {
Array.set(result, k, Array.get(firstArray, k));
} else {
Array.set(result, k, Array.get(secondArray, k - firstArrayLength));
}
}
return result;
}
然后再將老的pathList反射出來铛嘱,將新融合的dex數(shù)組塞進(jìn)去這樣老的里面就融合了新的dex文件暖释,下一步代碼是對native的lib包進(jìn)行融合,具體看插件代碼
第五步
將清單文件中的所有instrumentations墨吓,activity球匕,services,providers帖烘,broadcast都添加到列表中亮曹。
到了這里L(fēng)oadedPlugin類中存在的信息真多啊,把a(bǔ)pk的資源秘症,清單文件里面的資源都整合了照卦。
此時apk里面的信息已經(jīng)都獲取到了并且添加到資源與類加載器中了,繼續(xù)看
LoadedPlugin plugin = LoadedPlugin.create(this, this.mContext, apk);
if (null != plugin) {
this.mPlugins.put(plugin.getPackageName(), plugin);
synchronized (mCallbacks) {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onAddedLoadedPlugin(plugin);
}
}
// try to invoke plugin's application
plugin.invokeApplication();
public void invokeApplication() {
if (mApplication != null) {
return;
}
// make sure application's callback is run on ui thread.
RunUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
mApplication = makeApplication(false, mPluginManager.getInstrumentation());
}
}, true);
}
private Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
if (null != this.mApplication) {
return this.mApplication;
}
String appClass = this.mPackage.applicationInfo.className;
if (forceDefaultAppClass || null == appClass) {
appClass = "android.app.Application";
}
try {
this.mApplication = instrumentation.newApplication(this.mClassLoader, appClass, this.getPluginContext());
instrumentation.callApplicationOnCreate(this.mApplication);
return this.mApplication;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
執(zhí)行插件的Application.OnCreate
到此為止我們插件的初始化部分就完成了,下一篇我們繼續(xù)跟蹤啟動插件apk內(nèi)的activity