Android上下文Context

Android 上下文Context

源码基于Android8.0 API 26

1 Context的关联类

Android中,Activity可以直接new吗?

Activity mActivity = new Activity();

Android应用程序开发基于Java语言,Activity本质上也是一个对象,那么👆的写法有什么问题?Android应用程序不像Java应用程序那样,随便创建一个类,写一个main方法就可以运行,Android是基于组件的应用设计模式,组件的运行需要有一个完整的Android工程环境。 只有在这个环境下,ActivityServiceBroadcastReceiver等组件才可以正常工作。这个支持组件运行上下文环境就是Context 可以这样讲,Context是一个维持Android应用中各个组件能够正常工作的一个核心工程类。

如果想要在Android Studio中运行一个.java文件的main函数,需要在Project Root/.idea/gradle.xml的标签GradleProjectSettings添加:

<option name="delegatedBuild" value="false" />

在开发过程中,Context的使用场景总的来说可以分为两大类:

  • 使用Context调用方法,比如启动Activity、启动Service、发送广播、操作数据库、访问资源、调用系统服务等
  • 调用方法时传入Context,比如弹出Toast、创建Dialog

这些行为意味着需要访问系统。

那么Context是从哪来的呢?从AMSAMS是系统级进程,拥有访问系统的权利,应用程序的启动受AMS的调控,在程序启动的过程中,AMS会把一个“凭证”通过跨进程通信给应用程序,程序会把这个“凭证”封装程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.
 *
 * 提供了关于应用程序环境的全局信息的接口。
 * 它是一个被Android系统支持的抽象类。它允许获取和应用程序相关的资源和类,包括应用级别操作,比如启动Activity、
 * 发送广播、接收Intent等操作
 */
public abstract class Context {
    }
1.1 ContextImplContextWrapper

既然Context是抽象类,在它的内部定义了很多方法以及静态常量,它的具体实现类为ContextImplContext相关联的类,除了ContextImpl,还有ContextWrapperContextThemeWrapperApplicationService等。

Context的关联类

从上图中看,ContextImplContextWrapper继承自Context

1.1.1 ContextImpl

ContextImpContext功能的实现类,应用程序中所调用的各种Context类的方法,其实现均来自该类。 因为外界需要使用并拓展ContextImpl的功能,因此设计上使用了装饰模式。

以下是ContextImpl的源码:

class ContextImpl extends Context {
    }
1.1.2 ContextWrapper

ContextWrapper是上下文功能的封装类,它对ContextImpl进行包装,主要起方法传递作用。ContextWrapper中几乎所有的方法都是调用ContextImpl来实现的,因此,在ContentWrapper的构造函数中包含一个真正的Context的引用——mBasemBase具体指向ContextImpl,同时ContextWrapper提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向给其所包含的真正的Context对象。

以下是ContextWrapper的源码:

public class ContextWrapper extends Context {
   
  @UnsupportedAppUsage
  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 Resources getResources() {
   
    return mBase.getResources();
  }
  
  @Override
  public ContentResolver getContentResolver() {
   
    return mBase.getContentResolver();
  }
  
  @Override
  public Looper getMainLooper() {
   
    return mBase.getMainLooper();
  }
  
  @Override
  public Context getApplicationContext() {
   
    return mBase.getApplicationContext();
  }
  
  @Override
  public String getPackageName() {
   
    return mBase.getPackageName();
  }
  
  @Override
  public void startActivity(Intent intent) {
   
    mBase.startActivity(intent);
  }
  
  @Override
  public void sendBroadcast(Intent intent) {
   
    mBase.sendBroadcast(intent);
  }
  
  @Override
  public Intent registerReceiver(
    BroadcastReceiver receiver, IntentFilter filter) {
   
    return mBase.registerReceiver(receiver, filter);
  }
  
  @Override
  public void unregisterReceiver(BroadcastReceiver receiver) {
   
    mBase.unregisterReceiver(receiver);
  }
  
  @Override
  public ComponentName startService(Intent service) {
   
    return mBase.startService(service);
  }
  
  @Override
  public boolean stopService(Intent name) {
   
    return mBase.stopService(name);
  }
  
  @Override
  public boolean bindService(Intent service, ServiceConnection conn, int flags) {
   
    return mBase.bindService(service, conn, flags);
  }
  
  @Override
  public void unbindService(ServiceConnection conn) {
   
    mBase.unbindService(conn);
  }  
}

其实,在ContextWrapper中的方法是非常多的,但是,实现都非常统一,就是调用了mBase对象中对应当前方法名的方法。

