Understand the most familiar Context in android

Introduction of Context

Insert picture description here
Context is almost ubiquitous in Android development, and it is really familiar to development. But do you really understand it? Is it not clear when using it? And you may cause a memory leak if you are not careful.

Since there are different types of context in Android, as Android development, we may not know which context to use in a certain position at the beginning. So, let's see how to use Context correctly below.

Android mainly has two types of contexts:

Application Context: This is a singleton, which can be obtained by using getApplicationContext() in the activity. It is related to the life cycle of the application.

Activity Context: This is Activity. For example-MainActivity. It is only an instance of MainActivity.

Context provides an interface for global information about the application environment. It is an abstract class. Its execution is provided by the Android system. It allows access to resources and types characterized by applications. It is a context that governs some resource APP environment variables (including application-level operations, such as starting Activity, broadcasting, and receiving intent, etc.). Abstract will have its implementation class. In the source code, we can view its subclasses through AndroidStudio, and get the following relationship:
It has 2 concrete implementation subclasses: ContextImpl and ContextWrapper.
Insert picture description here
Among them ContextImpl, Context is the implementation class, and ContextWrapperit is the wrapper class of Context.

Activity, Application, and Service are all inherited from ContextWrapper(Activity inherits from ContextThemeWrapper, a subclass of ContextWrapper), but they all create ContextImplobjects during their initialization , and ContextImpl implements the methods in Context.

How many contexts are there in the app

Context数量 = Activity数量 + Service数量 + 1

The 1 above represents the number of Applications, because an application can have multiple Activities and multiple Services, but there can only be one Application.

Scope of Context

It is not enough to get a Context instance casually, there are some rules and restrictions on its use. Because the specific instance of Context is implemented by the ContextImpl class, the three types of Context, Activity, Service, and Application, are all equivalent. However, it should be noted that some scenarios, such as starting Activity, popping up Dialog, etc. For safety, Android does not allow Activity or Dialog to appear out of thin air. The startup of an Activity is definitely responsible for another Activity, which is the return stack formed by this, and the Dialog must be popped up on an Activity (system Alert type Dialog Except), in this case, we can only use Activity type Context, otherwise an error will be reported.

Activity is inherited from ContextThemeWrapper, and Application and Service are inherited ContextWrapper, so some operations are made ContextThemeWrapperon ContextWrapperthe basis of this, which makes Activity more powerful.

Insert picture description here
Regarding the two situations where Application and Service mentioned in the table are not recommended:

If you use ApplicationContext to start an Activity with a LaunchMode of standard, an error will be reported:

androud,util.AndroidRuntimeException:Calling startActivity from outside of an Activity context require the FLAG_ACTIVITY_NEW_TASK flag。Is this really what you want?

Translate and understand this FLAG knows that the non-Activity type Context at this time does not have a so-called return stack, so the activity with startup cannot find the stack. It also gives us a clear solution to FLAG, so that a new task stack is created for it when it is started, and the Activity is started in Single Task mode at this time. Therefore, this method of starting Activity with Application Context is not recommended, and Service is the same.
It is also legal to use layout inflate in Application and Service, but the system default theme style will be used. If some styles are customized, they may not be used, so it is not recommended.

Note: All related to UI should be handled by Activity Context. For some other operations, instances such as Service, Activity, and Application are all possible.
At the same time, pay attention to the Context's reference holding to prevent memory leaks. When it is destroyed, set Context to null.

How to get the Context object

1 View.getContext().: Returns the Context object of the current View object. Usually it is the Activity object currently being displayed.

2 Activity,getApplicationContext().: Get the Context object of the application process where the current Activity is located. Usually, when we use the Context object, we must give priority to this global process Context.

3 ContextWrapper.getBaseContext().: Used to obtain a ContextWrapper before decorating the Context. It is rarely used in actual development and is not recommended.

4 Activity.this.: Return the current Activity instance, if the UI control needs to use Activity as the Context object, but the default ApplicationContext actually used by Toast can also be used.

In the implementation of the View.OnClick monitoring method, write Toast instead of this, because this, in onClick (View view) refers to the view object instead of the Activity instance, so in this method, you should use the "current Activity name.this" , This is where beginners are more likely to confuse.

getApplication() and getApplicationContext():

Get the current Application object with getApplicationContext. But what is getApplication.

Application app=(Application)getApplication();
Log.e(TAG,"getApplication is "+app);
Context context=getApplicationContext();
Log.e(TAG,"getApplicationContext is "+ context);

Look at logcat after running. It can be seen from the printed results that their two memory addresses are the same, that is, they are the same object. But these two methods have a big difference in scope.

At a glance, getApplication() knows that it is used to obtain an Application instance (the reason can be thought of as getActivity()).

But getApplication() can only be called in Activity and Service.

For example, BroadcastReceiver, etc. also want to obtain Application instances, then the getApplicationContext() method is required.

//继承BroadcastReceiver并重写onReceive()方法
@Override
public void onReceive(Context context.Intent intent){
    
    
	Application app=(Application)context.getApplicationContext();
}

We often encounter memory leaks. For example, the Activity is destroyed, but the Context still holds a reference to the Activity, causing a memory leak. (Frequently encountered)

