VirtualAPK滴滴插件化框架源碼深入解讀

初始化部分

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乡摹,一起剝皮案震驚了整個濱河市役耕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌聪廉,老刑警劉巖瞬痘,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異板熊,居然都是意外死亡框全,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門邻邮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來竣况,“玉大人,你說我怎么就攤上這事〉と” “怎么了情萤?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長摹恨。 經(jīng)常有香客問我筋岛,道長,這世上最難降的妖魔是什么晒哄? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任睁宰,我火速辦了婚禮,結(jié)果婚禮上寝凌,老公的妹妹穿的比我還像新娘柒傻。我一直安慰自己,他們只是感情好较木,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布红符。 她就那樣靜靜地躺著,像睡著了一般伐债。 火紅的嫁衣襯著肌膚如雪预侯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天峰锁,我揣著相機(jī)與錄音萎馅,去河邊找鬼。 笑死虹蒋,一個胖子當(dāng)著我的面吹牛糜芳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播千诬,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼耍目,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了徐绑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤莫辨,失蹤者是張志新(化名)和其女友劉穎傲茄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沮榜,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡盘榨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蟆融。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片草巡。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖型酥,靈堂內(nèi)的尸體忽然破棺而出山憨,到底是詐尸還是另有隱情查乒,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布郁竟,位于F島的核電站玛迄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏棚亩。R本人自食惡果不足惜蓖议,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望讥蟆。 院中可真熱鬧勒虾,春花似錦、人聲如沸瘸彤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钧栖。三九已至低零,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拯杠,已是汗流浹背掏婶。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留潭陪,地道東北人雄妥。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像依溯,于是被迫代替她去往敵國和親老厌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容