Android Context

 

 

6.1.1.          Context相关类关系

Context是用来保存应用的运行环境的,并提供应用的操作接口。其相关类和关系如下,

 

ContextWrapper,代理类,继承了Context,实现了父类方法,方法全都以类构建的时候传入的Context实例为基础实现。

ContextWrapper的子类会调用attachBaseContext,传入相应的实例Contextbase,它有三个主要子类:ContextThemeWrapper (Activity)、Service、Application。

///

     protectedvoid attachBaseContext(Context base) {

        if (mBase!= null) {

            thrownew IllegalStateException("Base context already set");

        }

        mBase =base;

    }

 

    @Override

    publicAssetManager getAssets() {

        returnmBase.getAssets();

    }

 

 

如果子类是service,则通过attach调用attachBaseContext()初始化mBase,

///

    public finalvoid 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;

    }

 

    如果子类是Application,通过attach调用attachBaseContext()初始化mBase,

///

     /* package*/ final void attach(Context context) {

       attachBaseContext(context);

        mLoadedApk =ContextImpl.getImpl(context).mPackageInfo;

    }

 

    如果子类是ContextThemeWrapper,先通过Activity的attach,再通过ContextThemeWrapper的attachBaseContext来初始化mBase,

///

     @Overrideprotected void attachBaseContext(Context newBase) {

       super.attachBaseContext(newBase);

        mBase =newBase;

    }

 

 

对于我们常见的应用来说,上面的Activity、Service、Application并不是孤立的,以应用启动为例:

 

当启动应用时,会孵化一个新进程,启动应用的启动入口为ActivityThread.main(),如下图,在其后续的makeapplication流程中,新建一个ContextImpl实例给application,注意这个Context的生命周期是跟随application的。可以在activity里通过getApplicationContext得到application的Context,

 

 

在后续的启动过程中,会进行activity的实例创建,其中ActivityThread.java调用performLaunchActivity,先通过createBaseContextForActivity创建一个Context,然后再activity.attach,注意这个Context的生命周期是跟随activity的。

 

同样,Service也有自己的Context,的生命周期是跟随自己的。

 

这里强调Context的生命周期和归属有两个原因,一个是内存泄漏,一个是非法调用。

 

 

 

对于内存泄漏部分,如果需要释放的对象关联错了Context,就会引起不能回收,例如附着于activity的对象,如果基于application的Context去创建,在activity退出就不能及时的被回收,引起内存泄漏。

 

对于非法调用部分,如在service里面调用startActivity,如果参数没有设置FLAG_ACTIVITY_NEW_TASK,则会报如下错误,

android.util.AndroidRuntimeException: Calling startActivity()from outside of an Activity  context requires theFLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

 

具体原因呢,通过代码分析可以找到出错点,问题分析可参考如下文章,

http://bbs.51cto.com/thread-1133875-1.html

 

 

另外,在ActivityThread.java attach的时候,对于system_process,还有一个mSystemContext,它通过createSystemContext创建,用来操作framework资源,这个mSystemContext还会被SystemServer保存,用来操作framework接口。

 

 

所以,

在android系统里面,仅有一个SystemContext。

在一个应用里面,有1个application Context,有n个activity Context,n个service Context,要知道你的代码该使用哪一个Context。

 

 

6.1.2.          Context实例创建

 

上面提到的context的各子类实例化的时候都需要使用context的实例引用mBase,对于context的实例创建过程,是通过ContextImpl的创建来完成的。

 

ContextImpl的创建实例有不同的方法,

 

1)对于SystemContext类Context,

 

1.1)在android N之前,可以调用其方法createSystemContext,这里会新建一个ContextImpl类,并执行初始化过程,最后将实例引用返回给调用者。

 

///

    staticContextImpl createSystemContext(ActivityThread mainThread) {

        finalContextImpl context = new ContextImpl();

       context.init(Resources.getSystem(), mainThread, Process.myUserHandle());

        returncontext;

    }

 

采用这种方法来实现ContextImpl的创建有两种,如下,

可以通过activity来触发,在ActivityThread.java里定义mSystemContext来保存ContextImpl的引用,static ContextImplmSystemContext = null;

 

并在getSystemContext里调用createSystemContext来创建ContextImpl实例,赋值给mSystemContext。

///

    publicContextImpl getSystemContext() {

       synchronized (this) {

            if(mSystemContext == null) {

               ContextImpl context =

                   ContextImpl.createSystemContext(this);

               LoadedApk info = new LoadedApk(this, "android", context, null,

                       CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);

               context.init(info, null, this);

               context.getResources().updateConfiguration(

                       getConfiguration(), getDisplayMetricsLocked(

                               Display.DEFAULT_DISPLAY,

                               CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO));

               mSystemContext = context;

               //Slog.i(TAG, "Created system resources " +context.getResources()

               //        + ": " +context.getResources().getConfiguration());

            }

        }

        returnmSystemContext;

    }

 

 

   

    还可以通过LoadedApk类的创建,进行其内部的ContextImpl实例化。

///

    publicLoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,

           CompatibilityInfo compatInfo,

           ActivityThread mainThread, ClassLoader baseLoader,

           boolean securityViolation, boolean includeCode) {

       if(mAppDir == null) {

            if(ActivityThread.mSystemContext == null) {

               ActivityThread.mSystemContext =

                   ContextImpl.createSystemContext(mainThread);

               ActivityThread.mSystemContext.getResources().updateConfiguration(

                        mainThread.getConfiguration(),

                        mainThread.getDisplayMetricsLocked(

                                Display.DEFAULT_DISPLAY, compatInfo),

                        compatInfo);

            }

           mClassLoader = ActivityThread.mSystemContext.getClassLoader();

           mResources = ActivityThread.mSystemContext.getResources();

        }

    }

 

 

 

