浅析Android的Context

欢迎访问我的个人网站: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类中包含了两类内容:常量抽象方法定义

由于Context本身是abstract的,所以它只负责定义需要的操作,具体的实现它并不关心,而直接继承于Context的ContextWrapper虽然并不是使用abstract修饰,但并没有做实际的实现,只是做了简单的包装,以便更好的呈现给Application、Service这样的类。我们所能看到的对Context做第一层实现的,应该是位于android.app包下的ContextImpl

Context的继承关系

于是先分析ContextImpl中的成员变量(包括静态的、final的、public的、private的),以下源码中是ContextImpl的所有成员变量:

    // ContextImpl.java

    /**
     * Map from package name, to preference name, to cached preferences.
     */
    @GuardedBy("ContextImpl.class")
    private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;

    /**
     * Map from preference name to generated path.
     */
    @GuardedBy("ContextImpl.class")
    private ArrayMap<String, File> 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的重要!

Context的内存泄露

众所周知,Android中的内存泄露,很多数情况都是Context造成的,根据上面对其结构和用途的分析,可以推测一下几点原因:

  1. 涉及各种系统、APP资源,功能过于强大,导致使用的地方太多,在流程和引用关系上造成混乱,比如:异步的网络请求、View动画等,在流程上处理不当,导致无法释放

  2. 杂乱无章的传值,让很多对象都Hold一份Context,开辟了过多无法回收的资源

  3. Drawable对象、Bitmap对象回收不及时,甚至与View死死绑定

  4. 单例的滥用,导致Context长期被引用无法释放

解决方案:

  1. 没必要传值的时候,尽量使用Application的Context,这样保证Context即可以全局使用,又不会创建多份

  2. 在与Activity等组件耦合的情况下,必须要使用Activity的Context的时候,考虑使用弱引用,避免循环持有Context

总结

总得来说,Context在Android中就是对一系列系统服务接口的封装,包括:内部资源、包、类加载、I/O操作、权限、主线程、IPC和组件启动等操作的管理。其结构比较简单,但方法很多,涉及的操作也很多,在使用时应当特别注意内存泄露和多线程问题,以免引发冲突或者严重的bug

猜你喜欢

转载自blog.csdn.net/yuanguozhengjust/article/details/80052206