首页 / 预测世界杯足球

你对Context了解多少呢

2025-08-23 04:08:39预测世界杯足球 123

如需转载请评论或简信,并注明出处,未经允许不得转载

目录

前言

在Android应用进程的创建 — Activity的启动流程中我们发现,Application和Activity都是由系统创建的,它们并不能像其他java类一样,由应用层通过new操作创建出对象,且它们都继承自Context,那么大家有没有想过,这个Context到底有什么作用呢?它在Android应用中扮演了一个怎么样的角色呢?

Context继承关系

这里整理了一张Context的继承关系类图,从这个图中可以看出,Context是一个接口,ContextImp和ContextWrapper都是其实现类,我们常用的Activity、Service、Application都直接或间接继承自ContextWrapper

通过这张图,我们可以整理出几个问题:

一个应用程序有几个Context?

为什么Activity、Service、Application都继承自Context,Context的作用是什么呢?

为什么Activity需要继承自ContextThemeWrapper,而Service和Application直接继承自ContextWrapper呢?

为什么ContextWrapper中存在一个ContextImp类型的变量mBase,且同时它又实现了Context呢?

问题分析

问题一

一个应用程序有几个Context?

Application,Activity,Service都继承自Context,而应用有几个进程,就会存在几个Application对象

Context个数 = Activity个数 + Service个数 + 进程个数

问题二

为什么Activity、Service、Application都继承自Context,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.

*/

public abstract class Context {

// 四大组件相关

public abstract void startActivity(@RequiresPermission Intent intent);

public abstract void sendBroadcast(@RequiresPermission Intent intent);

public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,

IntentFilter filter);

public abstract void unregisterReceiver(BroadcastReceiver receiver);

public abstract ComponentName startService(Intent service);

public abstract boolean stopService(Intent service);

public abstract boolean bindService(@RequiresPermission Intent service,

@NonNull ServiceConnection conn, @BindServiceFlags int flags);

public abstract void unbindService(@NonNull ServiceConnection conn);

public abstract ContentResolver getContentResolver();

// 获取系统/应用资源

public abstract AssetManager getAssets();

public abstract Resources getResources();

public abstract PackageManager getPackageManager();

public abstract Context getApplicationContext();

public abstract ClassLoader getClassLoader();

public final @Nullable T getSystemService(@NonNull Class serviceClass) { ... }

public final String getString(@StringRes int resId) { ... }

public final int getColor(@ColorRes int id) { ... }

public final Drawable getDrawable(@DrawableRes int id) { ... }

public abstract Resources.Theme getTheme();

public abstract void setTheme(@StyleRes int resid);

public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { ... }

// 获取应用相关信息

public abstract ApplicationInfo getApplicationInfo();

public abstract String getPackageName();

public abstract Looper getMainLooper();

public abstract int checkPermission(@NonNull String permission, int pid, int uid);

// 文件相关

public abstract File getSharedPreferencesPath(String name);

public abstract File getDataDir();

public abstract boolean deleteFile(String name);

public abstract File getExternalFilesDir(@Nullable String type);

public abstract File getCacheDir();

...

public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);

public abstract boolean deleteSharedPreferences(String name);

// 数据库相关

public abstract SQLiteDatabase openOrCreateDatabase(...);

public abstract boolean deleteDatabase(String name);

public abstract File getDatabasePath(String name);

...

// 其它

public void registerComponentCallbacks(ComponentCallbacks callback) { ... }

public void unregisterComponentCallbacks(ComponentCallbacks callback) { ... }

...

}

public interface ComponentCallbacks {

void onConfigurationChanged(Configuration newConfig);

void onLowMemory();

}

结合代码可以看出,Context就像是应用的大管家,正是因为有了Context,各种应用组件才有意义,他们才能访问系统服务,系统资源

Context的作用总结为如下几个方面:

四大组件的交互,包括启动 Activity、Broadcast、Service,获取 ContentResolver 等

获取系统/应用资源,包括 AssetManager、PackageManager、Resources、System Service 以及 color、string、drawable 等

文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等

数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等

其它辅助功能,比如设置 ComponentCallbacks,即监听配置信息改变、内存不足等事件的发生

问题三

为什么Activity需要继承自ContextThemeWrapper,而Service和Application直接继承自ContextWrapper呢?

下面来看一下ContextThemeWrapper的源码

