欢迎访问我的个人网站:https://coderyuan.com
Context是Android App中用的非常多的一种概念,常被翻译成上下文,这种概念在其他的技术中也有所使用,无意间点了Context的源码,那么就来分析分析Context在Android中到底是什么东西?
/**
* 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 {
通过注释可以看出,Android官方对它的解释,大概可以理解为应用程序环境中全局信息的接口,它整合了许多系统级的服务,可以用来获取应用中的类、资源,以及可以进行应用程序级的调起操作,比如启动Activity、Service等等,而且Context这个类是abstract的,不包含具体的函数实现。
那就意思是能干很多事,反正很牛逼的样子。。。
于是搜了一下Context的引用,多的不能再多了
Context类中包含了两类内容:常量、抽象方法定义
由于Context本身是abstract的,所以它只负责定义需要的操作,具体的实现它并不关心,而直接继承于Context的ContextWrapper虽然并不是使用abstract修饰,但并没有做实际的实现,只是做了简单的包装,以便更好的呈现给Application、Service这样的类。我们所能看到的对Context做第一层实现的,应该是位于android.app包下的ContextImpl
于是先分析ContextImpl中的成员变量(包括静态的、final的、public的、private的),以下源码中是ContextImpl的所有成员变量:
// ContextImpl.java
/**
* Map from package name, to preference name, to cached preferences.
*/
@GuardedBy("ContextImpl.class")
private static ArrayMap> sSharedPrefsCache;
/**
* Map from preference name to generated path.
*/
@GuardedBy("ContextImpl.class")
private ArrayMap mSharedPrefsPaths;
final @NonNull ActivityThread mMainThread;
final @NonNull LoadedApk mPackageInfo;
private @Nullable ClassLoader mClassLoader;
private final @Nullable IBinder mActivityToken;
private final @Nullable UserHandle mUser;
private final ApplicationContentResolver mContentResolver;
private final String mBasePackageName;
private final String mOpPackageName;
private final @NonNull ResourcesManager mResourcesManager;
private @NonNull Resources mResources;
private @Nullable Display mDisplay; // may be null if default display
private final int mFlags;
private Context mOuterContext;
private int mThemeResource = 0;
private Resources.Theme mTheme = null;
private PackageManager mPackageManager;
private Context mReceiverRestrictedContext = null;
// The name of the split this Context is representing. May be null.
private @Nullable String mSplitName = null;
private final Object mSync = new Object();
@GuardedBy("mSync")
private File mDatabasesDir;
@GuardedBy("mSync")
private File mPreferencesDir;
@GuardedBy("mSync")
private File mFilesDir;
@GuardedBy("mSync")
private File mNoBackupFilesDir;
@GuardedBy("mSync")
private File mCacheDir;
@GuardedBy("mSync")
private File mCodeCacheDir;
// The system service cache for the system services that are cached per-ContextImpl.
final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
根据其命名和类型,可以分析出基本的作用:
sSharedPrefsCache:用于管理SharedPreference的缓存信息
mSharedPrefsPaths:用于保存SharedPreference文件存储路径
mMainThread:用于操作Activity等组件的主线程
mPackageInfo:当前加载的APK的PackageInfo,包括包名、版本号等
mClassLoader:用于加载Dex类的类加载器,类似其他Java项目中的ClassLoader,Android中做插件化的时候会用到
mActivityToken:一个Binder,可以用于IPC通信
mUser:用于管理用户数据,配合ContentResovler来使用
mContentResolver:应用程序级的ContentResolver,用来在不同进程间进行数据交换
mBasePackageName:基本包名
mOpPackageName:好像又是个包名。。。
mResourcesManager:系统资源服务,可以用来获取图片、字符串等资源,用的很多
mResources:系统资源,如主题、颜色定义等
mDisplay:系统显示器/屏幕信息,比如获取分辨率、PPI
mFlags:标志位,用来区分Context的类别
mOuterContext:外部使用的Context
mThemeResource:主题资源Id
mTheme:APP使用的主题
mPackageManager:包管理器,可以用来获取安装的APP的相关信息
mReceiverRestrictedContext:应该是用于BroadcastReceiver的Context
mSplitName:推测也是用于区别Context的
mSync:用于设置同步功能,用来保证文件操作的线程安全
mDatabasesDir:用于存储数据库文件的目录路径
mPreferencesDir:用于存储SharedPreference文件的目录路径
mFilesDir:用于存储一般文件的目录路径
mNoBackupFilesDir:用于存储不用于自动备份文件的目录路径
mCacheDir:用于存储缓存文件的目录路径
mCodeCacheDir:用于存储代码缓存文件(主要用于加载Dex)的目录路径
其中,@GuardedBy注解用来处理多线程的保护问题,来指明在多线程环境中,该对象被哪个加了同步锁的对象来保护
在分析了以上成员变量的用途以后,ContextImpl基本职责也就明确了,剩下的成员方法和静态方法,都是围绕这些成员展开的操作,也就是一些我们熟悉的Context可以进行的操作,比如:startActivity、startService、getExternalCacheDir、getDatabasesDir、getMainLooper、getResources、getTheme、checkPermission等等
不过,除了这些成员方法,有几个静态方法比较重要:
createSystemContext、createSystemUiContext、createAppContext、createActivityContext
虽然没有看到直接对这几个方法的引用,但拿Activity的启动流程来举例,ActivityManagerService(AMS)使用zygote进程先启动一个ActivityThread,在进行attach的时候,会调用ContextImpl的静态方法,来创建所需要的Context,同样,其他组件Application、Service等类中的Context,也是一样的道理,所以,我们在App中使用的各类Context,其实归根结底是由ContextImpl创建出来的,可见ContextImpl的重要!
众所周知,Android中的内存泄露,很多数情况都是Context造成的,根据上面对其结构和用途的分析,可以推测一下几点原因:
涉及各种系统、APP资源,功能过于强大,导致使用的地方太多,在流程和引用关系上造成混乱,比如:异步的网络请求、View动画等,在流程上处理不当,导致无法释放
杂乱无章的传值,让很多对象都Hold一份Context,开辟了过多无法回收的资源
Drawable对象、Bitmap对象回收不及时,甚至与View死死绑定
单例的滥用,导致Context长期被引用无法释放
解决方案:
没必要传值的时候,尽量使用Application的Context,这样保证Context即可以全局使用,又不会创建多份
在与Activity等组件耦合的情况下,必须要使用Activity的Context的时候,考虑使用弱引用,避免循环持有Context
总得来说,Context在Android中就是对一系列系统服务接口的封装,包括:内部资源、包、类加载、I/O操作、权限、主线程、IPC和组件启动等操作的管理。其结构比较简单,但方法很多,涉及的操作也很多,在使用时应当特别注意内存泄露和多线程问题,以免引发冲突或者严重的bug