5. Plug-in, modular, componentized, hot repair, incremental update, Gradle

1. Understanding of hot repair and plug-in 2. Principle analysis of plug-in 3. Modular implementation (benefits, reasons) 4. 6. Describe what happens after you click the Build button in Android StudioCopy the code

Reference answer:

1. Understanding of hot fixes and plugins

Blog.csdn.net/github_3713…

Java basedexClassLoader.java dexPathList.javaCopy the code
(1) PathClassLoader: it can only load apK files (/data/app directory) that have been installed in the Android system. It is the default class loader used by Android. (2) DexClassLoader: it can load dex/ JAR/APK /zip files under any directory. It is more flexible than PathClassLoader and is the focus of hot repair.Copy the code
// PathClassLoader
public class PathClassLoader extends BaseDexClassLoader {
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null.null, parent);
    }
 
    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent); }}// DexClassLoaderpublic 
class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
        super(dexPath, newFile(optimizedDirectory), librarySearchPath, parent); }}Copy the code

BaseDexClassLoader

DexPath: the directory where the program file to be loaded (dex file or JAR/APk /zip file) is located. OptimizedDirectory: output directory of dex files (this directory is specially used to store decompressed program files in jar/ APk /zip formats). LibraryPath: libraryPath used when loading program files. Parent: parent loaderCopy the code

Ali: DeXposed, andfix: start from the bottom binary (C language). Ali andFix hook method in native specific fields. The ART virtual machine is a structure called ArtMethod. The purpose of fixing the bug method is achieved by modifying the buggy fields on the structure. However, the artMethod is written dead according to the android native structure, and many third-party manufacturers in China will rewrite the artMethod structure, resulting in the replacement failure. Tinker: Start with Java loading mechanism. Qq dex plug-in is similar to the above analysis of the kind of. Insert the repaired dex file before the dexFileList of the APP to update the bug. However, the bug cannot take effect in time and needs to be restarted. However, during the installation of the VIRTUAL machine, the class is marked with CLASS_ISPREVERIFIED in order to improve the performance. Is there any effect on the performance of the class marked forcibly? The compiler inserts a piece of logic for each method and creates a ChangeQuickRedirect static member variable for each class. When it is not empty, the static member variable is transferred to the new code logic to fix the bug. The advantage is high compatibility, but will increase the application volumeCopy the code
Both PathClassLoader and DexClassLoader inherit from BaseDexClassLoader. 1. Android uses PathClassLoader as its classloader and can only load APK files installed in Android system. 2. DexClassLoader can load classes.dex files from inside.jar and.apk files. Hot fixes also use this class. (1) Dynamically change the dexElements indirectly referenced by BaseDexClassLoader; (2) During app packaging, prevent relevant classes from marking CLASS_ISPREVERIFIED. (3) We use the hook thought proxy startActivity method, using the trap method.Copy the code
1. The startActivity will end up in the AMS startActivity method 2. The system checks a bunch of messages to verify that the Activity is valid. The Handler of the ActivityThread is then called back to the handleLaunchActivity. Here we go to the performLaunchActivity method to create the Activity and call back a series of lifecycle methods 5. When creating an Activity, a LoaderApk object is created, and the getClassLoader of this object is used to create the Activity. We look at the getClassLoader() method and find that it returns a PathClassLoader, which then inherits from BaseDexClassLoader 7. If the BaseDexClassLoader creates a pathList object of type DexPathList, then it calls the pathList. FindClass method 8. We then look at findClass in the DexPathList class and find that it maintains an internal dex array of Element[] dexElements. We find findClass by iterating through the arrayCopy the code

2. Analysis of plug-in principle

Cloud.tencent.com/developer/a…

DexClassLoader and PathClassLoader, both of which inherit from BaseDexClassLoader. DexClassloader uploads an additional optimizedDirectoryCopy the code

DexPathList

More DexClassLoader

Each plug-in has a separate DexClassLoader, which is relatively isolated and adopted by RePluginCopy the code

Single DexClassLoader

Merge the pathList in the DexClassLoader of the plug-in into the DexClassLoader of the main project. Small uses this scheme to facilitate calls between plug-ins and hosts (plug-ins)Copy the code
The main project calls the main project ClassLoader as the parent of the plug-in ClassLoader. If multiple ClassLoaders are used, classes are loaded by the plug-in's ClassLoader and then called by reflection. If single ClassLoader is used, Accessing a class in a plug-in directly by its class name has the disadvantage that the versions of the library may be inconsistent and require specificationCopy the code