1.2) 而在android N上,createSystemContext如下,

///

    staticContextImpl createSystemContext(ActivityThread mainThread) {

        LoadedApkpackageInfo = new LoadedApk(mainThread);

       ContextImpl context = new ContextImpl(null, mainThread,

               packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);

       context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),

                context.mResourcesManager.getDisplayMetrics());

        returncontext;

    }

 

它是在系统init的时候,在SystemServer进程启动的时候被创建的,用来操作framework资源,调用过程如下,SystemServer.main()-- SystemServer().run -- SystemServer().createSystemContext -- activityThread.getSystemContext– ContextImpl.createSystemContext,

 

///

   /**

     * The mainentry point from zygote.

     */

    public staticvoid main(String[] args) {

        newSystemServer().run();

    }

 

 

///

    private voidrun() {

        try {

。。。

            //Initialize the system context.

           createSystemContext();   

 

 

///

        private void createSystemContext() {

       ActivityThread activityThread = ActivityThread.systemMain();

       mSystemContext = activityThread.getSystemContext();

       mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);

    }

  

 

 

这样,在android系统里就存在一个SystemContext,其实例引用被SystemServer和activityThread保存。

 

 

2)对于一般的Context

 

直接新建一个ContextImpl类,并执行初始化过程,最后将实例引用返回给调用者。

 

activity就是这么做的,在ActivityThread.java的performLaunchActivity调用createBaseContextForActivity,这里会得到一个ContextImpl类引用appContext,再调用activity.attach,通过activity将appContext传递给ContextThemeWrapper,进而传递给ContextWrapper,实现上面提到的mBase的初始化。

///

               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);

               activity.attach(appContext, this, getInstrumentation(), r.token,

                       r.ident, app, r.intent, r.activityInfo, title, r.parent,

                       r.embeddedID, r.lastNonConfigurationInstances, config);;

 

///

    privateContext createBaseContextForActivity(ActivityClientRecord r,

            finalActivity activity) {

       ContextImpl appContext = new ContextImpl();

       appContext.init(r.packageInfo, r.token, this);

       appContext.setOuterContext(activity);

}

 

 

Application相关的Context, 对于同一个包,公用一个应用,在第一次启动应用时,需要创建一个应用,应用同样也要新建一个ContextImpl实例,并使用attach与之关联起来,

///

    publicApplication makeApplication(boolean forceDefaultAppClass,

           Instrumentation instrumentation) {

        if(mApplication != null) {

           return mApplication;

        }

           java.lang.ClassLoader cl = getClassLoader();

           ContextImpl appContext = new ContextImpl();

           appContext.init(this, null, mActivityThread);

            app =mActivityThread.mInstrumentation.newApplication(

                    cl, appClass, appContext);

           appContext.setOuterContext(app);

       mActivityThread.mAllApplications.add(app);

       mApplication = app;

 

        if(instrumentation != null) {

            try {

               instrumentation.callApplicationOnCreate(app);

…   

 

 

      在之前的handleBindApplication里,还会新建ContextImpl,这个只是临时创建,通过它来使用应用接口的。

///

        finalContextImpl appContext = ContextImpl.createAppContext(this, data.info);

       updateLocaleListFromAppContext(appContext,

               mResourcesManager.getConfiguration().getLocales());      

 

 

 

对于service相关的ContextImpl,和activity类似,在service实例创建的时候,new一个ContextImpl,并调用init进行初始化,

///

           java.lang.ClassLoader cl = packageInfo.getClassLoader();

            service = (Service)cl.loadClass(data.info.name).newInstance();

           ContextImpl context = new ContextImpl();

           context.init(packageInfo, null, this);

 

           Application app = packageInfo.makeApplication(false, mInstrumentation);

           context.setOuterContext(service);

           service.attach(context, this, data.info.name, data.token, app,

                   ActivityManagerNative.getDefault());

           service.onCreate();

           mServices.put(data.token, service);

            try {

               ActivityManagerNative.getDefault().serviceDoneExecuting(

                       data.token, 0, 0, 0);

 

 

示例:对于service的context,举个例子说明一下怎么使用和传递context的,

例如在telecom里要关闭系统对话框,得使用context,它是传入的,

///

      /**

     * Closesopen system dialogs and the notification shade.

     */

    private voidcloseSystemDialogs(Context context) {

        Intentintent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);

        context.sendBroadcastAsUser(intent,UserHandle.ALL);

    }

 

 

在TelecomBroadcastIntentProcessor里context也是传入的

///

    publicTelecomBroadcastIntentProcessor(Context context, CallsManager callsManager) {

        mContext= context;

       mCallsManager = callsManager;

    }

 

           closeSystemDialogs(mContext);

 

 

再逐步找出调用栈,

 

///

79    static voidinitializeTelecomSystem(Context context) {

80        if(TelecomSystem.getInstance() == null) {

81           final NotificationManager notificationManager =

82                   (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);

83

84           TelecomSystem.setInstance(

85                   new TelecomSystem(

86                            context,

 

 

 

///

58public class TelecomService extends Service implementsTelecomSystem.Component {

59

60    @Override

61    publicIBinder onBind(Intent intent) {

62       Log.d(this, "onBind");

63       initializeTelecomSystem(this);

64       synchronized (getTelecomSystem().getLock()) {

65           return getTelecomSystem().getTelecomServiceImpl().getBinder();

66        }

67    }   

 

 

所以最初传递的时候,context就是service,而service是context类的子类,在service实例创建的时候,会创建一个context实例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值