《Android内核剖析》笔记 第7章 理解Context

不同点,可以从几个方面来说:首先看它们的继承关系

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方法

  1. if (activity != null) {

  2. Context appContext = createBaseContextForActivity(r, activity);

  3. /**

  4. *  createBaseContextForActivity中创建ContextImpl的代码

  5. *  ContextImpl appContext = new ContextImpl();

  6. *  appContext.init(r.packageInfo, r.token, this);

  7. *  appContext.setOuterContext(activity);

  8. */

  9. CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());

  10. Configuration config = new Configuration(mCompatConfiguration);

  11. if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "

  12. + r.activityInfo.name + " with config " + config);

  13. activity.attach(appContext, this, getInstrumentation(), r.token,

  14. r.ident, app, r.intent, r.activityInfo, title, r.parent,

  15. r.embeddedID, r.lastNonConfigurationInstances, config);

  16. if (customIntent != null) {

  17. activity.mIntent = customIntent;

  18. }

  19. }

可以看出,Activity在创建的时候会new一个ContextImpl对象并在attach方法中关联它,需要注意的是,创建Activity使用的数据结构是ActivityClientRecord。

Application对象中ContextImpl的创建

代码在ActivityThread中的handleBindApplication方法中,此方法内部调用了makeApplication方法

  1. public Application makeApplication(boolean forceDefaultAppClass,

  2. Instrumentation instrumentation) {

  3. if (mApplication != null) {

  4. return mApplication;

  5. }

  6. Application app = null;

  7. String appClass = mApplicationInfo.className;

  8. if (forceDefaultAppClass || (appClass == null)) {

  9. appClass = “android.app.Application”;

  10. }

  11. try {

  12. java.lang.ClassLoader cl = getClassLoader();

  13. ContextImpl appContext = new ContextImpl();

  14. appContext.init(this, null, mActivityThread);

  15. app = mActivityThread.mInstrumentation.newApplication(

  16. cl, appClass, appContext);

  17. appContext.setOuterContext(app);

  18. } catch (Exception e) {

  19. if (!mActivityThread.mInstrumentation.onException(app, e)) {

  20. throw new RuntimeException(

  21. "Unable to instantiate application " + appClass

  22. + ": " + e.toString(), e);

  23. }

  24. }

  25. }

看代码发现和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得到的都是同一份资源。这是很好理解的,请看下面的分析

得到资源的方式为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就是典型的例子。

  1. public Resources getTopLevelResources(String resDir, int displayId,

  2. Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {

  3. final float scale = compatInfo.applicationScale;

  4. ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale,

  5. token);

  6. Resources r;

  7. synchronized (this) {

  8. // Resources is app scale dependent.

  9. if (false) {

  10. Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);

  11. }

  12. WeakReference wr = mActiveResources.get(key);

  13. r = wr != null ? wr.get() : null;

  14. //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());

  15. if (r != null && r.getAssets().isUpToDate()) {

  16. if (false) {

  17. Slog.w(TAG, "Returning cached resources " + r + " " + resDir

  18. + “: appScale=” + r.getCompatibilityInfo().applicationScale);

  19. }

  20. return r;

  21. }

  22. }

  23. //if (r != null) {

  24. //    Slog.w(TAG, "Throwing away out-of-date resources!!! "

  25. //            + r + " " + resDir);

  26. //}

  27. AssetManager assets = new AssetManager();

  28. if (assets.addAssetPath(resDir) == 0) {

  29. return null;

  30. }

  31. //Slog.i(TAG, “Resource: key=” + key + “, display metrics=” + metrics);

  32. DisplayMetrics dm = getDisplayMetricsLocked(displayId);

  33. Configuration config;

  34. boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);

  35. final boolean hasOverrideConfig = key.hasOverrideConfiguration();

  36. if (!isDefaultDisplay || hasOverrideConfig) {

  37. config = new Configuration(getConfiguration());

  38. if (!isDefaultDisplay) {

  39. applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);

  40. }

  41. if (hasOverrideConfig) {

  42. config.updateFrom(key.mOverrideConfiguration);

  43. }

  44. } else {

  45. config = getConfiguration();

  46. }

  47. r = new Resources(assets, dm, config, compatInfo, token);

  48. if (false) {

  49. Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "

  50. + r.getConfiguration() + " appScale="

  51. + r.getCompatibilityInfo().applicationScale);

  52. }

  53. synchronized (this) {

  54. WeakReference wr = mActiveResources.get(key);

  55. Resources existing = wr != null ? wr.get() : null;

  56. if (existing != null && existing.getAssets().isUpToDate()) {

  57. // Someone else already created the resources while we were

  58. // unlocked; go ahead and use theirs.

  59. r.getAssets().close();

  60. return existing;

  61. }

  62. // XXX need to remove entries when weak references go away

  63. mActiveResources.put(key, new WeakReference®);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,阿里,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

腾讯T3架构师学习专题资料

如果你觉得自己学习效率低,缺乏正确的指导,可以点击加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧

群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

阿里,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

[外链图片转存中…(img-Vs1Qzk0y-1710882485615)]

如果你觉得自己学习效率低,缺乏正确的指导,可以点击加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧

群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

  • 12
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值