Two typical ways of misquoting:

Wrong singleton pattern:

public class Singleton{
    
    
	private static Singleton instancel
	private Context context;
	private Singleton(Context context){
    
    
		this.context=context;
	}
	public static Singleton getInstance(Context context){
    
    
		if(instance == null ){
    
    
			instance=new Singleton(context);
		}
		return instance;
	}
}

Those who are familiar with the singleton mode know that this is a non-thread-safe singleton mode. As a static object, instance has a longer life cycle than ordinary objects (a singleton is not destroyed until the APP exits the background), and it also contains Activity. For example, Activity A goes to getInstance() to get the instance object, and passes in this. The Singleton of the resident memory saves the A object we passed in and holds it forever. Even if the Activity is destroyed, its reference still exists in a Singleton. In the process, it is impossible to be GC (garbage collection), which leads to a memory leak. For example, typical database operations, storing data, need to repeatedly request data, use singleton to hold data and get Activity to hold context reference, because singleton can be regarded as God, it helps us save data. So even if the Activity is finished, there is still a reference to it in Singleton.

View holds Activity reference:

public class MainActivity extend Activity{
    
    
	private static Drawable mDrawable;	
	@Override
	protected void onCreate(Bundle saveInstanceState){
    
    
		super.onCreate();
		setContentView(R.layout.activity_main);
		ImageView imageview=new ImageView(this);//通过代码动态的创建组件,而不是传统的xml配置组件,这里的ImageView持有当前Activity的引用。
		mDrawable=getResources().getDrawable(R.drawable.ic_launcher);
		imageview.setImageDrawable(mDrawable);
	}
}

In the above code, there is a static Drawable object. When ImageView sets this Drawable, ImageView saves a reference to this mDrawable, and this is passed in when ImageView is initialized, where this refers to the context of MainActivity. Because mDrawable modified by static is resident in memory (loaded earlier than the class). MainActivity is an indirect reference to it. When MainActivity is destroyed, it cannot be GC off, which causes a memory leak.

How to use Context correctly in the program:

Generally, memory leaks caused by Context are almost always when the Context is destroyed, because it is referenced and the destruction fails. The Context object of the Application can be simply understood as the existence of the process (its life cycle is also very long, after all, the Application is loaded first when the APP is loaded, we can customize the Application and then inherit the Application of the system).
use correctly:

1. When the Context of Applicatin can be fixed, and objects with a long life cycle, use the Context of Application first;
2. Don't let objects with a life cycle longer than Activity hold Activity references.

3. Try not to use non-static inner classes in Activity. Non-static inner classes will implicitly hold references to instances of outer classes. If you use a static inner class, hold the external instance reference as a weak reference.

Interview:

When was the ContextImpl instance generated? Can you get this instance in the onCreate of Activity?

Let me start with the conclusion, yes. When we develop, we often get the Application in onCreate. If we get it with getApplicationContext, we will finally call the getApplicationContext method of ContextImpl. If we call the getApplication method, although ContextImpl is not called, it returns Activity's member variables mApplication and ContextImpl. The timing of initialization is the same.

Let's talk about its principle. The real start of Activity starts from ActivityThread.performLaunchActivity. This method does these things:

Load the target Activity's class through ClassLoader to create an object

Get the Application object from packageInfo

Call the createBaseContextForActivity method to create ContextImpl

Calling activity.attach (contextImpl, application) This method associates Activity with Application and ContextImpl, which is the same timing as mentioned in the above conclusion

Finally call activity.onCreate life cycle callback

Through the above analysis, we know that Activity first creates the class, then initializes the Context, and finally calls onCreate to get the answer to the question. This is not only the case with Activity, but also the Context initialization in Application and Service.

to sum up:

The Context class is just an abstract class that defines a set of abstract methods, and the real implementation of its internal methods is in the ContextImpl class.

ContextWrapper is a wrapper class of Context. All the method implementations in it are methods of calling its internal mBase variables, and mBase is the ContextImpl object. However, ContextWrapper also has a subclass of ContextThemeWrapper, which extends theme-related methods. Application and Service are inherited from ContextWrapper, and Activity is inherited from ContextThemeWrapper. When Activity starts, the system will load a theme, which is the android:theme=”@style/AppTheme” attribute that we usually write in the AndroidManifest.xml file! However, Service and Applicaton are not related to the UI interface! Therefore they inherit from ContextWrapper. So Activity, Application, and Service are actually associated with an mBase variable, and the mBase variable is the assignment of the ContextImpl object, and it is also the place where the abstract class Context is actually implemented.

ContextImpl implements all the methods in the abstract class Context, acquire resources, start Activity, Service, etc. It is worth noting that when ContextImpl is created, the static area is used to register various services of the system, so every class that holds a Context reference can easily obtain system services through getSystemService. For example, when we usually load an XML layout with the LayoutInflater class, the LayoutInflater layout loader also calls context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
OK, the context is finally explained, and we can use the context comfortably in the future! If you think you have learned something, please like it! ! Hehe

This reference


Reference link 1

Reference link 2

Reference link 3

Reference link 4

Reference link 5

Reference link 6

Guess you like

Origin blog.csdn.net/i_nclude/article/details/106245428