Like attention, no more lost, your support means a lot to me!

🔥 Hi, I’m Chouchou. GitHub · Android-Notebook has been included in this article. Welcome to grow up with Chouchou Peng. (Contact information at GitHub)

preface

  • In Android, when using a three-party library or a two-party library, you often need to initialize the Context. The general approach is to call the initialization method of the repository and pass in the appropriate Context object.
  • In this article, I will introduce a non-intrusive approach to retrieving a Context based on the ContentProvider startup mechanism. Please be sure to like and follow if you can help, it really means a lot to me.

Related articles

  • The Android | use ContentProvider without intrusion to obtain the Context”
  • The Android | a process how many the Context object (right not much)”
  • The Android | food tasteless! App Startup’s easier than you might think”

directory


1. General method of getting Context

First of all, we review the Context and its subclasses, in before this article, we have discussed: the Android | a process how many the Context object (right not much). To put it simply: Context uses decorator mode, and all contexts except ContextImpl are subclasses of ContextWrapper.

Activity, Service, and Application are subclasses of ContextWrapper.

1.1 Obtaining an Application Object

Application objects have the longest lifetime and are often used to initialize third-party libraries, either by exposing the object using a static method or by initializing third-party libraries in Application#onCreate() :

MainApplication.kt

class MainApplication : Application() { companion object { lateinit var application: Override fun onCreate() {super.oncreate () Application = this... }}Copy the code

1.2 Getting the Activity & Service Object

Similarly, Activity & Service is the implementation class of the Context, so we can initialize third-party libraries as needed while the program is running. For example, when you use Glide, you don’t need to call Glide#with(Context) first, you just need to call it when the image is displayed.

1.3 summary

  • advantages

The most common approach is simple to implement with no performance/stability risk; Third party libraries can be initialized on demand & lazy loading;

  • disadvantages

The need to get ApplicationContext/Context (the dependency is strongly coupled to the library code) is not conducive to componentization.

Below, I will introduce two kinds of method without intrusion to obtain the Context, will involve the Android process startup process, if still not understand, please be sure to read the article: the Android | show you understand the Application creation process,


2. Reflect ActivityThread to get ApplicationContext (not recommended)

This section describes an approach to getting an Application from ActivityThread.java as follows:

2.1 Source Code Analysis

When an Activity, a Service, a ContentProvider, or a BroadcastReceiver is started, an Application object must be created. If the process is not started, an Application object must be created.

