Project Description
Let’s briefly describe the problems encountered first.
The project is relatively large and is built in the form of componentization. Recording the crash log is done by a special component, so let's call it crash here. The core logic of crash is as follows:
//pseudocodepublic class MyCrash implements UncaughtExceptionHandler { private static UncaughtExceptionHandler defaultUncaughtExceptionHandler; public static void init(String path) { ... //Get the default ExceptionHandler defaultUncaughtExceptionHandler = (); //Set your own ExceptionHandler (new MyCrash()); } @Override public void uncaughtException(Thread t, Throwable e) { try { //Log Logic } catch (IOException e) { (); } finally { //The default ExceptionHandler callback if (defaultUncaughtExceptionHandler != null) { (t, e); } } } }
Then the component is initialized using ContentProvider, roughly as follows:
class MyContentProvider : ContentProvider() { override fun onCreate(): Boolean { //Pseudocode, initialization. ("") return true } ...... }
Problem troubleshooting
Feedback said that some phones will not record crash logs. This is very strange, because theoretically, as long as ExceptionHandle is set, it will catch the transmitted exception. Is it not set to ExceptionHandle?
After breakpoint troubleshooting, the phone that will not upload the crash log will not be uploaded. During the run phase, the defaultUncaughtExceptionHandler held by Thread is not the MyCrash we set, but a three-party component sets their own CrashExceptionHandle and does not call back our MyCrash, and they are also initialized using ContentProvider.
So at this time, the initialization process of ContentProvider is involved. Specifically in ActivityThread, let’s put the pseudo-code below.
ActivityThread private void handleBindApplication(AppBindData data) { ... //1. Obtain Application app = (, null); ... //2. Initialize ContentProvider installContentProviders(app, ); ... //3. Call Application's onCreate (app); ... }
The overall order is to get Application->Initialize ContentProvider->Call Application#onCreate. In other words, the initialization of the ContentProvider must be before the Application. The initialization of ContentProvider is to loop to facilitate the storage of ContentProvider's collection to call its onCreate method.
private void installContentProviders(Context context, List<ProviderInfo> providers) { ... for (ProviderInfo cpi : providers) { //The ContentProvider will be obtained here, and the contentProvider's attachInfo will be called, and onCreate will be called in the attachInfo. } ... }
The initialization order of ContentProvider is very clear. Our problem is that some mobile phones cannot record them, which means that the order of ContentProvider in the collection is not guaranteed. This can explain that some mobile phones have problems and some mobile phones are normal. So how does this order come about?
At first I thought about whether the order was related to the order of ContentProvider nodes registered in the merged file, and then I excluded the idea. Because the generated APK is the same, the order of ContentProvider nodes registered in the file is certain. The problem is some mobile phones, so it must not be the problem here. There is no way to continue to check the source code and see how ContentProvider reads into memory.
After some searching, it was found that the collection of ContentProvider was obtained from ComponentResolver private final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();. Since there are many source codes involved, I won't list them one by one here. Here is a rough call chain of source code
ActivityManagerService#attachApplicationLocked -> ActivityManagerService#generateApplicationProvidersLocked -> PackageManagerService#queryContentProviders -> ComponentResolver#queryProviders -> ActivityThread#bindApplication -> ActivityThread#handleBindApplication
Let's pay attention to the key points. ContentProvider information is stored in ArrayMap, and ArrayMap definitely cannot guarantee the order. If you don't understand ArrayMap, I will briefly introduce it.
ArrayMap is a key-value mapping set specially provided by Google. It is mainly to solve the problem of HashMap waste controls. It performs well in small data volumes, but it uses arrays at the bottom and uses binary searches. The order of binary searches is based on the hash value. The default hash value is obtained through (key), and this thing is related to the object address. So different phone orders will definitely be different.
At this point, the problem analysis is over. The final solution is to remove the initialization mechanism using ContentProvider and directly initialize it in Application.
Summarize
Although the above problem has been solved, is it really good to use ContentProvider to decouple initialization components? There are several intuitive problems.
- Memory leak. After the initialization is completed, ContentProvider will be directly held by the system, which is useless, but it is not deleted.
- The order in which components are initialized cannot be guaranteed. This is the problem we analyzed above
- Will prolong the startup time. As we saw above, the Application#onCreate call will be made after the ContentProvider loop is initialized, so for some components that are not necessary to initialize in the main thread, this will undoubtedly lengthen the startup time.
However, if you have to decouple component initialization, you can take a look at the Jetpack startup component. It is also used for initialization using ContentProvider, but it will eventually merge into a ContentProvider using the merge function, and memory maintenance sets can ensure the initialization order of components.
In short, don't abuse ContentProvider to just do an initialization.
This is the end of this article about Android using ContentProvider to initialize components. For more related content on Android ContentProvider initialization components, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!