ContextWrapper有三个直接的子类,ContextThemeWrapperServiceApplication。其中,ContextThemeWrapper是一个带主题的封装类,它有一个直接子类就是Activity

ContextWrapper的继承关系

ContextThemeWrapper类,其内容包含了主题Theme相关的接口,这里所说的主题是指在AndroidManifest.xml中通过android:themeApplication元素或者Activity元素制定的主题。当然只有Activity才需要主题,而Service是不需要主题的,因为Service是没有界面的后台场景,所以Service直接继承于ContextWrapperApplication同理。

Context的关联类采用了装饰模式,主要有以下优点:

  • 使用者(比如Service)能够更方便的使用Context
  • 如果ContextImpl发生了变化,它的装饰类ContextWrapper不需要做任何修改
  • ContextImpl的实现不会暴露给使用者,使用者也不必关系ContextImpl的实现
  • 通过组合而非继承的方式,拓展ContextImpl的功能,在运行时选择不同的装饰类,实现不同的功能

总结:Context的两个子类分工明确,其中ContextImplContext的具体实现类,ContextWrapperContext的包装类。ActivityApplicationService虽然都继承自ContentWrapperActivity继承自ContextWrapper的子类ContextThemeWrapper),但它们初始化的过程中都会创建ContextImpl对象mBase,由ContextImpl实现Context中的方法。

由此可知,Context一共有三种类型,分别是ApplicationActivityService。这三个类分别各自承担了不同的作用,而具体的功能则是由ContextImpl类去实现的。

因此在大多数的场景下,ActivityServiceApplication这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全因素的考虑,Android是不允许ActivityDialog凭空出现的,一个Activity的启动必须建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,只能使用Activity类型的Context,否则将会出错。

Activity常用于与UI有关的操作,如添加Window,常规使用可以直接用Activity.thisService可以和Activity一样直接使用Service.this来使用Context,和Activity不同的是,Service没有界面,也不需要主题。ContentProvider使用的是ApplicationContextBroadcast使用ActivityContext

1.2 Context的数量

那么一个应用程序中到底有多少个Context呢?ContextApplicationActivityService三种类型,因此一个应用程序中Context数量的计算公式可以这样写:Context数量 = Activity数量 + Service数量 + 11代表Application的数量, 因为一个应用程序可以有多个Activity和多个Service,但只能有一个Application

那么在四大组件中,为什么只有ActivityService持有Context那么Broadcast ReceiverContent Provider并不是Context的子类,它们所持有的Context都是其他地方传过去的,所以并不计入Context总数。

1.3 Context的作用域

Context的作用域还是有一些限制的。由于Context的具体实例是由ContextImpl类去实现的,因此在绝大多数的场景下,ActivityServiceApplication这三种类型的Context都是可以通用的。不过有几种场景比价特殊,比如启动Activity、还有弹出Dialog出于安全原因的考虑,Android是不允许Activity或者Dialog凭空出现的,一个Activity的启动必须建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,只能用Activity类型的Context,否则将会出错。

Context作用域

从上图中可以看出Activity所持有的Context的作用域最广,因为Activity继承自ContextThemeWrapper,而ApplicationService继承自ContentWrapper,很显然ContextThemeWrapperContextWrapper的基础上又做了一些操作使得Activity变得更加强大。

2 Application Context

2.1 Application Context的创建过程

在一个应用程序启动完成后,应用程序就会有一个全局的Application Context。以下是Application Context的创建过程的时序图:

Application Context 创建过程时序图

ActivityThread类作为应用程序进程的主线程管理类,它会调用它的内部类ApplicationThreadscheduleLaunchActivity方法来启动Activity

scheduleLaunchActivity方法中向H类发送LAUNCH_ACTIVITY类型的消息,目的是将启动Activity的逻辑放在主线程的消息队列中,这样启动Activity的逻辑会在主线程中执行。

H继承自Handler,是ActivityThread的内部类。在H类的handleMessage方法对LAUNCH_ACTIVITY类型的消息的处理。

Android应用启动流程

2.2 Application Context的获取

一个应用程序启动完成后,就会有一个全局的Application Context。通过getApplicationContext()方法可以获得应用程序全局的Application ContextgetApplicationContext()方法在ContextWrapper中实现,如下所示:

@Override
public Context getApplicationContext() {
   
  return mBase.getApplicationContext();
}

mBase指的是ContextImplContextImplgetApplicationContext()方法:


                
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值