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

🔥 Hi, I’m Chouchou. This paper route of “Android” | guide – from zero to infinity has included, there are Android advanced route notes & blog, welcome to follow peng ugly grows together. (Contact information at GitHub)


preface

  • Does View#getContext() always return an Activity object? The intuition is: View#getContext() must be an Activity if the View is managed by an Activity. Is it?
  • In this article, I will discuss with you the Context types, LayoutInflater layout parsing, and View system. Please be sure to like and follow if you can help, it really means a lot to me.

Related articles

  • The Android | a process how many the Context object (right not much)”
  • The Android | belt you explore LayoutInflater layout principle”
  • The Android | View & fragments & Window of getContext () must return to the Activity?”
  • The Android | about from Android: text to TextView process”

directory


1. Problem analysis

1.1 What are Contexts?

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. Call getBaseContext() to get the base object being proxied:

ContextWrapper.java

Context mBase;

public ContextWrapper(Context base) {
    mBase = base;
}

public Context getBaseContext() {
    return mBase;
}  
Copy the code

Note that activities can also be proxied as objects, like this:

Activity activity = ... ; Context wrapper = new ContextThemeWrapper(activity, themeResId); wrapper.startActivity(...) ; // OK wrapper instanceOf Activity // falseCopy the code

At this point, the Wrapper proxy object can use the Activity’s capabilities, startActivity(), or initialize the View, but it is not an Activity. Here, we seem to have found a hint of the problem: getContext() might return the Activity wrapper class instead of the Activity.

1.2 Problem Extension

In this article, we’re going to expand on the return value of View#getContext(). I’ll summarize the following cases to help you get a clearer picture:

    1. View#getContext()
    1. Fragment#getContext()
    1. Window#getContext()
    1. Dialog#getContext()

2. Return value of View#getContext()

View#getContext() returns the value set in the constructor. No other assignment statements are found in the source code. So, the key to this problem is to look at the Context object passed to the constructor when the View is instantiated.

View.java

@hide protected Context mContext; public final Context getContext() { return mContext; } public View(Context context) { mContext = context; . }...Copy the code

When using a View, there are two ways to instantiate a View:

  • Method 1: code call, like this:new TextView(Context)

Obviously, whenever you pass in an object, you call getContext() in the future, and you get the same object. Recalling the discussion in Section 1, you can pass in an Activity or a wrapper class. Hey, can you pass in Service, Application, ContextImpl? You can, but you want to make sure that the behavior after getContext() is correct, and you don’t usually do that.

New TextView(Activity), New TextView(ContextWrapper), new TextView(Service), you don't normally do that. New TextView(Application), you don't normally do that TextView(ContextImpl) doesn't normally do thisCopy the code
  • Method 2: Layout file, like this:<TextView ... >

This takes advantage of LayoutInflater’s ability to parse layouts, which we discussed earlier in this article: The Android | belt you explore LayoutInflater layout principles, if you’re LayoutInflater layout parsing process is still not familiar with, can first review, the same place not repeat. Here, we focus only on the View instantiated with reflection:

As you can see, instantiate the View where reflection is used, while Constructor#newInstance(…) The first argument to getContext() is the object to be returned in the future. So what is mConstructorArgs[0]? Let’s look against the source code:

LayoutInflater.java

public final View createView(@NonNull Context viewContext, @NonNull String name, @Nullable String prefix, @Nullable AttributeSet attrs){ ... Question: What is a viewContext? mConstructorArgs[0] = viewContext; final View view = constructor.newInstance(mConstructorArgs); . } createViewFromTag() -> createView() View createViewFromTag(View parent, String name, Context) AttributeSet attrs, boolean ignoreThemeAttr) { 1. Apply ContextThemeWrapper to support Android: Theme if (! ignoreThemeAttr) { final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); if (themeResId ! Context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); } Factory2 / Factory2 / Factory2 Using mPrivateFactory to instantiate a View is equivalent to intercepting 4. If (view == null) {view = createView(name, null, attrs); } return view; } // inflate() -> createViewFromTag() public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { ... Note: mContext final Context inflaterContext = mContext; . final View temp = createViewFromTag(root, name, inflaterContext, attrs); . } protected LayoutInflater(Context context) { mContext = context; initPrecompiledViews(); }Copy the code

AppCompatViewInflater.java

