2024字节跳动Android面试真题解析,阿里面试100%会问到的JVM

1、MVVM架构模式概览

这是使用MVVM架构模式+Kotlin协程+JetPack(ViewModel+LiveData)+Retrofit的架构,实现WanAndroid登录接口的小DEMO,后续会慢慢完善WanAndroid客户端

1、ViewModel

为了从界面控制器Activity/Fragment逻辑中分离出视图View数据所有权,架构组件为界面控制器提供了 ViewModel 辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个 Activity 或 Fragment 实例使用。

2、LiveData

LiveData 是一种可观察的数据存储器类,具有生命周期感知能力,意指它遵循其他应用组件如 Activity、Fragment 或 Service 生命周期,可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。LiveData 对象通常存储在 ViewModel 对象中,并可通过 getter 方法进行访问。

3、Kotlin协程

协程依附在线程上,可以实现顺序编写异步代码,自动进行线程切换。并且ViewModelScope为应用中的每个 ViewModel 定义了 ViewModelScope。如果 ViewModel 已清除,则在此范围内启动的协程都会自动取消。

4、Retrofit

将服务接口中的网络请求函数声明为suspend挂起接口函数,以支持Kotlin线程,并将suspend函数结果作为 LiveData 对象传送。

2、ViewModel

//获取ViewModel
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)` 

ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle。ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。

3、LiveData

//对User数据进行观察
viewModel.user.observe(this, Observer {
    //展示登录结果
    if (it.errorCode == 0) {
        Toast.makeText(this, it.data?.nickname, Toast.LENGTH_SHORT).show()
    } else {
        Toast.makeText(this, it.errorMsg, Toast.LENGTH_SHORT).show()
    }
})

使用 LiveData 具有以下优势:确保界面符合数据状态

LiveData 遵循观察者模式。当生命周期状态发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。观察者可以在每次发生更改时更新界面,而不是在每次应用数据发生更改时更新界面。

不会发生内存泄漏

观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。

不会因 Activity 停止而导致崩溃

如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。
不再需要手动处理生命周期
界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。

数据始终保持最新状态

如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。

适当的配置更改

如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。

共享资源

可以使用单一实例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。

4、Kotlin协程

4.1、异步的本质

什么是异步?

异步就是同时进行一个以上彼此目的不同的任务。

但是对于有前后依赖关系的任务,异步该如何处理呢?

利用异步中的回调机制处理。

为什么需要异步回调机制?

因为不同的任务之间存在前后的依赖关系。

异步回调机制有什么缺点?

代码结构过分耦合,遇到多重函数回调的嵌套耦合,也就是回调地狱,代码会难以维护。

解决回调地狱的方案有什么?

链式调用结构。
常见方式就是使用RxJava,它是反应函数式编程在Java中的实现。
但是RxJava中流的创建、转化与消费都需要使用到各种类和丰富的操作符,加大了RxJava的学习成本。
减少在无封装情况下使用RxJava,因为你无法保证团队里面的每一个成员都能看懂它,并且在修改时都能做出正确选择。

在串行的执行中,虽然代码确实是顺序执行的,但其实是在不同的线程上顺序执行的。那为什么在串行的执行中代码执行顺序一致,却还要使用回调呢?

因为串行的执行中,执行是阻塞式的,主线程的阻塞会导致很严重的问题,所以所有的耗时操作不能在主线程中执行,所以就需要多线程并行来执行。

在并行的执行中,异步回调其实就是代码的多线程顺序执行。那能不能既按照顺序的方式编写代码,又可以让代码在不同的线程顺序执行,自动完成线程的切换工作呢?

那就是Kotlin协程。
Kotlin 的协程是一种无栈协程的实现,它的控制流转依靠对协程体本身编译生成的状态机的状态流转来实现,变量保存也是通过闭包语法来实现的。

结论:

异步回调就是代码的多线程顺序执行,而Kotlin协程可以实现顺序编写异步代码,自动进行线程切换。

那么协程自动进行线程切换的原理是什么?

Yield:让出CPU,放弃调度控制权,回到上一次Resume的地方
Resume:获取调度控制权,继续执行程序,到上一次Yield的地方

例子:

1. GlobalScope.launch发起了一个协程,并在IO线程上执行,
2\. 在协程里,去调用接口获取结果。
3. 拿到结果,使用withContext(Dispatchers.Main)切换到主线程并更新界面

4.2、协程的类型

是协程范围,指的是协程内的代码运行的时间周期范围,如果超出了指定的协程范围,协程会被取消执行。

GlobalScope

指的是与应用进程相同的协程范围,也就是在进程没有结束之前协程内的代码都可以运行。

JetPack中提供的生命周期感知型协程范围:

ViewModelScope,为应用中的每个 ViewModel 定义了 ViewModelScope。如果 ViewModel 已清除,则在此范围内启动的协程都会自动取消。

LifecycleScope,为每个 Lifecycle 对象定义了 LifecycleScope。在此范围内启动的协程会在 Lifecycle 被销毁时取消。

使用 LiveData 时,可能需要异步计算值。可以使用 liveData 构建器函数调用 suspend 函数,并将结果作为 LiveData 对象传送。

相关链接:https://developer.android.google.cn/topic/libraries/architecture/coroutines

4.3、协程的启动

launch方法:

/**
 * 重要知识:ViewModel+协程
 */
fun ViewModel.launch(
    block: suspend CoroutineScope.() -> Unit,
    onError: (e: Throwable) -> Unit = {},
    onComplete: () -> Unit = {}
) {
    viewModelScope.launch(CoroutineExceptionHandler { _, e -> onError(e) }) {
        try {
            block.invoke(this)
        } finally {
            onComplete()
        }
    }
}

源码:

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

4.3.1、launch方法解释

context

协程上下文,可以指定协程运行的线程。默认与指定的CoroutineScope中的coroutineContext保持一致,比如GlobalScope默认运行在一个后台工作线程内。也可以通过显示指定参数来更改协程运行的线程,Dispatchers提供了几个值可以指定:Dispatchers.Default、Dispatchers.Main、Dispatchers.IO、Dispatchers.Unconfined。

start

协程的启动模式。默认的CoroutineStart.DEFAULT是指协程立即执行,除此之外还有CoroutineStart.LAZY、CoroutineStart.ATOMIC、CoroutineStart.UNDISPATCHED。

block

协程主体。也就是要在协程内部运行的代码,可以通过lamda表达式的方式方便的编写协程内运行的代码。

CoroutineExceptionHandler

指定CoroutineExceptionHandler来处理协程内部的异常。

Job

返回值,对当前创建的协程的引用。可以通过Job的start、cancel、join等方法来控制协程的启动和取消。

4.4、suspend挂起函数

suspend关键字只起到了标志这个函数是一个耗时操作,必须放在协程中执行的作用,而withContext方法则进行了线程的切换工作。

协程中的代码自动地切换到其他线程之后又自动地切换回了主线程!顺序编写保证了逻辑上的直观性,协程的自动线程切换又保证了代码的非阻塞性。挂起函数必须在协程或者其他挂起函数中被调用,也就是挂起函数必须直接或者间接地在协程中执行。

那为什么协程中的代码没有在主线程中执行呢?而且执行完毕为什么还会自动地切回主线程呢?

协程的挂起可以理解为协程中的代码离开协程所在线程的过程,协程的恢复可以理解为协程中的代码重新进入协程所在线程的过程。协程就是通过的这个挂起恢复机制进行线程的切换。

4.5、async await方法

用async方法包裹了suspend方法来执行并发请求,并发结果都返回之后,切换到主线程,接着再用await方法来获取并发请求结果。

5、Retrofit

HTTP接口suspend挂起函数:

interface ApiService {
    @FormUrlEncoded
    @POST("user/login")
    suspend fun loginForm(@Field("username")  username: String,@Field("password")  password: String): BaseResponse<User>
}

kotlin泛型:

data class BaseResponse<T>(
    val errorCode: Int=0,
    val errorMsg:String? = null,
    var data: T? = null
)

这是使用MVVM架构模式+Kotlin协程+JetPack(ViewModel+LiveData)+Retrofit的架构,实现WanAndroid登录接口的小DEMO,后续会慢慢完善WanAndroid客户端

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

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

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

img

img

img

img

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。

image

Android高级架构师之路很漫长,一起共勉吧!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

内容对你有帮助,可以扫码获取!!(备注:Android)**

[外链图片转存中…(img-bEFMX2qy-1713595382226)]

最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。

[外链图片转存中…(img-wclS31WA-1713595382227)]

Android高级架构师之路很漫长,一起共勉吧!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 23
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在面试中,面试官可能你是否有JVM调优的经验。JVM调优是指通过调整JVM的参数和配置来优化Java应用程序的性能和稳定性。要回答这个,你可以提到以下几点。首先,你可以提到你对JVM的体系结构有深入的了解,包括方法区、堆、栈、本地方法栈和程序计数器等组成部分。\[1\]其次,你可以提到你了解如何观察JVM的运行参数,比如使用jps、jstack、jhat等JVM自带的工具命令,以及Java自带的JMC图形界面工具,来监控Java进程的线程状态、CPU和内存占用情况,以及GC状态等。\[3\]最后,你可以提到你有经验调整JVM的参数和配置,以优化Java应用程序的性能和稳定性。这包括调整堆大小、垃圾回收器的选择和配置、线程池的大小等。通过这些调优措施,可以提高应用程序的响应速度、减少内存占用和避免OOM等。 #### 引用[.reference_title] - *1* *2* [面试你关于JVM性能调优那些事儿,你要怎样优雅回答?](https://blog.csdn.net/wdj_yyds/article/details/124759534)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [面试经常被 JVM 如何调优?这个该怎么回答?没有实际调优经验怎么办?](https://blog.csdn.net/weixin_60707895/article/details/129950623)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值