Android之Context进阶

一、Context作用是什么?

  下面看下Context源码中的注释:

/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */

  意思是,Context是Android系统提供的一个抽象类,Context具有应用环境的全局信息。它(Context对象)允许访问应用特定的资源和类,以及调用应用级的方法,比如:launching Activities、broadcasting和receiving intents等。

  Context抽象类及部分抽象函数:

/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */
public abstract class Context {

    ……

    /**
     * Returns an AssetManager instance for the application's package.
     * <p>
     * <strong>Note:</strong> Implementations of this method should return
     * an AssetManager instance that is consistent with the Resources instance
     * returned by {@link #getResources()}. For example, they should share the
     * same {@link Configuration} object.
     *
     * @return an AssetManager instance for the application's package
     * @see #getResources()
     */
    public abstract AssetManager getAssets();

    /**
     * Returns a Resources instance for the application's package.
     * <p>
     * <strong>Note:</strong> Implementations of this method should return
     * a Resources instance that is consistent with the AssetManager instance
     * returned by {@link #getAssets()}. For example, they should share the
     * same {@link Configuration} object.
     *
     * @return a Resources instance for the application's package
     * @see #getAssets()
     */
    public abstract Resources getResources();

    /** Return PackageManager instance to find global package information. */
    public abstract PackageManager getPackageManager();

    /**
     * Return the context of the single, global Application object of the
     * current process.  This generally should only be used if you need a
     * Context whose lifecycle is separate from the current context, that is
     * tied to the lifetime of the process rather than the current component.
     *
     * <p>Consider for example how this interacts with
     * {@link #registerReceiver(BroadcastReceiver, IntentFilter)}:
     * <ul>
     * <li> <p>If used from an Activity context, the receiver is being registered
     * within that activity.  This means that you are expected to unregister
     * before the activity is done being destroyed; in fact if you do not do
     * so, the framework will clean up your leaked registration as it removes
     * the activity and log an error.  Thus, if you use the Activity context
     * to register a receiver that is static (global to the process, not
     * associated with an Activity instance) then that registration will be
     * removed on you at whatever point the activity you used is destroyed.
     * <li> <p>If used from the Context returned here, the receiver is being
     * registered with the global state associated with your application.  Thus
     * it will never be unregistered for you.  This is necessary if the receiver
     * is associated with static data, not a particular component.  However
     * using the ApplicationContext elsewhere can easily lead to serious leaks
     * if you forget to unregister, unbind, etc.
     * </ul>
     */
    public abstract Context getApplicationContext();

    /** Return the name of this application's package. */
    public abstract String getPackageName();

    ……
    
}

二、Context是一个抽象类,那么,它的实现类有哪些?

  Context的实现类分别是ContextImpl和ContextWrapper。

  ContextImpl类:

  源码:frameworks/base/core/java/android/app/ContextImpl.java

/**
 * Common implementation of Context API, which provides the base
 * context object for Activity and other application components.
 */
class ContextImpl extends Context {
    private final static String TAG = "ContextImpl";
    private final static boolean DEBUG = false;

    private static final String XATTR_INODE_CACHE = "user.inode_cache";
    private static final String XATTR_INODE_CODE_CACHE = "user.inode_code_cache";

    /**
     * Map from package name, to preference name, to cached preferences.
     */
    @GuardedBy("ContextImpl.class")
    @UnsupportedAppUsage
    private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;

    /**
     * Map from preference name to generated path.
     */
    @GuardedBy("ContextImpl.class")
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    private ArrayMap<String, File> mSharedPrefsPaths;

    @UnsupportedAppUsage
    final @NonNull ActivityThread mMainThread;
    @UnsupportedAppUsage
    final @NonNull LoadedApk mPackageInfo;
    @UnsupportedAppUsage
    private @Nullable ClassLoader mClassLoader;

    private final @Nullable IBinder mToken;

    private final @NonNull UserHandle mUser;

    @UnsupportedAppUsage
    private final ApplicationContentResolver mContentResolver;

    @UnsupportedAppUsage
    private final String mBasePackageName;
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    private final String mOpPackageName;

    ……
    
}

  ContextImplt类应用在哪里?

  应用中Application、Acitivity、Service、View等里的Context对象都是ContextImpl的实例化。

  通过ContextImpl对象能够获取package信息、package管理、资源、资源管理、主题等信息。

  ContextWrapper类:

  源码:frameworks/base/core/java/android/content/ContextWrapper.java

/**
 * Proxying implementation of Context that simply delegates all of its calls to
 * another Context.  Can be subclassed to modify behavior without changing
 * the original Context.
 */
public class ContextWrapper extends Context {
    @UnsupportedAppUsage
    Context mBase;

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

    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     *
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

    /**
     * @return the base context as set by the constructor or setBaseContext
     */
    public Context getBaseContext() {
        return mBase;
    }

    @Override
    public AssetManager getAssets() {
        return mBase.getAssets();
    }

    @Override
    public Resources getResources() {
        return mBase.getResources();
    }

