6. We check the getClassLoader() method and find that the returned PathClassLoader inherits from BaseDexClassLoader
7. Then we check BaseDexClassLoader and find that it creates a pathList object of DexPathList type when it is created, and then calls pathList when it finds class Method of findClass
8. Then we check the findClass in the DexPathList class and find that it internally maintains a dex array of Element[] dexElements. When findClass is searched, it is traversed from the array
[]( )2.Analysis of plug-in principle ------------------------------------------------------------------------ ![](https://img-blog.csdnimg.cn/img_convert/6f7c166b974dff4496107e26c7ab06c7.png)
DexClassLoader and PathClassLoader, both of which inherit from BaseDexClassLoader.
DexClassloader passed an additional optimized directory
DexPathList ![](https://img-blog.csdnimg.cn/img_convert/27b8c0c9d0b80f604688ecc8f538e5a9.png) many DexClassLoader ![](https://img-blog.csdnimg.cn/img_convert/efe1eb11b6e514ceb02f5b53891263ea.png)
Each plug-in has a separate DexClassLoader, which is relatively isolated. This scheme is adopted by RePlugin
single DexClassLoader
Merge the pathList in the DexClassLoader of the plug-in into the DexClassLoader of the main project. This scheme is adopted by Small to facilitate the call between plug-ins and hosts (plug-ins)
Plug in calling main project Of main works ClassLoader As a plug-in ClassLoader Parent loader for Main project call plug-in If more than ClassLoader Mechanism through plug-in ClassLoader Load the class first, and then call through reflection If using single ClassLoader Mechanism, directly access the classes in the plug-in through the class name. The disadvantage is that the version of the library may be inconsistent and needs to be standardized ``` Resource loading ``` //Creating AssetManager objects AssetManager assets = new AssetManager(); //Add apk path to AssetManager if (assets.addAssetPath(resDir) == 0){ return null; } //Create Resource object r = new Resources(assets, metrics, getConfiguration(), compInfo); plug-in unit apk Add path to AssetManager in Create by reflection, and part Rom For created Resource Class has been modified, so you need to consider different Rom Compatibility. ``` Processing of resource paths ![](https://img-blog.csdnimg.cn/img_convert/8463f33fc9c0745b4453439f786c42fa.png) Context Treatment of ``` // Step 1: create a Resource if (Constants.COMBINE_RESOURCES) { //When the plug-in is merged with the main project resources, you need to hook the main project resources Resources resources = ResourcesManager.createResources(context, apk.getAbsolutePath()); ResourcesManager.hookResources(context, resources); return resources; } else { //The plug-in resource is independent, and the resource can only access the plug-in's own resources Resources hostResources = context.getResources(); AssetManager assetManager = createAssetManager(context, apk); return new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration()); }
//Step 2: hook the resources of the main project
//For the combined Resource access method, the Resource of the main project needs to be replaced. The following is the specific replacement code.
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(); }
Replaced the main project context in LoadedApk of mResource object New Resource Add to main project ActivityThread of mResourceManager And according to Android Versions are handled differently
//Step 3: Associate resource and Activity
Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
activity.setIntent(intent);
//Set the mResources property of the Activity. The mResources are used to access resources in the Activity
ReflectUtil.setField(ContextThemeWrapper.class, activity, "mResources", plugin.getResources());
Resource conflict resources id Is represented by an 8-bit hexadecimal number, expressed as 0 xPPTTNNNN, It consists of three parts: PackageId+TypeId+EntryId
Modify the aapt source code and modify the PP segment during compilation.
Modify resources ArsC file, which lists the mapping of resource id to specific resource path.
// Main.cpp result = handleCommand(&bundle); case kCommandPackage: return doPackage(bundle); // Command.cpp int doPackage(Bundle* bundle) { if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { err = buildResources(bundle, assets, builder); if (err != 0) { goto bail; } } } Resource.cpp buildResources ResourceTable.cpp switch(mPackageType) { case App: case AppFeature: packageId = 0x7f; break; case System: packageId = 0x01; break; case SharedLibrary: packageId = 0x00; break; }
First find the entry class: main Cpp:main function, parse the parameters, and then call the handleCommand function to handle the logic corresponding to the parameters. We see that there is a function doPackage.
Then you search for command CPP: a function of the compiler tool in its internal doPackage function: buildResources function. In the global search, a resource is found CPP: I found that this is the logic for processing compilation and building ResourceTable. In ResourceTable CPP is also the place to get PackageId. Let's see how to modify it?
In fact, the best way is to modify the aapt source code, add a parameter, and pass in the PackageId we want to compile as the input value. That is the Bundle type, which is from main The main function in CPP is passed to the last buildResources function, so we can carry this parameter with Bundle.
![](https://img-blog.csdnimg.cn/img_convert/fc13917feb4a8c14c168a94caa79aefe.png) ------------------------ In the whole process, it needs to be modified to R Documents resources.arsc And binary xml file Four component support ProxyActivity agent ![](https://img-blog.csdnimg.cn/img_convert/cc144fe456dc450c705bcbd6c76182f8.png)
The key of agency mode can be summarized as follows:
In ProxyActivity, you need to override getresources, getAssets and getClassLoader methods to return the corresponding objects of the plug-in. Life cycle functions and functions related to user interaction, such as onResume, onStop, onbackpressdon, KeyUponWindow, FocusChanged, etc., need to be forwarded to the plug-in.
All relevant methods calling context in PluginActivity, such as setContentView, getlayoutinflator, getSystemService, etc., need to call the corresponding methods of ProxyActivity.
This method has several obvious disadvantages:
The Activity in the plug-in must inherit PluginActivity, which is highly intrusive.
If you want to support launchmodes such as singleTask and singleInstance of an Activity, you need to manage the Activity stack yourself, which is cumbersome to implement.
Context needs to be handled carefully in the plug-in, which is prone to errors.
It takes a lot of extra work to transform the previous module into a plug-in.
Embedded StubActivity,hook System startup Activity Process of ![](https://img-blog.csdnimg.cn/img_convert/38d4cadb9cf5738fe5c211c8206ccf65.png)
By replacing the system Instrumentation, VirtualAPK hook s the startup and creation of the Activity, eliminates the tedious task of manually managing the life cycle of the plug-in Activity, and allows the plug-in Activity to be managed by the system like a normal Activity. In addition, the plug-in Activity can run independently and be called by the main project as a plug-in during development.
Most other plug-in frameworks have similar ideas when dealing with activities, which is nothing more than one or a combination of the two methods. In hook, different frameworks may choose different hook points. For example, 360's RePlugin framework selects hook as the system's ClassLoader, that is, the ClassLoader of Activity2. When it is determined that the Activity to be started is in the plug-in, it will call the plug-in's ClassLoader to construct the corresponding object. In addition, RePlugin selects as few hooks as possible for system stability. Therefore, instead of selecting the startActivity method of hook system to replace intent, it rewrites the startActivity of Activity. Therefore, its plug-in Activity needs to inherit a base class similar to PluginActivity. However, RePlugin provides a Gradle plug-in, replacing the base class of the Activity in the plug-in with PluginActivity, which is not perceived by users when developing plug-in activities.
Copy code
[www.jianshu.com/p/ac96420fc...]( ) [sanjay-f.github.io/2016/04/17/...]( ) [www.jianshu.com/p/d43e1fb42...]( )
Service plug-in summary
IActivityManager is initialized through the ActivityManagerProxy Hook.
When the service is started, it is intercepted by ActivityManagerProxy to determine whether it is a remote service. If it is a remote service, start RemoteService. If it is a service in the same process, start LocalService.
If it is a LocalService, load the target Service through the DexClassLoader, then call the attach method to bind the Context, and then execute the onCreate and onStartCommand methods of the Service
If it is RemoteService, first load the remote Service of the plug-in, and then it is consistent with the LocalService.
Copy code
[]( )3.Modular implementation (benefits, reasons) -----------------------------------------------------------------------------
1. Decoupling and reuse between modules.
(reason: after the business is modularized, in order to decouple the business modules, each module is independent and there is no dependency between them.
Each module is responsible for different functions, different business logic, and business decoupling between modules. The module has a single function and can be used in multiple projects.)
2. A module can be compiled separately to improve development efficiency.
(reason: each module is actually a complete project, which can be compiled and debugged separately)
3. It can be developed and tested by multiple teams in parallel.
Reason: each team is responsible for different modules to improve development and testing efficiency.
Componentization and modularization Componentization refers to splitting a system into individual components for the purpose of reuse
Avoid repeated wheel making and save development and maintenance costs;
Reduce project complexity and improve development efficiency;
Multiple teams share the same component, which ensures the unity of technical solutions at a certain level.
Modular business layering: from bottom to top
Basic component layer:
The underlying libraries and encapsulated tool libraries (libs), such as okhttp,rxjava,rxandroid,glide, etc
Business component layer:
Business related, encapsulating third-party SDKs, such as encapsulated payment, instant access, etc
Business module layer:
Divide modules according to business, such as IM module, information module, etc
Library Module Development issues
Various problems will be encountered when extracting code into individual library modules.
The most common problem is the R file problem. In Android development, all resource files are placed in the res directory. During the compilation process, R.java files will be generated.
The R file contains the id corresponding to each resource file. This id is a static constant, but in the Library Module, this id is not a static constant. This problem should be avoided during development.
For a common example, the same method handles click events of multiple views, sometimes using the method of switch(view.getId()),
Then use case r.id BtnLogin judges in this way. At this time, there will be a problem, because the ID is not a constant, so this method cannot be used.
[]( )4.Hot repair and plug-in ------------------------------------------------------------------------
Host: the currently running APP
Plug in: compared with plug-in technology, it is to load the running apk class file
Patch: compared with hot fix technology, it is to load and run patch,.dex,*.apk and a series of files containing DEX repair content.
![](https://img-blog.csdnimg.cn/img_convert/a8b6c9d2f468ec8a6af8191e4056da84.png) QQ Space super patch scheme Tinker ![](https://img-blog.csdnimg.cn/img_convert/d9ef4476b327aa1df907238ad14c4815.png) HotFix ![](https://img-blog.csdnimg.cn/img_convert/633327393a3bff19226894f0fd8fe093.png)
Of course, in terms of the implementation of thermal repair, each large factory also has its own implementation, such as Amigo and robot of meituan. The implementation, advantages and disadvantages are different, but generally speaking, there are two categories
ClassLoader loading scheme
Native layer replacement scheme
Or refer to the idea of Android Studio Instant Run to realize the overall incremental update of the code. But this is bound to have an impact on performance.
Sophix
Underlying alternative
Principle: the original method is directly replaced in the loaded class, which is modified based on the structure of the original class. When the hook method enters ArtMethod, the jump of the replacement method entry is realized by constructing a new ArtMethod.
Application: it can take effect immediately. Andfix adopts this scheme.
Disadvantages: the stability of the underlying replacement is poor, and there are restrictions on the scope of application. It is neither elegant nor convenient to bypass the restrictions by modifying the code, and resources and so repair have not been provided.
Class loading scheme
Principle: after the app is restarted, let ClassLoader load new classes. If you do not restart, the original class is still in the virtual machine and cannot be loaded repeatedly.
Advantages: wide repair range and less restrictions.
Application: Tencent includes QQ space, hand QFix and Tinker adopt this scheme.
QQ space will invade the packaging process.
QFix needs to obtain the functions of the underlying virtual machine, which is unstable.
Tinker is a complete full dex load.
![](https://img-blog.csdnimg.cn/img_convert/ceaad56a5df49005554fe8de2b95d246.png)
The difference between Tinker and Sophix solutions
Tinker uses dex merge to generate full DEX scheme. Decompile to smali, then compare the difference between the new apk and the baseline apk, and finally get the patch package.
In Dalvik, Sophix is the same as Tinker. In Art, Sophix does not need to do dex merge, because the virtual machine under Art essentially supports the loading of multiple dex. What to do is to load the patch dex as the main dex(classes.dex):
Name the patch dex classes dex, the dex in the original apk is named classes(2, 3, 4...) dex is good, and then packaged together into a compressed file. Then DexFile Loaddex gets the DexFile object. Finally, replace the entire DexFile object with the old dexElements array.
Resource recovery scheme
Basic reference to the implementation of InstantRun: construct a new AssetManager containing all new resources. And all previously referenced to the original AssetManager are replaced by reflection.
Sophix does not modify the reference of AssetManager. The constructed patch package only contains new or modified resources. Just add assetpath package in the original AssetManager. Resource packages do not need to compose complete packages at run time.
so library repair scheme
The essence is to repair and replace the native method. Similar to the class repair reflection injection method, insert the path of the patch so library into the front of the nativelibrary directories data.
Method Hook []( )5.Understanding of project componentization -------------------------------------------------------------------------
summary
Compared with a single project, componentization can improve the compilation speed, facilitate unit testing and improve the development efficiency.
Developers have a clearer division of labor and basically do not interfere with each other.
The architecture of business components can also be freely selected without affecting the cooperation between peers.
Reduce maintenance costs and make the code structure clearer.
[]( )6.Description clear Click Android Studio of build What happened after the button ------------------------------------------------------------------------------------------------------
apply plugin : 'com.android.application'
apply plugin : 'com.android.library'
Five stages of compilation
1. Preparation of dependencies
2. Merging resources and processing manifest
3. Compiling
4. Post processing
last
If you want to learn more about the interview with a large factory, you can praise and support it. In addition, I also share some free high-quality resources, including: Android learning PDF + Architecture Video + source code notes, advanced architecture technology brain map, special materials for Android development interview, and advanced architecture materials. To share with you, it is very suitable for friends who have recent interviews and want to continue to improve on the technical road.