1 . preface
Virtual APK is an excellent plug-in framework developed by didi travel. Its main developer is teacher Ren Yugang
Speaking of Mr. Ren Yugang, he can be said to be the first teacher of my Android FrameWork layer. When I first came into contact with Android, after dragging the controls for several years and writing some CURD operations, I came to the conclusion that the client is too boring. Now I am fully proficient in Android development. Until one day I read a book called "exploration of the art of Android development", I couldn't help feeling that Android development can still play like this. My previous understanding is really shallow
To get back to business, the features and usage of Virtual APK are not the focus of this article. If you need to know more, please move to the features and usage of Virtual APK. This paper mainly explains the implementation of Virtual APK.
2 . Important knowledge points
- Activity initiation process (AMS)
- DexClassLoader
- Dynamic agent
- reflex
- Dynamic registration of broadcast
3 . Implementation of host App
The central idea:
- Analyze the plug-in APK and obtain the information of the plug-in APK
- When the framework is initialized, a series of system components and interfaces are replaced to modify and monitor the startup and life cycle of Activity, Service and ContentProvider, so as to deceive or hijack the system to start the corresponding components of plug-in Apk.
3.1 analysis and loading of plug-in Apk
The plug-in Apk is loaded in the PluginManager#loadPlugin method. After loading, a LoadedPlugin object will be generated and saved in the Map. LoadedPlugin stores most of the important information in Apk and a DexClassLoader, which is used as the class loader of Apk.
Take a look at the specific implementation of LoadedPlugin. The notes indicate the meaning of each attribute:
public LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception { // PluginManager this.mPluginManager = pluginManager; // Host Context this.mHostContext = context; // Plug in apk path this.mLocation = apk.getAbsolutePath(); this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK); // Plug in apk metadata this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData; // Plug in apk package information this.mPackageInfo = new PackageInfo(); this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo; this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath(); // Plug in apk signature information if (Build.VERSION.SDK_INT >= 28 || (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) { // Android P Preview try { this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures; } catch (Throwable e) { PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES); this.mPackageInfo.signatures = info.signatures; } } else { this.mPackageInfo.signatures = this.mPackage.mSignatures; } // Plug in apk package name this.mPackageInfo.packageName = this.mPackage.packageName; // If the same apk has been loaded, an exception is thrown 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 = createPluginPackageManager(); this.mPluginContext = createPluginContext(null); this.mNativeLibDir = getDir(context, Constants.NATIVE_DIR); this.mPackage.applicationInfo.nativeLibraryDir = this.mNativeLibDir.getAbsolutePath(); // Create a plug-in Explorer this.mResources = createResources(context, getPackageName(), apk); // Create a dexClassLoader this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader()); tryToCopyNativeLib(apk); // 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 // Save Activity information of plug-in apk Map<ComponentName, ActivityInfo> activityInfos = new HashMap<ComponentName, ActivityInfo>(); for (PackageParser.Activity activity : this.mPackage.activities) { activity.info.metaData = activity.metaData; activityInfos.put(activity.getComponentName(), activity.info); } this.mActivityInfos = Collections.unmodifiableMap(activityInfos); this.mPackageInfo.activities = activityInfos.values().toArray(new ActivityInfo[activityInfos.size()]); // Cache services // Save Service information of plug-in apk 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 // Save the ContentProvider information of the plug-in apk 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()]); // Change all static registered broadcasts to dynamic registration 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()]); // try to invoke plugin's application // Create Application object of plug-in apk invokeApplication(); }
3.2 Activity startup processing and life cycle management
The overall scheme of the Activity in the Virtual APK startup plug-in APK:
- Hook Instrumentaion and the callback of the main thread Halder replace Intent or Activity at important startup process nodes
- Some plug-in activities are preset in the host APP. These plug-in activities will not really start, but cheat AMS. If the Activity to be started is in the plug-in APK, select the appropriate plug-in Activity according to the startup mode of the Activity. AMS will actually create the Activity to be started in the plug-in APK after processing the plug-in Activity in the startup phase and creating the Activity instance.
3.2.1 statement of pile insertion Activity:
There are many pile insertion activities. Select some:
<!-- Stub Activities --> <activity android:exported="false" android:name=".A$1" android:launchMode="standard"/> <activity android:exported="false" android:name=".A$2" android:launchMode="standard" android:theme="@android:style/Theme.Translucent" /> <!-- Stub Activities --> <activity android:exported="false" android:name=".B$1" android:launchMode="singleTop"/> <activity android:exported="false" android:name=".B$2" android:launchMode="singleTop"/> <activity android:exported="false" android:name=".B$3"
3.2.2 hook Instrumentation
- Replace the Instrumentation provided by the system with customized VAInstrumentation, and replace the callback of the main thread Handler with VAInstrumentation (VAInstrumentation implements the Handler.Callback interface)
protected void hookInstrumentationAndHandler() { try { // Gets the activityThread of the current process ActivityThread activityThread = ActivityThread.currentActivityThread(); // Get the Instrumentation of the current process Instrumentation baseInstrumentation = activityThread.getInstrumentation(); // Create custom Instrumentation final VAInstrumentation instrumentation = createInstrumentation(baseInstrumentation); // Replace the original Instrumentation object of the current process with a custom one Reflector.with(activityThread).field("mInstrumentation").set(instrumentation); // Replace the callback of the original main thread handler of the current process with a custom one Handler mainHandler = Reflector.with(activityThread).method("getHandler").call(); Reflector.with(mainHandler).field("mCallback").set(instrumentation); this.mInstrumentation = instrumentation; Log.d(TAG, "hookInstrumentationAndHandler succeed : " + mInstrumentation); } catch (Exception e) { Log.w(TAG, e); } }
3.2.3 cheat AMS when starting Activity
If we are familiar with the Activity startup process, we must know that the Activity startup and life cycle management are managed indirectly through Instrumentation If you are not familiar with it, it doesn't matter. You can read the AMS series articles I wrote before and make sure you understand it in seconds (FOG). VAInstrumentation rewrites some important methods of this class. We start the process one by one according to the Activity
3.2.3.1 execStartActivity
This method has many overloads. Choose one of them:
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode) { // Process the original Intent injectIntent(intent); return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode); }
The injectIntent method handles the Intent in the componentschandler#markintentifneeded method. It parses the original Intent and obtains the package name and class name of the target Activity. If the package name of the target Activity is different from the current process and the LoadedPlugin object corresponding to the package name exists, it indicates that it is an Activity in the plug-in APK we have loaded, then replace the target of the Intent:
public void markIntentIfNeeded(Intent intent) { ... String targetPackageName = intent.getComponent().getPackageName(); String targetClassName = intent.getComponent().getClassName(); // Determine whether the Activity of the plug-in Apk needs to be started if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null) { ... // Replace the target Acitivy of the original Intent with one of the preset instrumentation activities dispatchStubActivity(intent); } }
The dispatchstubbactivity method selects the appropriate plug-in Activity according to the startup mode of the original Intent, and modifies the class name in the original Intent to the class name of the plug-in Activity. Example code:
case ActivityInfo.LAUNCH_SINGLE_TOP: { usedSingleTopStubActivity = usedSingleTopStubActivity % MAX_COUNT_SINGLETOP + 1; stubActivity = String.format(STUB_ACTIVITY_SINGLETOP, corePackage, usedSingleTopStubActivity); break; } case ActivityInfo.LAUNCH_SINGLE_TASK: { usedSingleTaskStubActivity = usedSingleTaskStubActivity % MAX_COUNT_SINGLETASK + 1; stubActivity = String.format(STUB_ACTIVITY_SINGLETASK, corePackage, usedSingleTaskStubActivity); break; } case ActivityInfo.LAUNCH_SINGLE_INSTANCE: { usedSingleInstanceStubActivity = usedSingleInstanceStubActivity % MAX_COUNT_SINGLEINSTANCE + 1; stubActivity = String.format(STUB_ACTIVITY_SINGLEINSTANCE, corePackage, usedSingleInstanceStubActivity); break; }
3.2.3.2 newActivity
If only the original Intent is replaced, the plug-in Activity will be started eventually, which obviously fails to achieve the purpose of starting the activty in the plug-in Apk. In the Activity instance creation stage, the actually created activty needs to be replaced. The method is in VAInstrumentation#newActivity:
@Override public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { try { cl.loadClass(className); Log.i(TAG, String.format("newActivity[%s]", className)); } catch (ClassNotFoundException e) { ComponentName component = PluginUtil.getComponent(intent); String targetClassName = component.getClassName(); Log.i(TAG, String.format("newActivity[%s : %s/%s]", className, component.getPackageName(), targetClassName)); LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(component); // Use the DexClassLoader created in the LoadedPlugin object for class loading, which points to the path of the plug-in APK Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent); activity.setIntent(intent); // After the plug-in Activity instance is created, replace the Resource with the Resource of the plug-in APK Reflector.QuietReflector.with(activity).field("mResources").set(plugin.getResources()); return newActivity(activity); } return newActivity(mBase.newActivity(cl, className, intent)); }
If we start the Activity in the plug-in APK, the Catch statement block of this method will be executed, because the input parameter className has been replaced with the Activity of the plug-in, but we are only in the androidmanifest of the host App These actiivys are defined in XML and have no real implementation. After entering the Catch statement block, use the DexClassloader saved in LoadedPlugin to create an Activity.
3.2.3.3 AMS manages the Activity in the APK plug-in
After seeing this, some students may have problems. You replaced the activity to be started, but the pile insertion activity is still recorded in AMS. What about the subsequent interaction between this activity instance and AMS? Isn't that the record in AMS can't be found? Don't worry, this problem won't happen. Before reviewing the AMS series, we will know that the Activity management in AMS is based on a Binder instance called appToken, and the token corresponding to the client will be sent to Actiivty by Activity#attach after the execution of Instrumentation#newActivity.
This is also the reason why the plug-in scheme of cheating AMS is feasible, because the subsequent management uses token. If Android uses className and other management methods, I'm afraid this scheme will not be easy to implement.
3.2.3.4 replace Context, applicaiton and Resources
After the Context of the system creation plug-in Activity is created, it needs to be replaced with PluginContext. The difference between PluginContext and Context is that there is a LoadedPlugin object in it to facilitate the replacement of resources in the Context. The code is in VAInstrumentaiton#injectActivity and the call is in VAInstrumentaiton#callActivityOnCreate
protected void injectActivity(Activity activity) { final Intent intent = activity.getIntent(); if (PluginUtil.isIntentFromPlugin(intent)) { Context base = activity.getBaseContext(); try { LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent); Reflector.with(base).field("mResources").set(plugin.getResources()); Reflector reflector = Reflector.with(activity); reflector.field("mBase").set(plugin.createPluginContext(activity.getBaseContext())); reflector.field("mApplication").set(plugin.getApplication()); // set screenOrientation ActivityInfo activityInfo = plugin.getActivityInfo(PluginUtil.getComponent(intent)); if (activityInfo.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { activity.setRequestedOrientation(activityInfo.screenOrientation); } // for native activity ComponentName component = PluginUtil.getComponent(intent); Intent wrapperIntent = new Intent(intent); wrapperIntent.setClassName(component.getPackageName(), component.getClassName()); wrapperIntent.setExtrasClassLoader(activity.getClassLoader()); activity.setIntent(wrapperIntent); } catch (Exception e) { Log.w(TAG, e); } } }
3.3 Service handling
The overall scheme of the Activity in the Virtual APK startup plug-in APK:
- Use dynamic proxy to proxy all Service requests in the host APP
- Judge whether it is a Service in the plug-in APK. If not, it means it is in the host APP and can be opened directly
- If it is a Service in the plug-in APK, judge whether it is a remote Service. If it is a remote Service, start RemoteService and process it in its StartCommand method according to the agent's life cycle method
- If it is a local Service, start the LocalService and process it according to the agent's life cycle method in its StartCommand method
3.3.1 IActivityManager of agent system during the initialization of plug-in framework
IActivityManager is the implementation interface of AMS. Its implementation classes are ActivityManagerService and its proxy
The Proxy we need here is the Proxy, and the implementation method is in PluginManager#hookSystemServices
protected void hookSystemServices() { try { Singleton<IActivityManager object> defaultSingleton; // Get IActivityManager object if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { defaultSingleton = Reflector.on(ActivityManager.class).field("IActivityManagerSingleton").get(); } else { defaultSingleton = Reflector.on(ActivityManagerNative.class).field("gDefault").get(); } IActivityManager origin = defaultSingleton.get(); // Create a dynamic proxy for the activityManager object IActivityManager activityManager Dynamic proxy for objects = (IActivityManager) Proxy.newProxyInstance(mContext.getClassLoader(), new Class[] { IActivityManager.class }, createActivityManagerProxy(origin)); // Replace the previous IActivityManager object instance with a dynamic proxy Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy); if (defaultSingleton.get() == activityManagerProxy) { this.mActivityManager = activityManagerProxy; Log.d(TAG, "hookSystemServices succeed : " + mActivityManager); } } catch (Exception e) { Log.w(TAG, e); } }
The proxy of ActivityManager created by the system is replaced by dynamic proxy. In this way, when calling AMS method, it will go to the invoke method of ActivityManagerProxy and manage the Service life cycle according to the method name. There are many life cycle methods. Choose one of them:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("startService".equals(method.getName())) { try { return startService(proxy, method, args); } catch (Throwable e) { Log.e(TAG, "Start service error", e); } }
startService:
protected Object startService(Object proxy, Method method, Object[] args) throws Throwable { IApplicationThread appThread = (IApplicationThread) args[0]; Intent target = (Intent) args[1]; ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0); if (null == resolveInfo || null == resolveInfo.serviceInfo) { // Not found in the plug-in, indicating that it is the host APP's own Service return method.invoke(this.mActivityManager, args); } // Start the Service in the APK plug-in return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_START_SERVICE); }
In startDelegateServiceForTarget, wrapperTargetIntent will be called for processing, and finally each life cycle of the Service will be processed in onStartCommand of RemoteService or LocalService.
It should be noted that in RemoteService, APK needs to be parsed and loaded again to generate LoadedPlugin, because it runs in another process.
This also shows that if the Service process of the plug-in APK declares multiple, it is invalid, because they will eventually run in the process of the host RemoteService.
3.4 processing of ContentProvider
The processing of content provider is similar to that of Service. I won't say more.
4 . Implementation of plug-in App
Theoretically, the plug-in app does not need any special treatment. The only thing to pay attention to is the conflict of resource files. Therefore, it is necessary to build in the plug-in project app directory Add the following code to gradle:
virtualApk { packageId = 0x6f // the package id of Resources. targetHost = '../../VirtualAPK/app' // the path of application module in host project. applyHostMapping = true //optional, default value: true. }
Its function is to rewrite the resource ID when the plug-in APK is compiled. The processing method is in resourcecollector collect method of groovy file:
def collect() { //1,First, collect all resources by parsing the R symbol file. parseResEntries(allRSymbolFile, allResources, allStyleables) //2,Then, collect host resources by parsing the host apk R symbol file, should be stripped. parseResEntries(hostRSymbolFile, hostResources, hostStyleables) //3,Compute the resources that should be retained in the plugin apk. filterPluginResources() //4,Reassign the resource ID. If the resource entry exists in host apk, the reassign ID // should be same with value in host apk; If the resource entry is owned by plugin project, // then we should recalculate the ID value. reassignPluginResourceId() //5,Collect all the resources in the retained AARs, to regenerate the R java file that uses the new resource ID vaContext.retainedAarLibs.each { gatherReservedAarResources(it) } }
First, obtain the resource collection of plug-in app and host app, and then find the conflicting resource id to modify. The modification id is reassignPluginResourceId method:
private void reassignPluginResourceId() { // Sort resource ID S by typeId resourceIdList.sort { t1, t2 -> t1.typeId - t2.typeId } int lastType = 1 // Override resource ID resourceIdList.each { if (it.typeId < 0) { return } def typeId = 0 def entryId = 0 typeId = lastType++ pluginResources.get(it.resType).each { it.setNewResourceId(virtualApk.packageId, typeId, entryId++) } } }
Here is the composition of resource ID:
Resource id is a 32-bit hexadecimal integer. The first 8 bits represent app, the next 8 bits represent typeId(string, layout, id, etc.), which is accumulated from 01, and the last four bits are resource id, which is accumulated from 0000. Decompile an apk casually. Take a look at the structure of some of them:
A double loop is used to traverse the resource ID. the outer loop starts from 01 to traverse the typeId, and the inner loop starts from 0000 to traverse the resource ID corresponding to the typeId, and calls setNewResourceId in the inner loop to rewrite:
public void setNewResourceId(packageId, typeId, entryId) { newResourceId = packageId << 24 | typeId << 16 | entryId }
packageId is where we build Virtualapk defined in gradle packageId, which is shifted to the left by 24 bits, corresponds to the first 8 bits of the resource id, typeId corresponds to bits 9-16, followed by the resource id
In this way, the replacement of conflicting resource IDS is completed during the compilation of plug-in app, and there will be no conflict later
5 . summary
Reviewing the implementation of the whole Virtual APK, in fact, the logic is not particularly complex, but we can see the authors' familiarity with AMS, resource loading, class loader and other API s. If they are not particularly proficient in these knowledge systems, it is difficult to implement, or even have ideas. This is also the significance of learning the source code.
last
Share with you a collection of interview questions.
The following questions are all encountered during the interview in the Android communication group. If you have good questions or good opinions, please share them. The landlord will maintain this post for a long time.
Reference analysis: Guo Lin, Hongyang, Yugang, geek time, Tencent classroom
Content features: clear organization, including graphical representation, easier to understand.
Content summary: including Handler, Activity related, Fragment, service, layout optimization and AsyncTask related
, Android event distribution mechanism, Binder, Android advanced prerequisites: AMS,WMS,PMS, Glide, Android componentization and plug-in and other interview questions and technology stacks!
Handler related knowledge, interview must ask!
Frequently asked questions:
What is the Handler Looper Message relationship?
What is the data structure of Messagequeue? Why use this data structure?
How do I create a Handler in a child thread?
How does the Handler post method work?
Principle and source code analysis of Android message mechanism
Android Handler message mechanism
Activity related
Startup mode and usage scenario?
onNewIntent() and onConfigurationChanged()
onSaveInstanceState() and onRestoreInstanceState()
How exactly did the Activity start
Startup mode and usage scenario
onSaveInstanceState and onRestoreInstanceState use
onConfigurationChanged usage and problem solving
Activity start process resolution
Fragment
Fragment lifecycle vs. Activity
How to communicate between fragments
startActivityForResult of Fragment
Fragment overlap problem
On Fragment
Fragment overlap, how to communicate
Fragment lifecycle
Service related
Process keeping alive
Running thread of Service (all life cycle methods are in the main thread)
How to start and stop the Service
Which thread does the callback method in ServiceConnection run on?
Differences between startService and bingService
General routine of keeping process alive
Everything you need to know about keeping the process alive
ViewStub, include, merge for Android layout optimization
When do I use ViewStub, include, merge?
What is their principle?
Concept analysis of ViewStub, include and merge
Use and source code analysis of ViewStub, include and merge for Android layout optimization
BroadcastReceiver related
Registration method, priority
Broadcast type, difference
Usage scenario and principle of broadcasting
Android broadcast dynamic and static registration
Common usage and process analysis
Broadcast source code analysis
AsyncTask related
Is AsyncTask executed in serial or parallel?
AsyncTask changes with the Android version
AsyncTask full resolution
Serial or parallel
Android event distribution mechanism
The difference between onTouch and onTouchEvent is the calling sequence
dispatchTouchEvent, onTouchEvent, onInterceptTouchEvent method sequence and usage scenario
Sliding conflict, how to solve it
Event distribution mechanism
Event distribution resolution
Usage scenario analysis of dispatchTouchEvent, onTouchEvent, onInterceptTouchEvent methods
Android View drawing process
Brief description of View drawing process
Points needing attention in onMeasure, onlayout and ondraw methods
How to customize View
view redrawing mechanism
-
Android layoutinflator principle analysis, take you step by step in-depth understanding of view (I)
-
Android view status and redrawing process analysis take you step by step to deeply understand View(2)
-
Android view status and redrawing process analysis take you step by step to deeply understand View(3)
-
The implementation method of Android custom View takes you step by step to deeply understand View(4)
Android Window, Activity, DecorView and ViewRoot
Relationship among Window, Activity, DecorView and ViewRoot
Binder multi process AIDL, the core of Android
Common IPC mechanisms and usage scenarios
Why does Android use binder s for cross process transmission
Problems caused by multiple processes
-
Analysis of AIDL application
-
Analysis of binder principle
-
Bottom layer parsing of binder
-
Multi process communication mode and its problems
-
Comparison of multi process communication modes
Android advanced prerequisites: AMS,WMS,PMS
AMS,WMS,PMS creation process
-
AMS,WMS,PMS full resolution
-
AMS startup process
-
WindowManagerService startup process resolution
-
PMS startup process analysis
Android ANR
Why does ANR happen?
How to locate ANR?
How to avoid ANR?
What is ANR
How to avoid and analysis methods
ANR detailed explanation of Android performance optimization
Android memory related
Note: memory leak and memory overflow are two concepts
Under what circumstances will memory leak?
How to prevent memory leakage?
-
The difference between memory leak and overflow
-
OOM concept and Android memory management mechanism
-
Possibility of memory leakage
-
Methods to prevent memory leakage
Android screen adaptation
Analysis of terms related to screen adaptation
Popular screen adaptation methods
-
Screen adaptation nouns and concept analysis
-
Today's headline technology adaptation scheme
Android caching mechanism
How LruCache works
-
Android caching mechanism
-
How LruCache works
Android performance optimization
How to locate and optimize the power consumption of memory and cpu
Methods often used for performance optimization
How to avoid UI jamming
-
Performance optimization, full analysis, tool use
-
Performance optimization best practices
-
Know highly praised articles
Android MVC,MVP,MVVM
There are several. Which one should I choose? Advantages and disadvantages
Ren Yugang's article: selection of design mode
Android Gradle knowledge
These two official articles are basic enough
You must post the official document: configuration build
Gradle tips and tricks
Just understand the Gradle plug-in
Gradle custom plug-in mode
Comprehensive understanding of Gradle - execution timing
-
Gradle series I
-
Gradle Series II
-
Gradle Series III
RxJava
Application process, characteristics and principle analysis
RxJava nouns and how to use them
Principle analysis of Rxjava observer mode
Rxjava subscription process, thread switching, source code analysis series
OKHTTP and Retrofit
OKHTTP complete parsing
Retrofit use process and detailed mechanism
From HTTP to Retrofit
How does Retrofit work
The most popular picture loading Library: Glide
Glide analysis of Guoshen series
The most complete analysis of Android image loading framework (I), the basic usage of Glide
The most complete analysis of Android image loading framework (II), understand Glide's execution process from the perspective of source code
The most complete analysis of Android image loading framework (III), in-depth exploration of Glide's caching mechanism
The most complete analysis of Android image loading framework (IV), playing with Glide's callback and monitoring
Android image loading framework most complete analysis (V), Glide powerful image transformation function
The most complete analysis of Android image loading framework (6), explore the user-defined module function of Glide
Android image loading framework most complete analysis (VII), to achieve Glide image loading function with progress
The most complete analysis of Android image loading framework (8) takes you to fully understand the usage of Glide 4
Android componentization and plug-in
Why use componentization?
How do components communicate?
How to jump between components?
Sorting out Android plug-in and hot repair knowledge
Why use componentization
- Practice of Android complete componentization scheme
- Android completely componentized demo release
- Android is completely componentized - code and resource isolation
- Complete Componentization of Android - UI jump upgrade
- Android is completely componentized - how to use Arouter
Plug in framework history
Deep understanding of Android plug-in technology
Android hotness and plug-in repair knowledge
Due to space constraints, the detailed information of the document is too comprehensive and there are too many details, so only the screenshots of some knowledge points are roughly introduced. There are more detailed contents in each small node!
Well, this information will be introduced to you. If you need detailed documents, you can scan the QR code below on wechat for free~