  • insystem_serverProcess, throughAMS#getProcessRecordLocked(...)Get process information (ProcessRecord);
  • If not, callAMS#startProcessLocked(...)Create a process;
  • After Zygote incubates the target process, it executes in the target process reflectionActivityThread#main()And eventually inActivityThread#handleBindApplication(...)Create an Application object in.

ActivityThread.java

Application mInitialApplication; public Application getApplication() { return mInitialApplication; } private void handleBindApplication(AppBindData data) { // ... Application app; / / data. The info for LoadedApk. Java app = data. Info. MakeApplication (data restrictedBackupMode, null); / /... mInitialApplication = app; / /... }Copy the code

As you can see, the Application object is stored in the mInitialApplication property, so if we can access this property, then we can get the Application object.

First, we need to get the ActivityThread object, so let’s look at the source code to create the ActivityThread object:

ActivityThread.java

private static volatile ActivityThread sCurrentActivityThread; public static ActivityThread currentActivityThread() { return sCurrentActivityThread; } public static void main(String[] args) {Looper. PrepareMainLooper (); ActivityThread thread = new ActivityThread(); thread.attach(false, startSeq); Looper.loop(); } private void attach(boolean system, long startSeq) { sCurrentActivityThread = this; }Copy the code

The ActivityThread object is stored in the sCurrentActivityThread static variable, so we can write reflection code.

2.2 Procedure

Context.kt

Private var application: Context? = null fun context(): Context { if (null == application) { try { val activityThread: Any val clazz = Class.forName("android.app.ActivityThread") val currentActivityThread = clazz.getMethod("currentActivityThread").apply { isAccessible = true } val getApplication = clazz.getMethod("getApplication").apply { isAccessible = true } activityThread = currentActivityThread.invoke(null) application= getApplication.invoke(activityThread) as Context } catch (e: }} return application!! }Copy the code

Run the test. Context () returns the result:

android.app.Application@c12661f
Copy the code

2.3 summary

  • Advantages:

The dependent side does not need to pass the Context object to the library for initialization, which reduces the code coupling and is conducive to componentization.

  • Disadvantages:

Private API needs to be called by reflection, which may lead to system version adaptation risk. The use of reflection has a certain performance loss.


3. Use ContentProvider to obtain the ApplicationContext

This section describes one way to get an Application through ContentProvider.java. The common use of contentProviders is to provide content services for current/remote processes, which are initialized when the application starts. As such, we can use contentProviders to get the Context.

3.1 Source Code Analysis

ActivityThread.java

private void handleBindApplication(AppBindData data) { // ... 1. Instantiate Application object Application APP; app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; Initialize allContentProvider installContentProviders(app, data.providers); 3, call the application # onCreate () mInstrumentation. CallApplicationOnCreate (app); . } -> public Application makeApplication(Boolean forceDefaultAppClass, Instrumentation instrumentation) { ... 1.1 the Context based object ContextImpl appContext = ContextImpl. CreateAppContext (mActivityThread, this); Context#attachBaseContext(...) app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext); 1.3 the Context based object holds the proxy class appContext. SetOuterContext (app); . Private void installContentProviders(Context Context, List<ProviderInfo> providers) { final ArrayList<ContentProviderHolder> results = new ArrayList<>(); for (ProviderInfo cpi : ContentProvider ContentProviderHolder CPH = installProvider(Context, null, CPI, false /* Noisy */, true /*noReleaseNeeded*/, true /*stable*/); if (cph ! = null) { cph.noReleaseNeeded = true; results.add(cph); }} / /... }Copy the code

As you can see, installContentProviders() will install the ContentProvider belonging to the current process in Context#attachBaseContext(…). Between Application#onCreate() :

3.2 Procedure

Once you understand the ContentProvider startup mechanism, you can use the ContentProvider to get the ApplicationContext. The specific steps are as follows:

Step 1: Implement the ContentProvider subclass
// ContextProvider.kt internal class ContextProvider : ContentProvider(){ override fun onCreate(): Boolean { init(context!!) Kt private lateInit var application: Context fun init(Context: Context){ application= context } fun context() : Context{ return application }Copy the code
Step 2: Configure in AndroidManifest
// AndroidManifest.xml

<application>
    <provider
        android:name=".Contextprovider"
        android:authorities="${applicationId}.contextprovider"
        android:exported="false" />
</application>
Copy the code
Step 3: Use
Toast.makeText(context(),"",Toast.LENGTH_SHORT).show()
Copy the code

3.3 summary

  • Advantages:

Dependencies do not need to pass Context objects to the library for initialization, reducing code coupling and facilitating componentization

  • Disadvantages:

Initialize the ContentProvider at App startup, not lazily; If the application has too many ContentProviders, the startup time of the application will be increased.

  • Risk:

Make sure the initialization is very light, otherwise it will slow down the startup of the App


Case 4.

Here are some examples of how to get a Context innocently using the ContentProvider mechanism:

  • LeakCanary 2.4

AppWatcherInstaller.java

internal sealed class AppWatcherInstaller : ContentProvider() { internal class MainProcess : AppWatcherInstaller() internal class LeakCanaryProcess : AppWatcherInstaller() override fun onCreate(): Boolean { val application = context!! .applicationContext as Application AppWatcher. ManualInstall (Application) return true}Copy the code
  • AutoSize 1.1.2

InitProvider.java

public class InitProvider extends ContentProvider { @Override public boolean onCreate() { AutoSizeConfig.getInstance() .setLog(true) .init((Application) getContext().getApplicationContext()) .setUseDeviceSize(false); return true; } // other methods directly return}Copy the code
  • Picasso was 2.7

PicassoProvider.java

public final class PicassoProvider extends ContentProvider { @SuppressLint("StaticFieldLeak") static Context context; @Override public boolean onCreate() { context = getContext(); return true; } // other methods directly return}Copy the code

Recommended reading

  • Cryptography | is Base64 encryption algorithm?
  • Interview questions | back algorithm framework to solve problems
  • The interview questions | list questions summary algorithm
  • Computer network | graphic DNS & HTTPDNS principle
  • Say from Android Android | : text to TextView process
  • Android | interview will ask Handler, are you sure you don’t look at it?
  • Android | show you explore LayoutInflater layout analysis principle
  • Android | View & fragments & Window getContext () must return to the Activity?

Thank you! Your “like” is the biggest encouragement for me! Welcome to attentionPeng XuruiThe lot!