Factory2 / Factory instantiate final View createView(... { final Context originalContext = context; 2.1 application ContextThemeWrapper to support android: theme/app: theme if (readAndroidTheme | | readAppTheme) {context = themifyContext(context, attrs, readAndroidTheme, readAppTheme); } if (wrapContext) {2.2 Apply ContextThemeWrapper to support vector tint context = tintContextwrapper.wrap (context); } View view = null; Switch (name) {case "TextView": 2.3 Instantiating AppCompatTextView View = createTextView(Context, attrs); break; . default: view = createView(context, name, attrs); } return view; } -> 2.1 Apply ContextThemeWrapper to support Android :theme (simplified) private static Context themifyContext(Context Context, AttributeSet attrs, Boolean useAndroidTheme, Boolean useAppTheme) {// In fact, branch 1.1 has been handled, here is compatible with Android 5.0 before. return new ContextThemeWrapper(context, themeId); } -> 2.2 Applying ContextThemeWrapper to support vector images Android :tint public static Context wrap(@nonnull final Context Context) { return new TintContextWrapper(context); }Copy the code

AppCompatTextView.java

-> 2.3 Instantiate AppCompatTextView public AppCompatTextView(Context Context, AttributeSet attrs, int defStyleAttr) { super(TintContextWrapper.wrap(context), attrs, defStyleAttr); }Copy the code

The code above is fairly simplified, but you can also look at the conclusion directly:

Summary:

  • Branch 1.1: Apply ContextThemeWrapper to support itandroid:themeView#getContext() returns the wrapper class;
  • Branch 2.1: Apply ContextThemeWrapper to support itandroid:theme(In fact, branch 1.1 has already been handled, here it is compatible with Android 5.0 before), also returns the wrapper class;
  • Branch 2.2: Apply ContextThemeWrapper to support vector diagramsandroid:tintThis is to be compatible with Android 5.0 before it does not support TINt, and is also back to wrapper classes;
  • Branch 2.3: Instantiate AppCompatTextView, which also returns the wrapper class;
  • Branch 4: returns LayoutInflater#mContext, which isLayoutInflater.from(Context)Parameters passed in. In the Android | belt you explore LayoutInflater layout principles, we discussed:In the Activity/Fragment/View/Dialog, getLayoutInflater#getContext(), returns the Activity.

After the discussion in Section 2, the following sections are much easier.


3. The return value of getContext() for Dialog & Window

Direct look at the source code:

Window.java

private final Context mContext;

public final Context getContext() {
    return mContext;
}

public Window(Context context) {
    mContext = context;
    mFeatures = mLocalFeatures = getDefaultFeatures(context);
}
Copy the code

Activity.java

final void attach(Context context, ActivityThread aThread,...) {... MWindow = new PhoneWindow(this, window, activityConfigCallback); . }Copy the code

Dialog.java

public Dialog(@NonNull Context context, @StyleRes int themeResId) { this(context, themeResId, true); } Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { if (createContextThemeWrapper) { if (themeResId == Resources.ID_NULL) { final TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true); themeResId = outValue.resourceId; } wrap as ContextThemeWrapper mContext = new ContextThemeWrapper(context, themeResId); } else { mContext = context; }... final Window w = new PhoneWindow(mContext); . }Copy the code

Summary:

  • Dialog#getContext() returns ContextThemeWrapper;
  • In the Activity, Window#getContext() returns the Activity; In Dialog, Window#getContext() returns ContextThemeWrapper;

4. Return value of Fragment#getContext()

Direct look at the source code:

Fragment.java

FragmentHostCallback mHost;

public Context getContext() {
    return mHost == null ? null : mHost.getContext();
}
Copy the code

FragmentHostCallback.java

Context getContext() {
    return mContext;
}

FragmentHostCallback(FragmentActivity activity) {
    this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/);
}

FragmentHostCallback(Activity activity, Context context, Handler handler, int windowAnimations) {
    mActivity = activity;
    mContext = Preconditions.checkNotNull(context, "context == null");
    mHandler = Preconditions.checkNotNull(handler, "handler == null");
    mWindowAnimations = windowAnimations;
}
Copy the code

FragmentActivity.java

final FragmentController mFragments = FragmentController.createController(new HostCallbacks()); class HostCallbacks extends FragmentHostCallback<FragmentActivity> { public HostCallbacks() { super(FragmentActivity.this /*fragmentActivity*/); }... }Copy the code

Summary:

  • Fragment#getContext() returns Activity;

5. Get the Activity object from View#getContext()

In many scenarios, it is often necessary to obtain an Activity object from a View. From the previous sections, we have seen that the return value of View#getContext() can be in one of five cases:

Activity ContextWrapper Service will not be ContextImplCopy the code

So, to get an Activity, you just need to constantly get the proxied object of the Context (the base object) to get the Activity; Of course, the following Service & Application & ContextImpl cases return null, so we use @nullable.

Writing recursively:  @Nullable private static Activity findActivity(Context context) { if (context instanceof Activity) { return (Activity) context; } else if (context instanceof ContextWrapper) { return findActivity(((ContextWrapper) context).getBaseContext()); } else { return null; @nullable public static Activity findActivity(Context Context){Context cur = Context; while (true){ if (cur instanceof Activity){ return (Activity) cur; } if (cur instanceof ContextWrapper){ ContextWrapper cw = (ContextWrapper) cur; cur = cw.getBaseContext(); }else{ return null; }}}Copy the code

6. Summary

  • Takes an exam the advice
    • It can be any of Application, Service, ContextImpl, ContextWrapper, or Activity.
    • You should be familiar with the source code of Context types, LayoutInflater layout parsing, View system, etc., which can not only answer the questions in this article, but also solve more interesting/in-depth questions.

Recommended reading

  • Cryptography | is Base64 encryption algorithm?
  • Interview questions | back algorithm framework to solve problems
  • The interview questions | list questions summary algorithm
  • Java | show you understand the ServiceLoader principle and design idea
  • Android | interview will ask Handler, are you sure you don’t look at it?
  • Android | show you understand NativeAllocationRegistry principle and design idea
  • Computer composition principle | Unicode utf-8 and what is the relationship?
  • | why floating-point arithmetic of computer constitute principle is not accurate? (Ali written test)
  • Computer network | graphic DNS & HTTPDNS principle

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