Android Context详解

来源:互联网 发布:dbscan聚类算法 编辑:程序博客网 时间:2024/06/05 05:20

Context详解

类的继承关系

这里写图片描述

一个是ContextWrapper,一个是ContextImpl。那么从名字上就可以看出,ContextWrapper是上下文功能的封装类,而ContextImpl则是上下文功能的实现类。而ContextWrapper又有三个直接的子类,ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一个带主题的封装类,而它有一个直接子类就是Activity。
ContextWrapper.java

    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 void startActivity(Intent intent) {        mBase.startActivity(intent);    }    @Override    public boolean bindService(Intent service, ServiceConnection conn,            int flags) {        return mBase.bindService(service, conn, flags);    }    @Override    public Resources getResources()    {        return mBase.getResources();    }

看到没有ContextWrapper内部方法实际上其实都是调用mBase变量的相关方法,mBase是ContextImpl类型对象,见下面分析。这种模式也叫代理模式

APP Context总数 = Application数(1) + Activity数(Customer) + Service数(Customer);

Application

ActivityThread中handleBindApplication方法中会去创建Application对象,创建Application对象过程中会去设置mBase对象(实际为ContextImpl类型),然后调用Application的onCreate方法。。应用一启动,便执行handleBindApplication方法,同时只执行一次。同时也是优先于handleLaunchActivity/handleCreateService/handleBindService。所以说程序的入口其实是Application的onCreate方法,而不是activity或者是service的onCreate方法。。

    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.            Application app = data.info.makeApplication(data.restrictedBackupMode, null);            mInitialApplication = app;            ....            try {                mInstrumentation.callApplicationOnCreate(app);            } catch (Exception e) {                if (!mInstrumentation.onException(app, e)) {                    throw new RuntimeException(                        "Unable to create application " + app.getClass().getName()                        + ": " + e.toString(), e);                }            }        }        ......    }

makeApplication首先创建了Application对象,然后调用callApplicationOnCreate方法执行了application的onCreate方法。所以说application的构造函数优先于onCreate方法的

LoadedApk.java

    public Application makeApplication(boolean forceDefaultAppClass,            Instrumentation instrumentation) {        //如果不为空,直接返回,可以看到在performLaunchActivity方法在创建activity的时候也会调用makeApplication这个方法,但是因为已经创建了,所以不会重新创建application对象        if (mApplication != null) {            return mApplication;        }        Application app = null;        String appClass = mApplicationInfo.className;        if (forceDefaultAppClass || (appClass == null)) {            appClass = "android.app.Application";        }        try {            java.lang.ClassLoader cl = getClassLoader();            if (!mPackageName.equals("android")) {                initializeJavaContextClassLoader();            }            //首先执行ContextImpl.createAppContext方法,创建一个ContextImpl对象            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);            //创建了Application对象            app = mActivityThread.mInstrumentation.newApplication(                    cl, appClass, appContext);            appContext.setOuterContext(app);        } catch (Exception e) {            if (!mActivityThread.mInstrumentation.onException(app, e)) {                throw new RuntimeException(                    "Unable to instantiate application " + appClass                    + ": " + e.toString(), e);            }        }        mActivityThread.mAllApplications.add(app);        mApplication = app;        if (instrumentation != null) {            try {                instrumentation.callApplicationOnCreate(app);            } catch (Exception e) {                if (!instrumentation.onException(app, e)) {                    throw new RuntimeException(                        "Unable to create application " + app.getClass().getName()                        + ": " + e.toString(), e);                }            }        }        .........        return app;    }

首先执行ContextImpl.createAppContext方法,创建一个ContextImpl对象

    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");        return new ContextImpl(null, mainThread,                packageInfo, null, null, false, null, null);    }

然后app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);创建了Application对象
Application类中

    static public Application newApplication(Class<?> clazz, Context context)            throws InstantiationException, IllegalAccessException,             ClassNotFoundException {        Application app = (Application)clazz.newInstance();        app.attach(context);        return app;    }    final void attach(Context context) {        attachBaseContext(context);        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;    }

(Application)clazz.newInstance(); 此时如果应用继承了Application,那么此处就是自定义的Application.class了
ContextWrapper中有

    protected void attachBaseContext(Context base) {        if (mBase != null) {            throw new IllegalStateException("Base context already set");        }        mBase = base;    }

至此,Application就拿到了ContextImpl对象的引用了

    public final Application getApplication() {        return mApplication;    }
    @Override    public Context getApplicationContext() {        return (mPackageInfo != null) ?                mPackageInfo.getApplication() : mMainThread.getApplication();    }

Application的正确用法

在activity或者service中可以通过
这样的写法是大错特错的,此时app并不带有任何的context功能

    public class MyApplication extends Application {            private static MyApplication app;            public static MyApplication getInstance() {              if (app == null) {                  app = new MyApplication();              }              return app;          }      } 

因为会把mBase对象置空,就是没有ContextImpl对象的引用了

    public Application() {        super(null);    }    public ContextWrapper(Context base) {        mBase = base;    }

activity/service既可以通过getApplication也可以通过getApplicationContext。。

MyApplication app = (MyApplication) getApplication();

Activity