Resource to load

// Create AssetManager object
AssetManager assets = new AssetManager();
 // Add the APK path to AssetManager
  if (assets.addAssetPath(resDir) == 0) {return null;  
}
 // Create a Resource object

r = newResources(assets, metrics, getConfiguration(), compInfo); The path of the plug-in APK is added to the AssetManager to be created by reflection, and some of the ROMs are modified to create Resource classes, so compatibility between different ROMs needs to be considered.Copy the code

Processing of resource paths

The Context of the processing

// Create Resource

if (Constants.COMBINE_RESOURCES) {
    // The plugin and the main project resources 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. This 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());
}
Copy the code
// Step 2: Hook main project Resource

// For the combined Resource access mode, you need to replace the main project Resource, 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, newWeakReference<>(resourcesImpl)); }}catch (Exception e) {
        e.printStackTrace();
    }
Copy the code

Replaces the mResource object for LoadedApk in the main project Context

The new Resource is added to the mResourceManager of the main project ActivityThread and is processed differently according to the Android version

// Step 3: Associate resource and Activity

Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
activity.setIntent(intent);
// Set the Activity's mResources property, which is used to access resources in the Activity

ReflectUtil.setField(ContextThemeWrapper.class, activity, "mResources", plugin.getResources());
Copy the code

Resource conflict

The resource ID is represented by an 8-bit hexadecimal number 0xPPTTNNNN and consists of PackageId, TypeId, and EntryId

Modify AAPT source code, compile time modify PP section. Modify the resources.arsc file that lists the mapping of resource ids to specific resource paths.Copy the code

Blog.csdn.net/jiangwei091…

// 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;    
}

Copy the code
CPP: Main, parses the parameters, and then calls handleCommand to handle the logic for the parameters. We see that there is a functiondoPackage. It then searches for Command. CPP: its internaldoBuildResources function, which searches globally and finds Resource. CPP: In ResourceTable. CPP, this is where the PackageId is obtained. In fact, the best way to do this is to modify the aAPT source code and add a parameter that takes the PackageId that we want to compile as input, which is the Bundle type, which is passed from Main in main. CPP to the buildResources function at the end, So we can carry this parameter in the Bundle.Copy the code

Juejin. Cn/post / 684490… www.jianshu.com/p/8d691b6bf…

— — — — — — — — — — — — — — — — — — — — — — — —

Cloud.tencent.com/developer/a…

Along the way, you need to modify to R files, resources.arsc, and binary XML files

Four components support

ProxyActivity agent

The key points of proxy approach are summarized as follows: ProxyActivity needs to override getResouces, getAssets, getClassLoader methods to return the corresponding object of the plug-in. Lifecycle functions and user-related functions such as onResume, onStop, onBackPressedon, KeyUponWindow, FocusChanged, and so on need to be forwarded to the plug-in. All related methods in PluginActivity that call the context, such assetContentView, getLayoutInflater, getSystemService and so on all need to call the corresponding methods of ProxyActivity. This approach has several obvious disadvantages: Activities in plug-ins must inherit pluginActivities, which makes development intrusive. If you want to support the Activity singleTask, singleInstance and other launchmodes, you need to manage the Activity stack, which is cumbersome to implement. Context needs to be handled carefully in plug-ins, which are error prone. It takes a lot of extra work to turn a module into a plug-in.Copy the code

Buried StubActivity, hook system to start the Activity process

VirtualAPK by replacing the Instrumentation system, hook the Activity start and create, eliminating the manual management of plug-in Activity life cycle tedious, let plug-in Activity like normal Activity is managed by the system, And plug-in activities can be developed as usual, either independently or as plug-ins called by the main project. Most other plug-in frameworks handle activities in the same way, either in one or a combination of the two. At hook time, different frameworks may select different hook points. For example, 360's RePlugin framework hooks the system's ClassLoader, that is, the ClassLoader that constructs Activity2. When it determines that the Activity to be started is in the plug-in, it will call the plug-in's ClassLoader to construct the corresponding object. Instead of replacing the intent with the startActivity method of the hook system, RePlugin overwrites the startActivity of the Activity. Therefore, the plug-in Activity needs to inherit a base class similar to PluginActivity. RePlugin, however, provides a Gradle plug-in that replaces the base class of the Activity in the plug-in with a PluginActivity, which is also invisible to the user when developing the plug-in Activity.Copy the code

