iOS多线程面试题,Android应用Context详解及源码解析,2024大厂面试合集

SO,由此说明一个Activity就有一个Context,而且生命周期和Activity类相同(记住这句话,写应用就可以避免一些低级的内存泄漏问题)。

3-2 Service中ContextImpl实例化源码分析

写APP时我们通过startService或者bindService方法创建一个新Service时就会回调ActivityThread类的handleCreateService()方法完成相关数据操作(具体关于ActivityThread调运handleCreateService时机等细节分析与上面Activity雷同,后边文章会做分析)。具体handleCreateService方法代码如下:

private void handleCreateService(CreateServiceData data) {

//类似上面Activity的创建,这里创建service对象实例

Service service = null;

try {

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

service = (Service) cl.loadClass(data.info.name).newInstance();

} catch (Exception e) {

}

try {

//不做过多解释,创建一个Context对象

ContextImpl context = ContextImpl.createAppContext(this, packageInfo);

//特别特别留意这里!!!

//ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Service对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Service)。

context.setOuterContext(service);

Application app = packageInfo.makeApplication(false, mInstrumentation);

//将上面创建的context传入到service的attach方法

service.attach(context, this, data.info.name, data.token, app,

ActivityManagerNative.getDefault());

service.onCreate();

} catch (Exception e) {

}

}

再来看看service.attach,也就是Service中的attach方法,如下:

public final void attach(

Context context,

ActivityThread thread, String className, IBinder token,

Application application, Object activityManager) {

//特别特别留意这里!!!

//与上面handleCreateService方法中setOuterContext语句类似,不同的在于:

//通过ContextWrapper类的attachBaseContext方法,将handleCreateService中实例化的ContextImpl对象传入到ContextWrapper类的mBase变量,这样ContextWrapper(Context子类)类的成员mBase就被实例化为Context的实现类ContextImpl

attachBaseContext(context);

}

可以看出步骤流程和Activity的类似,只是实现细节略有不同而已。

SO,由此说明一个Service就有一个Context,而且生命周期和Service类相同(记住这句话,写应用就可以避免一些低级的内存泄漏问题)。

3-3 Application中ContextImpl实例化源码分析

当我们写好一个APP以后每次重新启动时都会首先创建Application对象(每个APP都有一个唯一的全局Application对象,与整个APP的生命周期相同)。创建Application的过程也在ActivityThread类的handleBindApplication()方法完成相关数据操作(具体关于ActivityThread调运handleBindApplication时机等细节分析与上面Activity雷同,后边文章会做分析)。而ContextImpl的创建是在该方法中调运LoadedApk类的makeApplication方法中实现,LoadedApk类的makeApplication()方法中源代码如下:

public Application makeApplication(boolean forceDefaultAppClass,

Instrumentation instrumentation) {

//只有新创建的APP才会走if代码块之后的剩余逻辑

if (mApplication != null) {

return mApplication;

}

//即将创建的Application对象

Application app = null;

String appClass = mApplicationInfo.className;

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

appClass = “android.app.Application”;

}

try {

java.lang.ClassLoader cl = getClassLoader();

if (!mPackageName.equals(“android”)) {

initializeJavaContextClassLoader();

}

//不做过多解释,创建一个Context对象

ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);

//将Context传入Instrumentation类的newApplication方法

app = mActivityThread.mInstrumentation.newApplication(

cl, appClass, appContext);

//特别特别留意这里!!!

//ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Application对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Application)。

appContext.setOuterContext(app);

} catch (Exception e) {

}

return app;

}

接着看看Instrumentation.newApplication方法。如下源码:

public Application newApplication(ClassLoader cl, String className, Context context)

throws InstantiationException, IllegalAccessException,

ClassNotFoundException {

return newApplication(cl.loadClass(className), context);

}

继续看重载两个参数的newApplication方法,如下:

static public Application newApplication(Class<?> clazz, Context context)

throws InstantiationException, IllegalAccessException,

ClassNotFoundException {

//继续传递context

app.attach(context);

return app;

}

继续看下Application类的attach方法,如下:

final void attach(Context context) {

//特别特别留意这里!!!

//与上面makeApplication方法中setOuterContext语句类似,不同的在于:

//通过ContextWrapper类的attachBaseContext方法,将makeApplication中实例化的ContextImpl对象传入到ContextWrapper类的mBase变量,这样ContextWrapper(Context子类)类的成员mBase就被实例化为Application的实现类ContextImpl

attachBaseContext(context);

}

可以看出步骤流程和Activity的类似,只是实现细节略有不同而已。

SO,由此说明一个Application就有一个Context,而且生命周期和Application类相同(然而一个App只有一个Application,而且与应用生命周期相同)。

4 应用程序APP各种Context访问资源的唯一性分析


你可能会有疑问,这么多Context都是不同实例,那么我们平时写App时通过context.getResources得到资源是不是就不是同一份呢?下面我们从源码来分析下,如下:

class ContextImpl extends Context {

private final ResourcesManager mResourcesManager;

private final Resources mResources;

@Override

public Resources getResources() {

return mResources;

}

}

看见没,有了上面分析我们可以很确定平时写的App中context.getResources方法获得的Resources对象就是上面ContextImpl的成员变量mResources。那我们追踪可以发现mResources的赋值操作如下:

private ContextImpl(ContextImpl container, ActivityThread mainThread,

LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,

Display display, Configuration overrideConfiguration) {

//单例模式获取ResourcesManager对象

mResourcesManager = ResourcesManager.getInstance();

//packageInfo对于一个APP来说只有一个,所以resources 是同一份

Resources resources = packageInfo.getResources(mainThread);

if (resources != null) {

if (activityToken != null

|| displayId != Display.DEFAULT_DISPLAY

|| overrideConfiguration != null

|| (compatInfo != null && compatInfo.applicationScale

!= resources.getCompatibilityInfo().applicationScale)) {

//mResourcesManager是单例,所以resources是同一份

resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),

packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),

packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,

overrideConfiguration, compatInfo, activityToken);

}

}

//把resources赋值给mResources

mResources = resources;

}

由此可以看出在设备其他因素不变的情况下我们通过不同的Context实例得到的Resources是同一套资源。

PS一句,同样的分析方法也可以发现Context类的packageInfo对于一个应用来说也只有一份。感兴趣可以自行分析。

5 应用程序APP各种Context使用区分源码分析


5-1 先来解决getApplication和getApplicationContext的区别

很多人一直区分不开这两个方法的区别,这里从源码来分析一下,如下:

首先来看getApplication方法,你会发现Application与Context都没有提供该方法,这个方法是哪提供的呢?我们看下Activity与Service中的代码,可以发下如下:

public class Activity extends ContextThemeWrapper

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

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

感觉现在好多人都在说什么安卓快凉了,工作越来越难找了。又是说什么程序员中年危机啥的,为啥我这年近30的老农根本没有这种感觉,反倒觉得那些贩卖焦虑的都是瞎j8扯谈。当然,职业危机意识确实是要有的,但根本没到那种草木皆兵的地步好吗?

Android凉了都是弱者的借口和说辞。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

,就是现在。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值