四大组件以及Application和Context的全面理解

简介: 1.概述Context抽象结构2.用处1.Context的实现类有很多,但是ContextImpl(后称CI)是唯一做具体工作的,其他实现都是对CI做代理。

1.概述

Context抽象结构

2.用处

  • 1.Context的实现类有很多,但是ContextImpl(后称CI)是唯一做具体工作的,其他实现都是对CI做代理。
  • 2.CI中有一些成员对象,先来看看这些对象的用处
    • 1.mSharedPrefsPaths(ArrayMap<String, File>)、sSharedPrefsCache(ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>>):这两个对象是用于获取SharedPreferences的,在我前一篇博客里面有讲到。全面剖析SharedPreferences
    • 2.mMainThread(ActivityThread(后称AT)):这个对象是一个app进程的主线程,一个app的framework层就是从这里启动的。
    • 3.mPackageInfo(LoadedApk(后称LA)):在AT初始化app的主线程的时候,会将APK加载到内存中,apk在内存中就是以这个对象的形式存在的,该对象可以加载apk的资源和dex文件。
    • 4.mUser(UserHandle):多用户相关
    • 5.mContentResolver(ApplicationContentResolver):继承于ContentResolver(后称CR),主要功能是通过Uri来获取文件、数据库、asset、res等数据,还有就是通过ContentProvider来获取其他应用和本机数据。
    • 6.mResourcesManager(ResourcesManager):单例,因为一个apk不同机型的适配资源,所以用来加载Resource对象,以保证一个app中所有的CI使用的都是同一份资源。
    • 7.mResources(Resources):获取apk中res资源的对象。
    • 8.mOuterContext(Context):用于指向代理本对象的Context,例如Activity、Service等
    • 9.mTheme(Resources.Theme):主题
    • 10.mPackageManager(PackageManager(后称PM)):包管理类,不仅可以获取我们apk包的信息,还能获取本机apk包的信息。
  • 3.CI中有很多api,我将这些api归了一下类
    • 1.获取成员对象:即获取上面我列出来的那些对象,这些对象获取到了之后又有更多api暴露出来,在这里CI相当于做了一个聚合。最常用的就是getResource()了。
    • 2.获取成员对象的成员对象:即为了方便,CI封装了一些常用的获取成员对象中的信息的方法。例如getPackageName(),是通过PM来获取的。
    • 3.关于SharedPreferences(后称SP)的操作:我们知道SP其实就是xml文件,所以这里的操作有:获取、移动、删除。
    • 4.文件操作:增删移文件、打开文件流、获取app私有文件夹地址等等。
    • 5.数据库操作:我们知道sqlite其实是一种文件型数据库,所以有:打开、创建、移动、删除、获取数据库文件路径,等操作。
    • 6.壁纸相关操作:这个不是成员变量提供的,WallpaperManager是系统Service一种,所以是SystemService提供的。
    • 7.启动Activity:包括一般启动Acitivyt、多用户启动Activity、启动多个Activity。
    • 8.广播操作:发送普通广播、发送需要权限的广播、发送有序广播、发送粘连广播、发送有序粘连广播、多用户广播、移除各种广播、注册各种广播、取消注册各种广播。
    • 9.Service操作:启动、停止、重启、绑定、解绑、获取系统服务以及多用户操作。
    • 10.权限操作:检查本app是否有某种权限、检查某app是否有某种权限、检查Uri权限、授予权限等等。
    • 11.各种情况下创建CI:这个比较重要
      • 1.createSystemContext(ActivityThread mainThread):在SystemService创建的时候为其创建一个CI
      • 2.createAppContext(ActivityThread mainThread, LoadedApk packageInfo):在Application/Service创建的时候为其创建一个CI
      • 3.createActivityContext(ActivityThread mainThread,
        LoadedApk packageInfo, IBinder activityToken, int displayId,
        Configuration overrideConfiguration):在Activity创建的时候为其创建一个CI。

3.四大组件以及Application初始化与Context的关系

在了解Binder的时候有如下要注意的点