public class ContextThemeWrapper extends ContextWrapper {

private int mThemeResource;

private Resources.Theme mTheme;

private LayoutInflater mInflater;

private Configuration mOverrideConfiguration;

private Resources mResources;

public ContextThemeWrapper() {

super(null);

}

public ContextThemeWrapper(Context base, @StyleRes int themeResId) {

super(base);

mThemeResource = themeResId;

}

public ContextThemeWrapper(Context base, Resources.Theme theme) {

super(base);

mTheme = theme;

}

@Override

protected void attachBaseContext(Context newBase) {

super.attachBaseContext(newBase);

}

public void applyOverrideConfiguration(Configuration overrideConfiguration) {...}

public Configuration getOverrideConfiguration() {...}

@Override

public AssetManager getAssets() {...}

@Override

public Resources getResources() {...}

private Resources getResourcesInternal() {...}

@Override

public void setTheme(int resid) {...}

@Override

public int getThemeResId() {...}

@Override

public Resources.Theme getTheme() {...}

@Override

public Object getSystemService(String name) {...}

protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) {

private void initializeTheme() {...}

}

ContextThemeWrapper类,从它的命名就可以看出,其内部包含了与Theme相关的接口,当然,只有Activity才需要主题,Service和Application是不需要主题的,因为Service是没有界面的后台场景,所以Service和Application直接继承于ContextWrapper

问题四

为什么ContextWrapper中存在一个ContextImp类型的变量mBase,且同时它又实现了ContextWrapper呢?

下面来看ContextWrapper的代码

public class ContextWrapper extends Context {

Context mBase;

public ContextWrapper(Context base) {

mBase = base;

}

protected void attachBaseContext(Context base) {

if (mBase != null) {

throw new IllegalStateException("Base context already set");

}

mBase = base;

}

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

}

....

}

很显然,ContextWrapper只是一个Context静态代理类,所有的操作都是通过内部成员 mBase 完成的,而mBase就是ContextImp对象。为什么要这么设计呢?如果Application直接继承ContextImp会不会有什么问题呢?

一般情况下,使用代理而不直接使用某个对象,目的可能有两个:

定制自己的行为

不影响原对象

其中 Servcie 和 Application 的父类 ContextWrapper 完全没有自定义的行为,而 Activity 的父类 ContextThemeWrapper 则自定义了 Resource 以及 Theme 的相关行为,因此:

对于 Service 和 Application 而言,不直接继承ContextImp,是担心用户修改了ContextImp而导致错误的发生

对于 Activity 而言,除了担心用户的修改之外,ContextImp和 Activity 本身对于 Reource 以及 Theme 的相关行为是不同的

其他使用Context注意点

在单例模式中使用Context要注意内存泄漏问题

我们知道,单例模式的生命周期是和应用的生命周期保持一致的,所以在单例模式中使用Context,不能使用Activity Context,需要使用Application Context

创建dialog需要使用Activtiy Context

果我们使用Application的Context,或者说Token可以不是Activity的Token,那么用户可能已经跳转到别的应用的Activity界面了,但我们却可以在别人的界面上弹出我们的Dialog,想想就觉得很危险

具体可以参考:https://www.jianshu.com/p/628ac6b68c15

Activity的this和getBaseContext()有什么区别?

Activity就是继承Context的,所以this是返回Activity自己

getBaseContext()返回的是ContextWrapper里面的mBase

getApplication()和getApplicationContext()有什么区别?

都是返回Applicatoin对象,但是他们的作用域不一样

getApplicatoin()是Activity和Service里面特有的,其他地方不能用

BroadcastReceiver的onReceive()中的第一个参数,拿到的Context对象是不能调用getApplication()的只能调getApplicationContext()

Application构造方法中调用父类方法会发生闪退(如getResource()等)

Application的创建过程主要是下面三步

//1.创建ContextImpl

ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);

//2.创建Application

Application app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);

//3.调用Application#onCreate()

app.onCreate();

Instrumentation#newApplication()

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;

}

Application#attach()

final void attach(Context context) {

attachBaseContext(context);

mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;

}

Application#attachBaseContext()

protected void attachBaseContext(Context base) {

if (mBase != null) {

throw new IllegalStateException("Base context already set");

}

mBase = base;

}

从上面的代码中可以看出,在Application对象刚被创建的时候,其内部的mBase变量是空的,直到执行attachBaseContext()后,mBase才会有值,之后才会调用Application#onCreate()。所以在Application中使用Context接口中的相关方法,可以在onCreate()里面调用,而不能在构造方法中调用

总结

Context是一个抽象类,我们开发过程中几乎每天都要和它打交道,但是我相信很多人都说不出他是个什么东西,很多只是知道它叫做上下文。其实当我们觉得一个东西很抽象很难理解的时候,无外乎就是看一下它的创建过程以及它具备哪些方法

这里做一个比喻,对于我们应用层开发来说,Activity就像是一个”皇帝“,Activity可以做很多很多的事情,但是Context就像是他手中的权利,如果没有Context,Activity其实只是一个”普通人“(普通java类)而已

我们很多人往往把Activity理解成它继承了Context,是的没错,它确实继承自Context,但我认为,把Activity理解成它代理了Context,会更贴合实际意义一些