Activity的继承关系
Service和Application的继承关系
通过对比可以清晰地发现,Service和Application的类继承关系比较像,而Activity还多了一层继承ContextThemeWrapper,这是因为Activity有主题的概念,而Service是没有界面的服务,Application更是一个抽象的东西,它也是通过Activity类呈现的。
下面来看一下三者在Context方面的区别
上文已经指出,Context的真正实现都在ContextImpl中,也就是说Context的大部分方法调用都会转到ContextImpl中,而三者的创建均在ActivityThread中完成,我之前写过一篇文章Android源码分析-Activity的启动过程,在文中我指出Activity启动的核心过程是在ActivityThread中完成的,这里要说明的是,Application和Service的创建也是在ActivityThread中完成的。下面我们看下三者在创建时是怎么和ContextImpl相关联的。
Activity对象中ContextImpl的创建
代码为ActivityThread中的performLaunchActivity方法
-
if (activity != null) {
-
Context appContext = createBaseContextForActivity(r, activity);
-
/**
-
* createBaseContextForActivity中创建ContextImpl的代码
-
* ContextImpl appContext = new ContextImpl();
-
* appContext.init(r.packageInfo, r.token, this);
-
* appContext.setOuterContext(activity);
-
*/
-
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
-
Configuration config = new Configuration(mCompatConfiguration);
-
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
-
+ r.activityInfo.name + " with config " + config);
-
activity.attach(appContext, this, getInstrumentation(), r.token,
-
r.ident, app, r.intent, r.activityInfo, title, r.parent,
-
r.embeddedID, r.lastNonConfigurationInstances, config);
-
if (customIntent != null) {
-
activity.mIntent = customIntent;
-
}
-
…
-
}
可以看出,Activity在创建的时候会new一个ContextImpl对象并在attach方法中关联它,需要注意的是,创建Activity使用的数据结构是ActivityClientRecord。
Application对象中ContextImpl的创建
代码在ActivityThread中的handleBindApplication方法中,此方法内部调用了makeApplication方法
-
public Application makeApplication(boolean forceDefaultAppClass,
-
Instrumentation instrumentation) {
-
if (mApplication != null) {
-
return mApplication;
-
}
-
Application app = null;
-
String appClass = mApplicationInfo.className;
-
if (forceDefaultAppClass || (appClass == null)) {
-
appClass = “android.app.Application”;
-
}
-
try {
-
java.lang.ClassLoader cl = getClassLoader();
-
ContextImpl appContext = new ContextImpl();
-
appContext.init(this, null, mActivityThread);
-
app = mActivityThread.mInstrumentation.newApplication(
-
cl, appClass, appContext);
-
appContext.setOuterContext(app);
-
} catch (Exception e) {
-
if (!mActivityThread.mInstrumentation.onException(app, e)) {
-
throw new RuntimeException(
-
"Unable to instantiate application " + appClass
-
+ ": " + e.toString(), e);
-
}
-
}
-
…
-
}
看代码发现和Activity中ContextImpl的创建是相同的。
Service对象中ContextImpl的创建
通过查看代码发现和Activity、Application是一致的。分析到这里,那么三者的Context有什么区别呢?没有区别吗?尽管如此,有一些细节是确定的:Dialog的使用需要Activity,在桌面上我们采用Application的Context无法弹出对话框,同时在桌面上想启动新的activity,我们需要为intent设置FLAG_ACTIVITY_NEW_TASK标志,否则无法启动activity,这一切都说明,起码Application的Context和Activity的Context还是有区别的,当然这也可能不是Context的区别,因为在桌面上,我们的应用没有界面,这意味着我们能干的事情可能受到了限制,事情的细节目前我还没有搞的很清楚。
很明确,不同的Context得到的都是同一份资源。这是很好理解的,请看下面的分析
得到资源的方式为context.getResources,而真正的实现位于ContextImpl中的getResources方法,在ContextImpl中有一个成员 private Resources mResources,它就是getResources方法返回的结果,mResources的赋值代码为:
mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
Display.DEFAULT_DISPLAY, null, compatInfo, activityToken);
下面看一下ResourcesManager的getTopLevelResources方法,这个方法的思想是这样的:在ResourcesManager中,所有的资源对象都被存储在ArrayMap中,首先根据当前的请求参数去查找资源,如果找到了就返回,否则就创建一个资源对象放到ArrayMap中。有一点需要说明的是为什么会有多个资源对象,原因很简单,因为res下可能存在多个适配不同设备、不同分辨率、不同系统版本的目录,按照android系统的设计,不同设备在访问同一个应用的时候访问的资源可以不同,比如drawable-hdpi和drawable-xhdpi就是典型的例子。
-
public Resources getTopLevelResources(String resDir, int displayId,
-
Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
-
final float scale = compatInfo.applicationScale;
-
ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale,
-
token);
-
Resources r;
-
synchronized (this) {
-
// Resources is app scale dependent.
-
if (false) {
-
Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
-
}
-
WeakReference wr = mActiveResources.get(key);
-
r = wr != null ? wr.get() : null;
-
//if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
-
if (r != null && r.getAssets().isUpToDate()) {
-
if (false) {
-
Slog.w(TAG, "Returning cached resources " + r + " " + resDir
-
+ “: appScale=” + r.getCompatibilityInfo().applicationScale);
-
}
-
return r;
-
}
-
}
-
//if (r != null) {
-
// Slog.w(TAG, "Throwing away out-of-date resources!!! "
-
// + r + " " + resDir);
-
//}
-
AssetManager assets = new AssetManager();
-
if (assets.addAssetPath(resDir) == 0) {
-
return null;
-
}
-
//Slog.i(TAG, “Resource: key=” + key + “, display metrics=” + metrics);
-
DisplayMetrics dm = getDisplayMetricsLocked(displayId);
-
Configuration config;
-
boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
-
final boolean hasOverrideConfig = key.hasOverrideConfiguration();
-
if (!isDefaultDisplay || hasOverrideConfig) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!**](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算