图片

  • 1.Activity初始化:
    • 1.CI.startActivity():将调用交给Instrumentation(负责监控Activity和AMS的交互,所有Activity的本地进程到远端进程的调用转换都是其来执行),
    • 2.Instrumentation.execStartActivity():传入一个ApplicationThread(后称APT)然后通过Binder机制将调用过程转移到ActivityManagerService(后称AMS)所在的系统服务进程,本地主线程则继续运行,不过本地主线程后续也没别的操作了,接下来就是本地的MessageQueue等待AMS服务运行完毕,发送消息将Activity的启动重新交给本地主线程。
    • 3.AMS.startActivity():从这里开始会调用会按顺序在 ActivityStarter、ActivityStackSupervisor、ActivityStack 这三个类之间进行调用,主要会进行下面这些操作,不按顺序:
      • 1.对Intent的内容进行解析,获取目标Activity的信息。
      • 2.根据传入的APT获取被调用app的信息封装成 ProcessRecord(后称PR)。
      • 3.将1、2和其他信息结合,将源Activity和目标Activity封装成两个ActivityRecord(后称AR)
      • 4.解析Activity的启动模式 和当前的Activity栈状态,判断是否需要创建栈和Activity。(注意这里的AR有着app中的Activity的全部信息,可以将其看成系统服务里面的Activity的化身)
      • 5.获取到Activity和Activity栈之后,接下来要判断是否要将当前Activity 执行onPause() 以及让使用Binder执行目标Activity的 onCreate()和onResume(注意这里onStart()会在Binder远程调用onCreate()的时候直接执行),这里AMS进程会使用APT调用app进程的Activity执行相应的生命周期。
      • 6.在AMS中前置准备一切就绪之后,会通过APT使用Handler的形式调用到app进程的AT中。
      • 7.最终到了ActivityStackSupervisor.realStartActivityLocked()中会使用APT将调用交给app进程-->AT.scheduleLaunchActivity()-->AT.handleLaunchActivity()
    • 4.AT.handleLaunchActivity():将有以下操作
      • 1.AT.performLaunchActivity:这个方法有以下操作
        • 1.创建对象LoadedApk(后称LA,一个app只加载一次)
        • 2.创建对象Activity
        • 3.创建对象Application(一个app,只创建一次)
        • 4.创建对象CI:CI.createActivityContext()
        • 5.Application/CI都attach到Activity对象:Activity.attach()
        • 6.执行onCreate():Instrumentation.callActivityOnCreate()-->Activity.performCreate()-->Activity.onCreate()
        • 7.执行onStart():AT.performLaunchActivity-->Activity.performStart()-->>Instrumentation.callActivityOnStart()—>Activity.onStart()
      • 2.AT.handleResumeActivity()
        • 1.AT.performResumeActivity()-->Activity.performResume()-->Instrumentation.callActivityOnResume()-->Activity.onResume()
        • 2.Activity.makeVisible()-->WindowManager.addView():开始进行View的绘制流程。
      • 3.从上面我们可以总结一下:在AMS将调用交给app进程之后,三个生命周期都是在app进程被回调的,并且在onResume()之后View才进行绘制
  • 2.Service初始化:
    • 1.CI.startService()-->CI.startServiceCommon():在这里传入一个APT,类似Activity启动时的第二步,将调用过程转移到AMS中,本地主线程继续运行,等待APT从AMS进程将调用转移到本地主线程中。
    • 2.AMS.startService():到了AMS进程之后,Service的启动就会全权交给ActiveServices(后称AS,这是AMS用来管理Service的成员变量)
    • 3.AS.startServiceLocked():这里做了以下操作
      • 1.根据传入的APT获取被调用app的信息封装成 PR
      • 2.解析Intent等参数获取到Service的信息,封装成ServicecRecord(后称SR,这个类可以看做是Service在系统服务的化身,记录了Service的一切信息)
      • 3.再进过一系列调用:AS.startServiceInnerLocked()-->AS.bringUpServiceLocked()-->AS.realStartServiceLocked()到这里才是真正在app进程启动Service的流程。
    • 4.AS.realStartServiceLocked():这里会有以下操作:
      • 1.SR.thread.scheduleCreateService():thread就是APT,这里会将调用转到app进程,但是当前的进程还会继续执行,这里就到了app线程的APT,这个方法里有以下操作
        • 1.通过Handler转到AT.handleCreateService()
        • 2.创建对象LA(一个app只加载一次)
        • 3.创建对象Service
        • 4.创建对象CI
        • 5.创建对象Application(一个app只创建一次)
        • 6.Application/CI分别attach到Service对象
        • 7.执行Service.onCreate()回调
        • 8.此时Service已经启动了
      • 2.AS.sendServiceArgsLocked()-->SR.app.thread.scheduleServiceArgs():这里就转到了app进程的APT中,这里会有以下操作:
        • 1.APT.scheduleServiceArgs()
        • 2.AT.handleServiceArgs()
        • 3.Service.onStartCommand()
        • 4.此时我们需要在Service中进行的操作将会执行。
  • 3.ContentProvider初始化:
    • 1.AT.main()-->AT.attach()-->AMS.attachApplication():传入一个APT,调用转到了AMS进程
    • 2.AMS.attachApplicationLocked():获取到ApplicationInfo 和 ProviderInfo列表之后通过APT将调用转回app进程。
    • 3.APT.bindApplication()-->AT.handleBindApplication()-->AT.installContentProviders():到这里之后将会循环初始化ContentProvider。
    • 4.AT.installProvider():这个方法里面有以下操作
      • 1.创建对象LA:CI.createPackageContext()中
      • 2.创建对象CI:CI.createPackageContext()中
      • 3.创建对象ContentProvider:ClassLoader创建
      • 4.CI attach到ContentProvider对象:ContentProvider.attachInfo()中
      • 5.执行onCreate回调:ContentProvider.attachInfo()中
  • 4.BroadCastReceiver静态初始化:因为动态广播的注册时进程已创建, 基本对象已创建完成,只需要回调BroadcastReceiver的onReceive()方法即可,所以这里不分析
    • 1.当收到广播时会调用AT.handleReceiver()
    • 2.创建对象LA(一个app只加载一次)
    • 3.创建对象BroadcastReceiver
    • 4.创建对象Application
    • 5.从创建的Application中获取CI
    • 6.执行onReceive()回调
  • 5.Application初始化:由上面四个组件的初始化我们可以知道,当app还没启动的时候唤醒任意组件都会创建一个Application,而这里分析的是正常情况启动一个app的时候创建Application的流程。
    • 1.这里的流程其实就是包含了ContentProvider初始化的流程,所以前面都差不多
    • 2.最后到了AT.handleBindApplication()中,这里有以下操作:
      • 1.创建对象LA
      • 2.创建对象CI
      • 3.创建对象Instrumentation
      • 4.创建对象Application;
      • 5.安装providers
      • 6.执行Create回调

