Android Context

本文深入解析Android中的Context机制,包括Context及其相关类的关系、不同类型的Context生命周期管理、内存泄漏及非法调用问题、以及Context实例的创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

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

 

 

再逐步找出调用栈,

///////////////////////////////////////////////////////////////////////////////////////////////////////

    public TelecomSystem(

Contextcontext,

mContext= context.getApplicationContext();

mTelecomBroadcastIntentProcessor= new TelecomBroadcastIntentProcessor(

mContext, mCallsManager);

 

 

///////////////////////////////////////////////////////////////////////////////////////////////////////

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实例。

<think>好的,用户让我介绍一下Android中的Context。首先,我需要回忆一下自己对Context的理解。ContextAndroid开发中确实是一个基础且重要的概念,但刚接触的时候可能会有点混淆。 用户可能是一位刚开始学习Android开发的程序员,或者对Android内部机制感兴趣的人。他们可能在使用某些API时遇到了需要传递Context的情况,但不清楚它的具体作用和不同场景下的使用区别。需要从基础开始解释,同时确保内容结构清晰,帮助他们逐步理解。 首先,应该定义Context是什么。根据官方文档,Context是应用程序环境的接口,提供访问资源和类的方法,以及执行应用级操作。接下来,需要区分不同的Context类型,比如Application Context、Activity Context和Service Context。这部分很重要,因为错误使用可能会导致内存泄漏或其他问题。 然后,要详细说明每种Context的使用场景。例如,Activity Context用于和UI相关的操作,而Application Context更适合需要生命周期无关的场景。需要举一些例子,比如显示Toast、启动Activity、获取资源等,说明应该使用哪种Context。 还需要提到常见的错误使用情况,比如在单例或静态变量中持有Activity Context导致的内存泄漏,以及如何避免这些问题。这部分对实际开发很有帮助,能帮助用户避免常见的陷阱。 另外,可能需要补充一些代码示例,但根据用户的要求,可能不需要具体的代码,而是用文字描述。但要注意结构清晰,分点说明。 最后,总结Context的重要性,并强调正确使用不同类型的Context的必要性。这样用户能有一个全面的认识,并知道在实际开发中如何应用这些知识。 需要检查是否有遗漏的重要点,比如Context提供的具体功能,如访问资源、启动组件、获取系统服务等。这些都需要涵盖到,确保回答全面。同时,要确保语言简洁,避免过于技术化的术语,让初学者也能理解。 可能还需要比较Application Context和Activity Context的区别,特别是在生命周期方面的差异。例如,使用Application Context启动Activity时需要添加FLAG_ACTIVITY_NEW_TASK标志,而Activity Context则不需要。这样的细节能帮助用户更深入理解。 总之,回答的结构应该是:定义、类型、功能、使用场景、常见问题及注意事项。这样逻辑清晰,逐步引导用户理解Context的作用和正确使用方法。</think>### Android Context 详解 #### 1. **定义与作用** Context(上下文)是 Android 系统中**应用程序环境的全局信息接口**,核心功能包括: - 访问应用资源(字符串、图片、布局等) - 启动组件(Activity、Service、Broadcast) - 获取系统服务(如定位、传感器) - 操作文件与数据库 #### 2. **Context 类型** | 类型 | 生命周期范围 | 典型场景 | |---------------------|---------------------|-----------------------------| | **Application** | 应用级(全程存活) | 全局单例、长生命周期操作 | | **Activity** | 界面级(随界面销毁)| UI操作、启动其他界面 | | **Service** | 服务级(随服务终止)| 后台任务相关操作 | #### 3. **核心功能对比** $$ \text{Application Context} \neq \text{Activity Context} $$ - **Activity Context**: - 包含界面主题信息 - 可直接启动新Activity - 示例:`startActivity()`、`getLayoutInflater()` - **Application Context**: - 无法执行界面相关操作 - 启动Activity需添加标志: `intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)` #### 4. **典型使用场景** - **正确案例**: ```java // 显示Toast(可用Application) Toast.makeText(getApplicationContext(), "提示", Toast.LENGTH_SHORT).show(); // 启动Activity(必须用Activity Context) Intent intent = new Intent(this, NextActivity.class); startActivity(intent); ``` - **错误案例**: ```java // 错误:静态变量持有Activity Context public class Utils { private static Context sContext; // 可能导致内存泄漏 } ``` #### 5. **内存泄漏防范** - **问题根源**: Activity被销毁后仍被其他对象引用 → 无法被GC回收 - **解决方案**: 1. 优先使用Application Context 2. 使用WeakReference弱引用 3. 及时解绑广播接收器、监听器 #### 6. **进阶理解** - **ContextImpl**: Context的具体实现类,系统通过它管理资源访问 - **ContextWrapper**: 包装类模式,Activity/Service等组件通过继承它扩展功能 #### 总结 ContextAndroid开发的核心枢纽,正确理解其类型差异和生命周期管理,可有效提升应用性能和稳定性。关键原则:**UI操作用Activity Context,全局操作用Application Context,避免非必要引用**。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值