正如《Android从简单到深入全面解析Handler消息机制》一文中所讨论的那样,Handler可以:
我们避免手动编写无限循环和输入阻塞来不断获取用户输入,避免直接终止线程。相反,我们采用事务驱动的设计,利用Handler消息机制,让AMS控制整个程序的运行逻辑。
这是android程序设计中比较重要的一部分。对此不太了解的读者可以去阅读以了解更多。而当我们知道Android程序是通过main方法来运行,然后通过handler机制来控制程序的运行时,那么四大组件和普通的Java类有什么区别呢?尽管相同的Java 类,为什么像ActivityThread、Activity 等类如此特别?我们的代码和布局是通过什么路径使用系统资源来在屏幕上显示界面的?这一切都涉及到我们今天的主角:语境。
什么是Context
回想一下你刚学习Android开发的时候,第一次使用context是什么时候?如果你和我一样,通过郭林的《第一行代码》开始接触Android,那么通常就是Toast。 Toast的常规用法是:
Toast.makeText(this, "I am toast", Toast.LENGTH_SHORT).show() 一开始并不知道Context是什么。它只知道自己需要一个context类型,只是将activity对象传入即可。从那时起,context就被用到了我开发过程的方方面面,但我还是不知道这个context有什么用?你为什么想要这个物体?我们先看一下官方对Context类的注释:
/**
* 有关应用程序环境的全局信息的接口。这是
* 一个抽象类,其实现由
* 安卓系统。它
* 允许访问特定于应用程序的资源和类,以及
* 对应用程序级操作的调用,例如启动活动,
* 广播和接收意图等
*/
public Abstract class Context {.}关于应用程序环境的全局信息的接口。 这是一个抽象类,它的实现是由Android系统提供。 它允许访问特定应用的资源和类,以及向上调用应用程序级的操作,如启动活动,广播和接收Intent等。可以看到,Context最重要的作用就是获取全局消息、访问系统资源、调用应用程序级操作。您可能对这些功能没有太多印象。想一想,如果没有上下文,我们怎么能进行以下操作:
弹出一个toast并启动一个activity来获取程序布局文件、drawable文件等来访问数据库。
一旦上下文丢失,这些看似简单的操作就无法执行。这些行为都有一个共同点:需要与系统交互。为什么四大组件配置为组件,而我们写的只能称为普通的Java类呢?正是由于上下文的这些功能,四大组件才具有不同的能力。简单来说,context就是:应用程序和系统之间的桥梁,应用程序访问系统各种资源的接口。一般我们使用context最多的场景有两种:直接调用context方法和调用接口时需要context参数。这些行为都意味着我们需要访问系统相关的资源。
上下文从哪里来? AMS! AMS是系统级进程,拥有系统级操作的访问权限。应用程序的启动由AMS控制。在程序启动的过程中,AMS会通过跨进程通信的方式给我们的应用程序一个“凭证”。我们的程序会将这个“凭证”封装到上下文中,并提供一系列接口,以便我们的程序可以方便地访问系统资源。这样做的好处是:
系统可以对应用程序级的操作进行调控,限制各种情景下的权限,同时也可以防止恶意攻击。比如Application类和Activity类的上下文权限不同,生命周期也不同。希望操作系统攻击用户的程序也会被阻止。不允许的Java类没有任何权限,而Activity仅向用户开放一些有限的权限。我们开发者获取上下文的路径只能从activity、application等组件中获取。
那么,什么是Context呢?上下文是应用程序和系统之间的沟通桥梁。它是应用程序访问系统资源的接口。也是系统给予应用程序的“许可证书”。有了上下文,Java 类就可以称为组件。
Context家族
在上一部分中,我们了解了上下文是什么以及上下文的重要性。这一部分我们将学习源码中context的子类继承。我们先看一张图:
Context家族的顶层是Context抽象类,它定义了一系列与系统交互的接口。 ContextWrapper继承自Context,但实际上并没有实现Context中的接口。相反,它托管ContextImpl 接口的实现。 ContextImpl是Context接口的真正实现者。从AMS 获取的“凭证”也封装到ContextImpl 中。然后分配给ContextWrapper,这里使用一个模式:装饰者模式。 Application和Service都继承自ContextWrapper,因此它们也具有Context的接口方法,本身就是上下文,方便开发者使用。 Activity比较特殊,因为它有界面,所以它需要一个主题:Theme。 ContextThemeWrapper在ContextWrapper的基础上添加了主题相关的操作。
这种设计具有以下优点:
活动等可以更方便的使用context。他们可以使用自己作为上下文。遇到需要上下文的接口,直接将自己传入即可。使用装饰器模式将ContextImpl的内部逻辑与外部屏蔽。同时,当ContextImpl的逻辑实现需要改变时,ContextWrapper的逻辑几乎不需要改变。轻松扩展不同场景的逻辑。例如,service和activity的场景不同,需要不同的接口方法,但与系统交互的接口是相同的。使用装饰器模式可以扩展很多功能,只需要为其赋值ContextImpl对象即可。
context的分类
前面讲Context家族系统的时候我们了解到它的最终实现类是:Application、Activity、Service。 ContextImpl 由前三个持有,是Context 接口的真正实现。那么这里我们讨论一下三者之间的区别以及使用时需要注意的问题。
Application
Application是一个全局Context,整个应用程序只有一个,它可以访问应用程序的包信息等资源信息。获取Application一般有两种方法:
context.getApplicationContext()
Activity.getApplication()可以通过context和activity来获取Application。这两种方法有什么区别?没有什么区别。我们可以打印出来看看:
覆盖fun onCreate(savedInstanceState: Bundle?) {
.
Log.d("修仙的猿猴", "application:$application")
Log.d("修仙的猿猴", "applicationContext:$applicationContext")
}log.png 可以看到确实是同一个对象。但为什么要提供两种效果相同的方法呢? getApplication()方法比较直观,但只能在activity中调用。 getApplicationContext()的适用范围更广,任何上下文对象都可以调用该方法。
Application类的Context的特点是生命周期长,在整个应用程序运行过程中都会存在。同时我们可以自定义Application,在里面做一些全局的初始化操作,或者写一个静态的上下文进行全局获取,而不需要在方法中传入上下文。喜欢:
类MyApplication : 应用程序(){
//全局上下文
伴随对象{
Lateinit var context: 上下文
}
重写fun onCreate() {
超级.onCreate()
//进行全局初始化操作
RetrofitManager.init(这个)
上下文=这个
}
这样我们就可以在应用程序启动时初始化一些组件,同时通过MyApplication.context获取Application对象。
但!请不要将Application用作工具类。由于Application获取的方便,一些开发者会在Application中编写一些工具方法,并全局获取。这是不可能的。自定义Application的目的是为了在程序启动时做全局初始化工作,不能用来替代工具类。这严重违反了Google设计Application的原则,也违反了Java代码规范的单一责任原则。
四大组件
Activity继承自ContextThemeWrapper,是一个有主题的上下文对象。Activity常用于与UI有关的操作,比如添加窗口等。常规使用可以直接使用activity.this。
Service继承自ContextWrapper,也可以直接使用service.this来像Activity一样使用上下文。与Activity 不同,Service 没有界面,因此不需要主题。
ContextProvider使用Application的上下文,Broadcast使用activity的上下文。这两点后面会在源码中分析。
BaseContext
嗯?什么是baseContext?把这个拿出来单独说,细心的读者可能会发现,activity中有一个方法:getBaseContext。这就是ContextWrapper中的mBase对象,也就是ContextImpl,也是context接口真正的逻辑实现。
context的使用问题
使用上下文时最重要的问题之一是注意内存泄漏。不同的上下文有不同的生命周期。只要应用存在,Application就会一直存在,而Activity会随着界面的销毁而被销毁。如果我们的代码长时间保存活动的上下文,例如静态引用或单个示例类,则活动将不会被释放。比如下面的代码:
对象我的类{
Lateinit var mContext : 上下文
有趣的showToast(上下文:上下文){
mContext=上下文
}
}单例类在应用程序运行期间将始终存在,因此上下文将始终被保存,并且Activity 无法回收,从而导致内存泄漏。
然后,我们就可以切换到Application,如下:
对象我的类{
Lateinit var mContext : 上下文
有趣的showToast(上下文:上下文){
mContext=上下文.applicationContext
}
}答案是:不能。什么时候可以使用应用程序?不涉及UI以及启动Activity操作。Activity的上下文有主题属性。如果使用Application来操作UI,自定义的主题将会丢失,而使用系统默认的主题。同时,有些UI操作只能由Activity来执行,比如弹出对话框,这就涉及到窗口的token问题。关于token验证我在这篇文章中给出了详细的解答。有兴趣的读者可以阅读。这也是官方对于上下文不同权限的设计。没有界面的上下文,就应该无权操作该界面。使用Application启动的Activity必须指定一个task并标记为singleTask,因为Application没有任务栈,需要重新打开一个新的任务栈。因此,我们需要根据不同context的不同职责来执行不同的任务。
Context的创建过程
经过以上讨论,读者对脉络有了一定的了解。但我总觉得缺少了一些东西:activity是什么时候创建的,它的contextImpl是如何分配的?那么应用呢?为什么说ContextProvider的上下文是Application,Broadcast的上下文是Activity呢? contextImpl是如何创建的?要解决这些疑惑,就必须阅读源码。阅读源代码有很多好处。我的上述描述是基于我阅读源代码后的理解,“一千个观众有一千个哈姆雷特”。阅读源码可以形成自己对整个机制的思考和理解。同时,你可以真正将你对上下文的了解落实到代码中,增强你对知识的信心。当别人和你有不同意见的时候,你可以拍着胸脯说:我看过源码了,就是这样。你是不是非常自信、傲慢?
不过,读源码并不是读更多的源码,而是在掌握整体流程之后再去读关键源码,而不是陷入源码堆太深而无法自拔。例如,如果我认为Activity的contextImpl是在Activity的创建过程中分配的,那么我就会去寻找该activity的启动过程的源码,然后只看与context相关的部分。在提高效率的同时,也能切中我们学习的重点。在下面的源码阅读中,我们先给出整体流程,然后重点理解关键代码。其他源码读者可以下载源码进行跟随阅读。
Application
应用程序应用程序级上下文是在创建应用程序时创建的。它是第一个被创建的上下文,也是最后一个被销毁的上下文。因此,跟踪Application的创建需要从应用程序的启动过程开始。应用启动的源码流程如下(简化版):
Application应用程序从ActivityThread的main方法开始执行。从Handler消息机制我们知道,main方法主要是打开线程的Looper和handler,然后AMS向主线程发送消息来控制应用程序的启动过程。因此,我们可以针对图中的最后一个方法:handleBindApplication,其中最有可能创建Application:
ActivityThread.class(api29)私有无效handleBindApplication(AppBindData数据){
.
//创建LoadedApk对象
data.info=getPackageInfoNoCheck(data.appInfo, data.compatInfo);
.
应用程序;
.
尝试{
//创建应用程序
应用程序=data.info.makeApplication(data.restrictedBackupMode, null);
.
}
尝试{
.
//回调Application的onCreate方法
mInstrumentation.callApplicationOnCreate(app);
}
.
}handleBindApplication的参数AppBindData是AMS给应用程序的启动信息,其中包括“权限凭据”——ApplicationInfo等。LoadedApk使用这些对象来创建和获取系统资源的访问权限,然后使用LoadApk创建ContextImpl和Application。
这里我们只关注上下文创建相关的逻辑。前面的启动程序的源码以及AMS是如何处理的这里就不讨论了。有兴趣的读者可以阅读ContextProvider启动流程一文,其中包含了上述ContextProvider启动流程的源码。详细跟踪。
那么我们继续关注Application是如何创建的:
-LoadeApk.class(api29)
公共应用程序makeApplication(布尔forceDefaultAppClass,
仪器仪表){
//如果应用已经存在,则直接返回
if (mApplication !=null) {
返回mApplication;
}
.
应用程序app=null;
String appClass=mApplicationInfo.className;
.
尝试{
java.lang.ClassLoader cl=getClassLoader();
.
//创建ContextImpl
ContextImpl appContext=ContextImpl.createAppContext(mActivityThread, this);
//使用类加载器加载我们在AndroidMenifest中指定的Application类
应用程序=mActivityThread.mInstrumentation.newApplication(
cl、appClass、appContext);
//将Application的引用给contextImpl,这样contextImpl也可以方便的访问Application。
appContext.setOuterContext(app);
}
.
mActivityThread.mAllApplications.add(app);
//将应用程序设置为mApplication。当我们调用context.getApplicationContext时,我们得到这个对象
mApplication=应用程序;
if(检测!=null){
尝试{
//回调Application的onCreate方法
仪器.callApplicationOnCreate(应用程序);
}
.
}
.
返回应用程序;
}代码逻辑并不复杂。首先判断LoadedApk对象中的mApplication是否存在,否则创建ContextImpl,然后使用类加载器和contextImpl创建Application,最后将Application对象赋值给LoadedApk的mApplication,然后回调该方法的onCreate方法应用。我们首先看一下contextImpl是如何创建的:
ContextImpl.class(api29)static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
字符串opPackageName) {
if (packageInfo==null) 抛出new IllegalArgumentException("packageInfo");
ContextImpl 上下文=new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
null, opPackageName);
context.setResources(packageInfo.getResources());
返回上下文;
}这里直接创建了一个新的ContextImpl,同时为ContextImpl分配了与访问系统资源相关的“权限”对象——ActivityThread、LoadedApk等。让我们回到应用程序创建过程。我们可以猜测newApplication中包含的逻辑一定包括:利用反射创建Application,然后将contextImpl赋值给Application。原因是每个人定制的Application类不一样,需要反射来创建对象。其次,Application中的mBase属性是对ContextImpl的引用。看一下源码:
Instrumentation.class(api29)public Application newApplication(ClassLoader cl, String className, Context context)
抛出InstantiationException、IllegalAccessException、
类NotFoundException {
应用程序app=getFactory(context.getPackageName())
.instantiateApplication(cl, 类名);
应用程序.attach(上下文);
返回应用程序;
}Application.class(api29)final void Attach(Context context) {
AttachBaseContext(上下文);
mLoadedApk=ContextImpl.getImpl(context).mPackageInfo;
}ContextWrapper.class(api29)Context mBase;
受保护的无效attachBaseContext(上下文基础){
如果(mBase!=null){
throw new IllegalStateException("基础上下文已设置");
}
mBase=基数;
}结果与我们的猜测非常一致,首先创建Application对象,
再把ContextImpl通过Application的attach方法赋值给Application。然后Application的attach方法调用了ContextWrapper的attachBaseContext方法,因为Application也是继承自ContextWrapper。这样,就把ContextImpl赋值给Application的mBase属性了。 再回到前面的逻辑,创建了Application之后需要回调onCreate方法: Instrumentation.class(api29)public void callApplicationOnCreate(Application app) { app.onCreate(); }简单粗暴,直接回调。到这里,Application的创建以及context的创建流程就走完了。但是需要注意的是,全局初始化需要在onCreate中进行,而不要在Application的构造器中执行。从代码中我们可以看到ContextImpl是在Application被创建之后再赋值的。Activity
Activity的context也是在Activity创建的过程中被创建的,这个就涉及到Activity的启动流程,这里涉及到三个流程:应用程序请求AMS,AMS处理请求,应用程序响应Activity创建事务: Activity的创建流程依然,我们专注于Activity的创建流程,其他的读者可阅读Activity启动流程这篇文章了解。和Application一样,Activity的创建时由AMS来控制的,AMS向应用程序进程发送消息来执行具体的启动逻辑。最后会执行到handleLaunchActivity这个方法: ActivityThread.class(api29)public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) { ... final Activity a = performLaunchActivity(r, customIntent); ... return a; }最终的就是中间这句代码,进入看源码: ActivityThread.class(api29)private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... // 创建Activity的ContextImpl ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; try { // 利用类加载创建activity实例 java.lang.ClassLoader cl = appContext.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); ... } try { // 创建Application Application app = r.packageInfo.makeApplication(false, mInstrumentation); ... if (activity != null) { ... // 把activity设置给context,这样context也可以访问到activity了 appContext.setOuterContext(activity); // 调用activity的attach方法把contextImpl设置给activity activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback, r.assistToken); int theme = r.activityInfo.getThemeResource(); if (theme != 0) { // 设置主题Service
依然只关注关键代码流程,先看Service的启动流程图: Service的启动流程图Service的创建过程也是受AMS的控制,同样我们看到创建Service的那一步,最终会调用到handleCreateService这个方法: private void handleCreateService(CreateServiceData data) { ... LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; try { java.lang.ClassLoader cl = packageInfo.getClassLoader(); service = packageInfo.getAppFactory() .instantiateService(cl, data.info.name, data.intent); } ... try { ... ContextImpl context = ContextImpl.createAppContext(this, packageInfo); context.setOuterContext(service); Application app = packageInfo.makeApplication(false, mInstrumentation); service.attach(context, this, data.info.name, data.token, app, ActivityManager.getService()); service.onCreate(); mServices.put(data.token, service); ... } ... }Service的逻辑就相对简单了,同样创建service实例,再创建contextImpl,最后把contextImpl通过Service的attach方法赋值给mBase属性,最后回调Service的onCreate方法。过程和上面的很像,这里就不再深入讲了,感兴趣的读者可自行去阅读源码,也可以阅读Android中Service的启动与绑定过程详解(基于api29)这篇文章了解Service的详细内容。Broadcast
Broadcast和上面的组件不同,他并不是继承自Context,所以他的Context是需要通过上述三者来给予。我们一般使用广播的context是在接受器中,如: class MyClass :BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { TODO("use context") } }那么onReceive的context对象是从哪里来的呢?同样我们先看广播接收器的注册流程: 广播接收器的注册流程同样,详细的广播相关工作流程可以阅读Android广播Broadcast的注册与广播源码过程详解(基于api29)这篇文章了解。因为在创建Receiver的时候并没有传入context,所以我们需要追踪他的注册流程,看看在哪里获取了context。我们先看到ContextImpl的registerReceiver方法: ContextImpl.class(api29)public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { // 注意参数 return registerReceiverInternal(receiver, getUserId(), filter, broadcastPermission, scheduler, getOuterContext(), 0); }registerReceiver方法最终会来到这个重载方法,我们可以注意到,这里有个getOuterContext,这个是什么?还记得Activity的context创建过程吗?这个方法获取的就是activity本身。我们继续看下去: ContextImpl.class(api29)private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) { IIntentReceiver rd = null; if (receiver != null) { if (mPackageInfo != null && context != null) { ... rd = mPackageInfo.getReceiverDispatcher( receiver, context, scheduler, mMainThread.getInstrumentation(), true); } ... } ... }这里利用context创建了ReceiverDispatcher,我们继续深入看: LoadedApk.class(api29)public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r, Context context, Handler handler, Instrumentation instrumentation, boolean registered) { synchronized (mReceivers) { LoadedApk.ReceiverDispatcher rd = null; ... if (rd == null) { rd = new ReceiverDispatcher(r, context, handler, instrumentation, registered); ... } ... } }ReceiverDispatcher.class(api29)ReceiverDispatcher(..., Context context,...) { ... mContext = context; ... }这里确实把receiver和context创建了ReceiverDispatcher,嗯?怎么没有给Receiver?其实这涉及到广播的内部设计结构。Receiver是没有跨进程通信能力的,而广播需要AMS的调控,所以必须有一个可以跟AMS沟通的对象,这个对象是InnerReceiver,而ReceiverDispatcher就是负责维护他们两个的联系,如下图: ReceiverDispatcher而onReceive方法也是由ReceiverDispatcher回调的,最后我们再看到回调onReceive的那部分代码: ReceiverDispatcher.java/Args.class;public final Runnable getRunnable() { return () ->{ ...; try { ...; // 可以看到这里回调了receiver的方法,这样整个接收广播的流程就走完了。 receiver.onReceive(mContext, intent); } } }Args是Receiver的内部类,mContext就是在创建ReceiverDispatcher时传入的对象,到这里我们就知道这个对象确实是Activity了。 但是,,不一定每个都是Activity。在源码中我们知道是通过getOuterContext来获取context,如果是通过别的context注册广播,那么对应的对象也就不同了,只是我们一般都是在Activity中创建广播,所以这个context一般是activity对象。ContentProvider
ContextProvider我们用的就比较少了,内容提供器主要是用于应用间内容共享的。虽然ContentProvider是由系统创建的,但是他本身并不属于Context家族体系内,所以他的context也是从其他获取的。老样子,先看ContentProvider的创建流程: ContentProvider的创建流程咦?这不是Application创建的流程图吗?是的,ContentProvider是伴随着应用启动被创建的,来看一张更加详细的流程图: ContentProvider.png我们把目光聚集到ContentProvider的创建上,也就是installContentProviders方法。同样,详细的ContentProvider工作流程可以访问Android中ContentProvider的启动与请求源码流程详解(基于api29)这篇文章。installContentProviders是在handleBindApplication中被调用的,我们看到调用这个方法的地方: private void handleBindApplication(AppBindData data) { try { // 创建Application app = data.info.makeApplication(data.restrictedBackupMode, null); ... if (!data.restrictedBackupMode) { if (!ArrayUtils.isEmpty(data.providers)) { // 安装ContentProvider installContentProviders(app, data.providers); } } }可以看到这里传入了application对象,我们继续看下去: private void installContentProviders( Context context, Listproviders) { final ArrayListresults = new ArrayList<>(); for (ProviderInfo cpi : providers) { ... ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); ... } ... }这里调用了installProvider,继续往下看: private ContentProviderHolder installProvider(Context context, ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ContentProvider localProvider = null; IContentProvider provider; if (holder == null || holder.provider == null) { ... // 这里c最终是由context构造的 Context c = null; ApplicationInfo ai = info.applicationInfo; if (context.getPackageName().equals(ai.packageName)) { c = context; } ... try { // 创建ContentProvider final java.lang.ClassLoader cl = c.getClassLoader(); LoadedApk packageInfo = peekPackageInfo(ai.packageName, true); ... localProvider = packageInfo.getAppFactory() .instantiateProvider(cl, info.name); provider = localProvider.getIContentProvider(); ... // 把context设置给ContentProvider localProvider.attachInfo(c, info); } ... } ... }这里最重要的一行代码是localProvider.attachInfo(c, info);,在这里把context设置给了ContentProvider,我们再深入一点看看: ContentProvider.class(api29)public void attachInfo(Context context, ProviderInfo info) { attachInfo(context, info, false); }private void attachInfo(Context context, ProviderInfo info, boolean testing) { ... if (mContext == null) { mContext = context; ... } ... }这里确实把context赋值给了ContentProvider的内部变量mContext,这样ContentProvider就可以使用Context了。而这个context正是一开始传进来的Application。从源码设计角度看Context
到这里关于Context的知识也讲得差不多了。研究Framework层知识,不能只停留在他是什么,有什么作用即可。Framework层他是一个整体,构成了android这个庞大的体系,还需要看Context,在其中扮演着什么样的角色,解决了什么样的问题。在window机制中我讲到window的存在是为了解决屏幕上view的显示逻辑与触摸反馈问题,在Hanlder机制中我写到整个android程序都是基于Handler机制来驱动执行的,而Context呢? Android系统是一个完整的生态,他搭建了一个环境,让各种程序可以运行在上面。而任何一个程序,想要运行在这个环境上,必须得到系统的允许,也就是软件安装。安卓与电脑不同的是,他不是任意一个程序就可以直接访问到系统的资源。我们在window上可以写一个java程序,然后直接开启一个文件流就可以读取和修改文件了。而Android没这么简单,他任意一个程序的运行都必须经过系统的调控。也就是,即时程序获得允许(安装在手机上了),程序本身要运行,还得是系统来控制程序运行,程序无法自发地执行在Android环境中。我们通过源码可以知道程序的main方法,仅仅只是开启了线程的Looper循环,而后续的一切,都必须等待AMS来控制。 那应用程序自己硬要执行可不可以?可以,但是没卵用。想要获得系统资源,如启动四大组件、读取布局文件、读写数据库、调用系统柜摄像头等等,都必须要通过Context,而context必须要通过AMS来获取。这就区分了一个程序是一个普通的Java程序,还是android程序。 Context承受的两大重要职责是:身份权限、程序访问系统的接口。一个Java类,如果没有context那么就是一个普通的Java类,而当他获得context那么他就可以称之为一个组件了,因为它获得了访问系统的权限,他不再是一个普通的身份,是属于android“公民”了。而“公民”并不是无法无天,系统也可以通过context来封装以及限制程序的权限。要想弹出一个通知,你必须通过这个api,用户关闭你的通知权限,你就别想通过第二条路来弹出通知了。同时 程序也无需知道底层到底是如何实现,只管调用api即可。四大组件为何称为四大组件,因为他们生来就有了context,特别是activity和service,包括Application。而我们写的一切程序,都必须间接或者直接从其中获取context。 总而言之,context就是负责区分android内外程序的一个机制,限制程序访问系统资源的权限。总结
文章从什么是context开始介绍,再针对context的不同子类进行解析,最后结合源码深入地讲解了context的创建过程。最后再谈了我对context的设计理解。 关于context想说的就已经说完了。虽然这些内容日常很少用得到,但是非常有助于我们对Android整个系统框架的理解。而当我们对系统有更加深入的理解后,写出来的程序也就会更加健壮。 希望文章对你有帮助。 全文到此,原创不易,觉得有帮助可以点赞收藏评论转发。 笔者能力有限,有任何想法欢迎评论区交流指正。 如需转载请私信交流。深入剖析Android核心:Context机制详解的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于、深入剖析Android核心:Context机制详解的信息别忘了在本站进行查找哦。
【深入剖析Android核心:Context机制详解】相关文章:
用户评论
我一直很好奇Android应用是怎么运作的,这个主题应该能让我了解一下!
有16位网友表示赞同!
想学习一下android开发,这篇文章看上去蛮深入的
有18位网友表示赞同!
Context是一个很核心的概念吧,必须好好理解下。
有19位网友表示赞同!
最近在做安卓项目,碰到一些Context的使用问题,这篇文章可以帮到我吗?
有13位网友表示赞同!
看完标题就感觉很高级的样子,有点期待能学到新知识!
有6位网友表示赞同!
Android开发入门指南里讲过Context的,希望能深入了解一下原理
有18位网友表示赞同!
我以前只知道Context是个对象,不知道它还能这么复杂
有15位网友表示赞同!
这篇帖子内容是不是会比较专业?适合有一定开发经验的人看吗?
有11位网友表示赞同!
学习new android知识!全面解析太合适了!
有20位网友表示赞同!
Context机制在不同的安卓版本之间差异很大吗?文章里提到了吗?
有20位网友表示赞同!
我一直想搞懂Android的内部运行机制,这篇文章能让我了解吗?
有17位网友表示赞同!
希望作者能把Context的原理讲得通俗易懂
有10位网友表示赞同!
这篇写的详细吗?包括了常用的Context类型的介绍和用法?
有13位网友表示赞同!
有代码示例吗?实际操作才能理解更好地
有12位网友表示赞同!
安卓开发者的必修课啊!学习一遍Context机制就更熟练了!
有13位网友表示赞同!
这篇文章应该能解答我很多之前关于Context的概念上的疑惑
有11位网友表示赞同!
看标题感觉很强大,期待深入了解Android的核心机制
有7位网友表示赞同!
想提升android开发水平,这篇文章绝对可以参考学习
有8位网友表示赞同!