4.四大组件以及Application绑定Context的方法

由上一节我们可以知道,四大组件以及Application在初始化的时候都会进行Context的绑定或者创建,这节就来讲讲各个组件是如何对context进程赋值的。

  • 1.Activity:
    • 1.AT.performLaunchActivity()
    • 2.AT.createBaseContextForActivity(ActivityClientRecord , Activity)
    • 3.ContextImpl.createActivityContext(ActivityThread , LoadedApk , IBinder , int displayId , Configuration)
    • 4.ContextImpl():被赋值了 ActivityThread、LoadedApk、IBinder activityToken、Configuration
  • 2.Service/Application:
    • 1.AT.handleCreateService()
    • 2.ContextImpl.createAppContext(ActivityThread , LoadedApk)
    • 3.new ContextImpl():被赋值了 ActivityThread、LoadedApk
  • 3.BroadCastReceiver:在AT.handleReceiver()中直接获取Application的Context,其自身并不创建Context
  • 4.ContentProvider:
    • 1.AT.installProvider()
    • 2.Context.createPackageContext()-->CI.createPackageContext()-->CI.createPackageContextAsUser():这里是通过一个Application的Context创建的Context,所以可以看做是Application的Context的一个复制。

5.总结

1.组件初始化会创建的对象:

image.png
  • 1.LoadedApk:所有组件在初始化的时候,如果LA没被初始化都会初始化一遍
  • 2.Context:
    • 1.只有Activity的CI有上一个Activity的Token
    • 2.Receiver的Context是继承于ContextWrapper 的 ReceiverRestrictedContext,不可绑定Service。
  • 3.Application:
    • 1.Receiver使用的Context是ReceiverRestrictedContext包装的Application的Context,所以其可以通过Context获取到Application
    • 2.ContentProvider一般是在app初始化的时候在初始化Application的过程中加载的,此时Application会被加载。但是如果是多个app共享进程,第二个app由ContentProvider调起,那么Application不会被初始化。

2.Context使用场景

image.png

说明: (图中第一列代表不同的Context, √代表允许在该Context执行相应的操作; ×代表不允许; -代表分情况讨论)

  • 1.当Context为Receiver的情况下:
    • 1.不允许执行bindService()操作, 由于限制性上下文(ReceiverRestrictedContext)所决定的,会直接抛出异常.
    • 2.registerReceiver是否允许取决于receiver;
    • 3.当receiver == null用于获取sticky广播, 允许使用;否则不允许使用registerReceiver;
  • 2.纵向来看startActivity操作
    • 1.当为Activity Context则可直接使用;
    • 2.当为其他Context, 要启动的Activity不属于任何Activity栈,所以必须带上FLAG_ACTIVITY_NEW_TASK flags才能使用