www.jianshu.com/p/ac96420fc…

Sanjay – f.g ithub. IO / 2016/04/17 /…

www.jianshu.com/p/d43e1fb42…

Service plug-in summary initialization hooks the IActivityManager through ActivityManagerProxy. ActivityManagerProxy intercepts the service to determine whether it is a RemoteService. If it is a RemoteService, start RemoteService; if it is a service in the same process, start LocalService. If LocalService is used, the target Service is loaded by DexClassLoader, and the attach method is called to bind the Context. If the onCreate and onStartCommand methods of Service are RemoteService, the RemoteService of the plug-in is loaded first, and the rest is the same as LocalService.Copy the code

3. Modular implementation (benefits, reasons)

www.cnblogs.com/Jackie-zhan…

1. Decoupling and reuse between modules. (Reason: After the modular separation of services, in order to decouple the service modules, each module is independent and there is no dependency between them. Each module is responsible for different functions, different business logic, business decoupling between modules. Module function is relatively simple, can be used in multiple projects. 2. It can compile a module separately to improve development efficiency. (Reason: Each module is actually a complete project, which can be compiled and debugged separately.) 3. Multiple teams can develop and test in parallel. Reason: Each team is responsible for different modules to improve development and test efficiency.Copy the code

Componentization and modularization

Componentization is the breaking up of a system into individual components for reuse

Avoid repeated wheel construction, save development and maintenance costs; Reduce project complexity and improve development efficiency; Multiple teams share the same component, which ensures the uniformity of technical solutions at a certain level.Copy the code

Modular service layering: From bottom to top

Basic components layer: the underlying use of libraries and wrap some of the tools library (libs), such as okhttp rxjava, rxandroid, glide etc. Business component layer: business-related, encapsulation third-party SDK, such as payment after encapsulation, real-time traffic and other business module layer: Divide modules according to business, such as IM module, information module, etcCopy the code

Library Module development issues

There are problems with extracting code into individual Library modules. The most common problem is R files. In Android development, each resource file is placed in the RES directory. During compilation, r.Java files are generated. The R file contains the id of each resource file. This ID is a static constant, but in the Library Module this ID is not static, so avoid this problem during development. For example, the same method can handle multiple view click events, sometimes use switch(view.getid ()) and then use case r.i.b.tnlogin. This method will cause problems, because id is not always constant, so this method will not be used.Copy the code

4. Hot repair and plug-in

www.jianshu.com/p/704cac3eb…

Host: is currently running APP plug-in: relative to plug-in technology, is to load running apK class file patch: relative to hot repair technology, is to load running. Patch,. Dex,*. Apk and a series of files containing dex repair content.Copy the code

Qzone super patch program

Tinker

HotFix

Of course, in terms of the realization of thermal repair, each major factory still has its own realization, such as the Amigo of Ele. me and the Robust of Meituan. The realization, advantages and disadvantages are different. But in general, there are two categories of ClassLoader loading schemes Native layer replacement schemes or reference to Android Studio Instant Run to achieve incremental updates of the code as a whole. But there is definitely a performance impact.Copy the code

Sophix

www.jianshu.com/p/4d30ce3e5…

Underlying replacement scheme principle: The original method is directly replaced in the loaded class, which is modified on the basis of the structure of the original class. In the hook method entry ArtMethod, by constructing a new ArtMethod to realize the jump of the replacement method entry. Application: It works instantaneously. Andfix uses this solution. Disadvantages: Poor stability of low-level replacements, limited scope, inelegant and inconvenient to work around by modifying code, and no resource or fix for SO. Class loading solution principle: let the app restart after the ClassLoader to load new classes. If you do not restart the vm, the original class cannot be reloaded in the VM. Advantages: wide repair range, less restrictions. Application: Tencent series including Qzone, QFix and Tinker adopt this solution. Qzone will invade the packaging process. QFix needs to get the functions of the underlying virtual machine, which is unstable. Tinker is complete with full dex loading.Copy the code

Tinker differs from Sophix scheme by using DEX Merge to generate the full DEX scheme. Decompile into SMALI, then compare the new APK with the baseline APK, and finally get the patch pack. In Dalvik, Sophix is the same as Tinker. Under Art, Sophix does not need to merge dex, because in Art, the virtual machine already supports loading of multiple dex. All you need to do is to load the patch dex as the primary dex(classes.dex) : Name the patch dex classes.dex and the original APK dex classes(2, 3, 4...). Dex is fine, and then packaged together as a compressed file. Then the dexfile. loadDex gets the DexFile object and finally replaces the entire DexFile object with the old dexElements array. Resource repair solution basic reference to InstantRun's implementation: construct a new AssetManager that contains all new resources. And before all references to the original AssetManager are replaced by reflection. Sophix does not modify the AssetManager reference, the construction of the patch pack contains only new or modified resources, in the original AssetManager addAssetPath package will do. Resource bundles do not need to be synthesized at run time. The essence of so library repair scheme is the repair and replacement of native methods. Similar to class fix reflection injection, insert the patch so library path to the front of the nativeLibraryDirectories data.Copy the code

Method Hook

www.jianshu.com/p/7dcb32f8a… Pqpo. Me / 2017/07/07 /…

5. Understanding of project componentization

Juejin. Cn/post / 684490…

Conclusion Compared with single engineering, componentization in component mode can improve compilation speed, facilitate unit testing and improve development efficiency. The division of labor of developers is more clear, basically do not interfere with each other. The architecture of business components can also be chosen freely without affecting collaboration between peers. Lower maintenance cost and clearer code structure.Copy the code

6. Describe what happens after you click the Build button in Android Studio

Blog.csdn.net/u011026779/… Blog.csdn.net/github_3713…

apply plugin : 'com.android.application'
apply plugin : 'com.android.library'Preparation of dependecies 2 Merging resources and processing the Manifest for Merging resources and proccesssing Manifest 3 Compiling 4. Postprocessing 5. Packaging and publishingCopy the code

Simple build process: 1. The Android compiler (Dalvik before 5.0, ART after) converts the source code of the project (including some third-party libraries, JAR packages and AAR packages) into DEX files and other resources into compiled resources. 2. The APK packer packages DEX files and compiled resources after signing with the secret key. 3. Before generating the final APK, the packer optimizes the application using tools such as Zipalign to reduce its memory footprint when running on the device. Apk for test or release at the end of the build process.Copy the code

Rectangles represent files used or generated, and ellipses represent tools. 1. Use aAPT to package RES resource files to generate R.Java, resources.arsc and RES files. 3. Use Java Compiler to compile R.Java, Java interface files, and Java source files to generate.class files. Use the dex command to generate classes.dex 5 by processing the. Class file and the. Class file in the third-party library. Use apkBuilder to package resources. Arsc with res files, assets files and classes.dex to generate APK 6. Use Jarsigner to debug or release the above APK. 7. Use zipalign to align the signed APK. The result is an Android application that you can install and run.Copy the code

7. Thoroughly understand the differences and connections between Gradle, Gradle Wrapper and Android Plugin for Gradle

Zhuanlan.zhihu.com/p/32714369 blog.csdn.net/LVXIANGAN/a…

May occur when 'Offline work' occurs"No cached version of com.android.tools.build:gradle:xxx available for offline mode"The problemCopy the code
Gradle: Gradle - wrapper. The properties of distributionUrl=https/://services.gradle.org/distributions/gradle-2.10-all.zip gradle plug-in: The classpath that builds. Gradle depends on'com. Android. Tools. Build: gradle: 2.1.2'
Copy the code
Gradle: A build system, a tool for building projects, for building Android apps that simplify your build, package, and test processes. Gradle is an automated project builder based on Apache Ant and Apache Maven concepts. Instead of traditional XML, it uses a Groovy-based domain-specific language to declare project Settings. Currently the languages supported are limited to Java, Groovy, and Scala Gradle plugins: the Gradle we used in AS is called the Android PluginforGradle is essentially an AS plug-in that calls Gradle's own code and batch tools to build projects, AS well AS the compile and package functions of the Android SDK. The Gradle plugin is associated with the Android SDK BuildTool because it also carries over building-related functionality from AS, Specify the Android SDK path in your project's local.properties file and the reason for buildToolsVersion in build.gradle.Copy the code
The plug-in version Gradle version
1.0.0-1.1.3 2.2.1-2.3 –
1.2.0-1.3.1 2.2.1-2.9 –
1.5.0 2.2.1-2.13 –
2.0.0-2.1.2 2.10-2.13
2.1.3-2.2.3 2.14.1 +
2.3.0 + 3.3 +
3.0.0 + 4.1 +
3.1.0 + 4.4 +
3.2.0-3.2.1 4.6 +
3.3.0-3.3.2 rainfall distribution on 10-12 4.10.1 +
3.4.0 + 5.1.1 +

Done