    @Override
    public PackageManager getPackageManager() {
        return mBase.getPackageManager();
    }

    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }

    @Override
    public Looper getMainLooper() {
        return mBase.getMainLooper();
    }

    @Override
    public Executor getMainExecutor() {
        return mBase.getMainExecutor();
    }

    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }

    @Override
    public void setTheme(int resid) {
        mBase.setTheme(resid);
    }

    /** @hide */
    @Override
    @UnsupportedAppUsage
    public int getThemeResId() {
        return mBase.getThemeResId();
    }

    @Override
    public Resources.Theme getTheme() {
        return mBase.getTheme();
    }

    @Override
    public ClassLoader getClassLoader() {
        return mBase.getClassLoader();
    }

    @Override
    public String getPackageName() {
        return mBase.getPackageName();
    }

    ……

}

  ContextWrapper是Application类、Activity类、Service类的基类。

  ContextWrapper中的函数实现都是通过静态代理,通过mBase(ContextImpl实例)对象获取的。

三、应用中哪些组件拥有Context对象

  应用中Application、Activity、Service拥有自己的Context对象,而Broadcasting、Content provider是没有自己的Context对象的,是由外部传入的。

  1. Application Context实现及创建

  Application类:
/**
 * Base class for maintaining global application state. You can provide your own
 * implementation by creating a subclass and specifying the fully-qualified name
 * of this subclass as the <code>"android:name"</code> attribute in your
 * AndroidManifest.xml's <code>&lt;application&gt;</code> tag. The Application
 * class, or your subclass of the Application class, is instantiated before any
 * other class when the process for your application/package is created.
 *
 * <p class="note"><strong>Note: </strong>There is normally no need to subclass
 * Application.  In most situations, static singletons can provide the same
 * functionality in a more modular way.  If your singleton needs a global
 * context (for example to register broadcast receivers), include
 * {@link android.content.Context#getApplicationContext() Context.getApplicationContext()}
 * as a {@link android.content.Context} argument when invoking your singleton's
 * <code>getInstance()</code> method.
 * </p>
 */
public class Application extends ContextWrapper implements ComponentCallbacks2 {
    private static final String TAG = "Application";
    @UnsupportedAppUsage
    private ArrayList<ComponentCallbacks> mComponentCallbacks =
            new ArrayList<ComponentCallbacks>();
    @UnsupportedAppUsage
    private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
            new ArrayList<ActivityLifecycleCallbacks>();
    @UnsupportedAppUsage
    private ArrayList<OnProvideAssistDataListener> mAssistCallbacks = null;

    /** @hide */
    @UnsupportedAppUsage
    public LoadedApk mLoadedApk;

……

}

  Application继承链:Application -> ContextWrapper -> Context。

  Application创建

 

  AMS通知应用创建Application,创建Application实现,在ActivityThread类中handleBindApplication(...)函数:

@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
    ……
    try {
        // If the app is being launched for full backup or restore, bring it up in
        // a restricted environment with the base application class.
        app = data.info.makeApplication(data.restrictedBackupMode, null);

        ……
    } finally {
        ……
    }

    ……
}

  LoadedApk类makeApplication(...)函数实现:

  LoadedApk源码:frameworks/base/core/java/android/app/LoadedApk.java

@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
    if (mApplication != null) {
        return mApplication;
    }

    ……

    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    // The network security config needs to be aware of multiple
    // applications in the same process to handle discrepancies
    NetworkSecurityConfigProvider.handleNewApplication(appContext);
    app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);

    ……
}

  创建Application实现,Instrumentation类中:

  Instrumentation源码:frameworks/base/core/java/android/app/Instrumentation.java

/**
 * Perform instantiation of the process's {@link Application} object.  The
 * default implementation provides the normal system behavior.
 * 
 * @param cl The ClassLoader with which to instantiate the object.
 * @param className The name of the class implementing the Application
 *                  object.
 * @param context The context to initialize the application with
 * 
 * @return The newly instantiated Application object.
 */
public Application newApplication(ClassLoader cl, String className, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = getFactory(context.getPackageName())
            .instantiateApplication(cl, className);
    app.attach(context);
    return app;
}

  从实现中可以看出,Applicaiton的Context是ContextImpl实例对象,后面调用app.attach()函数。

  Applicatoin类:

public class Application extends ContextWrapper implements ComponentCallbacks2 {

    ……

    @UnsupportedAppUsage
    /* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

    ……

}

  由于Appliation继承ContextWrapper类,最后会调用到函数attachBaseContext()为ContextWrapper的mBase成员变量赋值:

  ContextWrapper类:

/**
 * Proxying implementation of Context that simply delegates all of its calls to
 * another Context.  Can be subclassed to modify behavior without changing
 * the original Context.
 */
public class ContextWrapper extends Context {
    @UnsupportedAppUsage
    Context mBase;

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

    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     *
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }

    ……

}

  在ContextWrapper类中将ContextImpl实例对象赋值给mBase,而在ContextWrapper类的函数实现都是通过调用mBase对象调用具体实现函数,这里用到了静态代理设计模式

  如果,Hook了mBase,就可以将Application里的Context替换成开发自己的Context实现,在一些SDK里是这样用的。比如:多语言语料更新的SDK库。

  2. Activity Context创建及实现

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback,
        AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
       …… 
}

  Activity继承链:Activity -> ContextThemeWrapper -> ContextWrapper -> Context

  Activity创建:

  源码:frameworks/base/core/java/android/app/ActivityThread.java

  performLaunchActivity(...)函数实现Activity创建:

/**  Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

    ……

    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to instantiate activity " + component
                + ": " + e.toString(), e);
        }
    }

    ……

    activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);
    
    ……

    return activity;
}

   通过createBaseContextForActivity()函数创建ContextImpl类型的Context对象。mInstrumentation.newActivity(...)函数创建Activity对象。

  Instantiation类中newActivity(...)函数:

  源码:frameworks/base/core/java/android/app/Instantiation.java

/**
 * Perform instantiation of the process's {@link Activity} object.  The
 * default implementation provides the normal system behavior.
 * 
 * @param cl The ClassLoader with which to instantiate the object.
 * @param className The name of the class implementing the Activity
 *                  object.
 * @param intent The Intent object that specified the activity class being
 *               instantiated.
 * 
 * @return The newly instantiated Activity object.
 */
public Activity newActivity(ClassLoader cl, String className,
        Intent intent)
        throws InstantiationException, IllegalAccessException,
        ClassNotFoundException {
    String pkg = intent != null && intent.getComponent() != null
            ? intent.getComponent().getPackageName() : null;
    return getFactory(pkg).instantiateActivity(cl, className, intent);
}

  在返回Activity对象后,通过调用activity.attach(...)函数为ContextImpl对象成员变量mBase赋值,流程和Applicaiton一样,在performLaunchActivity(...)函数中:

/**  Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

    ……

    activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);
    
    ……

    return activity;
}

  在Activity类中attach(...)函数实现:

@UnsupportedAppUsage
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {

    attachBaseContext(context);

    ……
    
}

  ContextWrapper类中attachBaseContext(Context base)函数实现:

/**
    * Set the base context for this ContextWrapper.  All calls will then be
    * delegated to the base context.  Throws
    * IllegalStateException if a base context has already been set.
    *
    * @param base The new base context for this wrapper.
    */
protected void attachBaseContext(Context base) {
    if (mBase != null) {
        throw new IllegalStateException("Base context already set");
    }
    mBase = base;
}

 

  3. Service Context的创建与实现

public abstract class Service extends ContextWrapper implements ComponentCallbacks2,
        ContentCaptureManager.ContentCaptureClient {
    ……
}

  Service继承链:Service -> ContextWrapper -> Context

  Service创建实现:

  源码:frameworks/base/core/java/android/app/ActivityThread.java

  创建Service函数:handleCreateService(...):

@UnsupportedAppUsage
private void handleCreateService(CreateServiceData data) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    unscheduleGcIdler();

    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
        // Service resources must be initialized with the same loaders as the application
        // context.
        context.getResources().addLoaders(
                app.getResources().getLoaders().toArray(new ResourcesLoader[0]));

        context.setOuterContext(service);
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        service.onCreate();
        mServices.put(data.token, service);
        try {
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(service, e)) {
            throw new RuntimeException(
                "Unable to create service " + data.info.name
                + ": " + e.toString(), e);
        }
    }
}

  通过ContextImpl.createAppContext(...)函数创建ContextImpl实例对象context。

  通过packageInfo.getAppFactory().instantiateService(...)函数创建Service对象。

  最后,通过attach(context, ...)函数,将context对象作为参数传入,在调用attachBaseContext(context)函数给ContextWrapper类的成员变量mBase(ContextImpl)。流程和Application一样。

  Service类中attach(...)函数实现:

@UnsupportedAppUsage
public final void attach(
        Context context,
        ActivityThread thread, String className, IBinder token,
        Application application, Object activityManager) {
    attachBaseContext(context);
    mThread = thread;           // NOTE:  unused - remove?
    mClassName = className;
    mToken = token;
    mApplication = application;
    mActivityManager = (IActivityManager)activityManager;
    mStartCompatibility = getApplicationInfo().targetSdkVersion
            < Build.VERSION_CODES.ECLAIR;

    setContentCaptureOptions(application.getContentCaptureOptions());
}

  ContextWrapper类中attachBaseContext(Context base)函数实现:

/**
    * Set the base context for this ContextWrapper.  All calls will then be
    * delegated to the base context.  Throws
    * IllegalStateException if a base context has already been set.
    *
    * @param base The new base context for this wrapper.
    */
protected void attachBaseContext(Context base) {
    if (mBase != null) {
        throw new IllegalStateException("Base context already set");
    }
    mBase = base;
}

  

四、总结

   1. 在应用中,一共有多少个Context对象?

    Application、Activity、Service拥有自己的Context对象,而广播、Content Provider的Context对象是外部传入的。所以,Context对象数量 = Application对象 + Activity对象 + Service对象。

  2. Context作用?

    Context,它允许访问应用特定的资源和类,也可以调用应用级别的方法,比如:startActivity(...),sendBroadcase(...),getResources()等。

  

 

  

  

posted @ 2021-09-10 12:12  naray  阅读(307)  评论(0编辑  收藏  举报