我们知道在启动一个activity的时候,执行完application相关的动作之后,会去调用handleLaunchActivity,这个方法先调用performLaunchActivity方法,创建activity,调用onCreate,onStart等方法,然后调用handleResumeActivity方法去执行onResume方法(这个执行完后视图才在屏幕上刷新,用户可见)

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {        .......        Activity activity = null;        try {            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();            //首先执行activity的构造器方法            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);            }        }        try {            //此时application对象其实上面已经创建过了,不会再创建了。。            Application app = r.packageInfo.makeApplication(false, mInstrumentation);            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);            if (localLOGV) Slog.v(                    TAG, r + ": app=" + app                    + ", appName=" + app.getPackageName()                    + ", pkg=" + r.packageInfo.getPackageName()                    + ", comp=" + r.intent.getComponent().toShortString()                    + ", dir=" + r.packageInfo.getAppDir());            if (activity != null) {                //创建ContextImpl对象                Context appContext = createBaseContextForActivity(r, activity);                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());                Configuration config = new Configuration(mCompatConfiguration);                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "                        + r.activityInfo.name + " with config " + config);                //attach方法是关键,拿到了ContextImpl对象和Application对象的引用                activity.attach(appContext, this, getInstrumentation(), r.token,                        r.ident, app, r.intent, r.activityInfo, title, r.parent,                        r.embeddedID, r.lastNonConfigurationInstances, config,                        r.voiceInteractor);        .......         return activity;    }

createBaseContextForActivity返回ContextImpl对象,关键来看activity的attach方法

    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, IVoiceInteractor voiceInteractor) {        attachBaseContext(context);        ......        //赋值了mApplication变量        mApplication = application;        ......    }

attachBaseContext方法上面已经介绍了,其实就是负责父类ContextWrapper中的mBase变量,activity间接继承自ContextWrapper。同时此时赋值了mApplication变量

getApplication和getApplicationContext区别

ContextWrapper.java

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

ContextImpl.java

    @Override    public Context getApplicationContext() {        return (mPackageInfo != null) ?                mPackageInfo.getApplication() : mMainThread.getApplication();    }

Activity.java/Service.java

    /** Return the application that owns this activity. */    public final Application getApplication() {        return mApplication;    }

此时mApplication就是activity中attach方法赋值的mApplication属性。

可以看到,其实返回的都是应用的唯一的Application对象而已,没有区别的。
另一方面,getApplication()方法的语义性非常强,一看就知道是用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。那么也许在绝大多数情况下我们都是在Activity或者Service中使用Application的,但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就可以借助getApplicationContext()方法了
BroadcastReceiver还有ContentProvider这两个组件是不继承自ContextImpl类的

总结

上面的分析可知
Application的Context生命周期与应用程序完全相同。
Activity或者Service的Context与他们各自类生命周期相同。
所以说对于Context使用不当会引起内存泄漏。譬如一个单例模式的自定义数据库管理工具类需要传入一个Context,而这个数据库管理对象又需要在Activity中使用,如果我们传递Activity的Context就可能造成内存泄漏,所以需要传递Application的Context。

参考

  1. Android应用Context详解及源码解析
  2. Android Context完全解析,你所不知道的Context的各种细节

Context以及ApplicationContext用处

context使用

测试代码

getApplication()返回的是一开始就创建的application对象 等同于getApplicationContext()
但是new BaseApp(),那么会把mBase置为空的,但是不影响一开始就创建的application对象。。。所以报错。。

    public Application() {        super(null);    }    public ContextWrapper(Context base) {        mBase = base;    }

测试一下:

startService(new Intent(this,MyService.class))getApplication().startService(new Intent(this,MyService.class));

毫无问题。。。实际上调用的都是ContextImpl中的startservice方法

BaseApp继承自Application,并写日mainfest.xml中

new BaseApp().startService(new Intent(this, MyService.class));

new BaseApp() 把mBase置为空了。。所以空指针
E/AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method ‘android.content.ComponentName android.content.Context.startService(android.content.Intent)’ on a null object reference
E/AndroidRuntime: at android.content.ContextWrapper.startService(ContextWrapper.java:581)

getApplicationContext().startActivity(new Intent(this, AnoActivity.class));

E/AndroidRuntime: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
E/AndroidRuntime: at android.app.ContextImpl.startActivity(ContextImpl.java:672)
E/AndroidRuntime: at android.app.ContextImpl.startActivity(ContextImpl.java:659)
E/AndroidRuntime: at android.content.ContextWrapper.startActivity(ContextWrapper.java:331)

调用的是contextimpl的startactivity方法

    @Override    public void startActivity(Intent intent, Bundle options) {        warnIfCallingFromSystemProcess();        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {            throw new AndroidRuntimeException(                    "Calling startActivity() from outside of an Activity "                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."                    + " Is this really what you want?");        }        mMainThread.getInstrumentation().execStartActivity(                getOuterContext(), mMainThread.getApplicationThread(), null,                (Activity) null, intent, -1, options);    }

但是此时如果设置intent的flag为FLAG_ACTIVITY_NEW_TASK,就正常启动了。直接使用mInstrumentation,让AMS启动一个activity

startActivity(new Intent(this, AnoActivity.class));
调用的是activity的方法,所以没错。。直接使用mInstrumentation,让AMS启动一个activity

0 0