3.getApplication()和getApplicationContext()

绝大多数情况下, getApplication()和getApplicationContext()这两个方法完全一致, 返回值也相同; 那么两者到底有什么区别呢? 真正理解这个问题的人非常少. 接下来彻底地回答下这个问题:

  • 1.getApplicationContext()这个的存在是Android历史原因. 我们都知道getApplication()只存在于Activity和Service对象; 那么对于BroadcastReceiver和ContentProvider却无法获取Application, 这时就需要一个能在Context上下文直接使用的方法, 那便是getApplicationContext().
  • 2.对于Activity/Service来说, getApplication()和getApplicationContext()的返回值完全相同; 除非厂商修改过接口;
  • 3.BroadcastReceiver在onReceive的过程, 能使用getBaseContext().getApplicationContext获取所在Application, 而无法使用getApplication;
  • 4.ContentProvider能使用getContext().getApplicationContext()获取所在Application. 绝大多数情况下没有问题, 但是有可能会出现空指针的问题, 情况如下:当同一个进程有多个apk的情况下, 对于第二个apk是由provider方式拉起的, 前面介绍过provider创建过程并不会初始化所在application, 此时执getContext().getApplicationContext()返回的结果便是NULL. 所以对于这种情况要做好判空.

参考文章:Context全面理解

相关文章
|
18天前
|
缓存 Android开发
Android——application全局类的使用
Android——application全局类的使用
|
8月前
|
Android开发
Android 四大组件之Service的详解
Android 四大组件之Service的详解
81 0
|
8月前
|
数据库 Android开发 开发者
Android 开发四大组件(Activity、Service、Broadcast Receiver、Content Provider)
Android 开发四大组件(Activity、Service、Broadcast Receiver、Content Provider)
104 0
|
10月前
|
XML 传感器 缓存
Android:四大组件之 Service
Service 是 Android 中实现程序后台运行的解决方案,它非常适合用于去执行那些不需要和用户交互而且还要求长期运行的任务。但不要被“后台”二字迷惑,Service 默认并不会运行在子线程中,它也不会运行在一个独立的进程中,它同样执行在 UI 线程中,因此,不要在 Service 中执行耗时的操作,除非你在 Service 中创建子线程来完成耗时操作。
94 0
Android:四大组件之 Service
|
Java Android开发 开发者
Android Context ContextWrapper ContextImpl Activity Service关系
Android Context ContextWrapper ContextImpl Activity Service关系
Android Context ContextWrapper ContextImpl Activity Service关系
|
存储 缓存 安全
为什么不能使用 Application Context 显示 Dialog?
为什么不能使用 Application Context 显示 Dialog?
为什么不能使用 Application Context 显示 Dialog?
|
存储 缓存 Android开发
Android 缓存目录 Context.getExternalFilesDir()和Context.getExternalCacheDir()方法
一、基础知识 应用程序在运行的过程中如果需要向手机上保存数据,一般是把数据保存在SDcard中的。大部分应用是直接在SDCard的根目录下创建一个文件夹,然后把数据保存在该文件夹中。这样当该应用被卸载后,这些数据还保留在SDCard中,留下了垃圾数据。
1828 0
|
安全 Java Android开发
【Android 安全】DEX 加密 ( Application 替换 | 分析 Activity 组件中获取的 Application | ActivityThread | LoadedApk )(二)
【Android 安全】DEX 加密 ( Application 替换 | 分析 Activity 组件中获取的 Application | ActivityThread | LoadedApk )(二)
95 0
|
安全 Java Android开发
【Android 安全】DEX 加密 ( Application 替换 | 分析 Activity 组件中获取的 Application | ActivityThread | LoadedApk )(三)
【Android 安全】DEX 加密 ( Application 替换 | 分析 Activity 组件中获取的 Application | ActivityThread | LoadedApk )(三)
117 0
|
安全 Java Android开发
【Android 安全】DEX 加密 ( Application 替换 | 分析 Activity 组件中获取的 Application | ActivityThread | LoadedApk )(一)
【Android 安全】DEX 加密 ( Application 替换 | 分析 Activity 组件中获取的 Application | ActivityThread | LoadedApk )(一)
158 0