2024年最全【Android Jetpack高手日志】ViewModel 从入门到精通(1),面试建议信英语作文

文末

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的相关的几十套腾讯、头条、阿里、美团等公司21年的面试专题,其中把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分免费分享给大家,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家~

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【延伸Android必备知识点】

这里只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢在关注一下~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

“androidx.lifecycle.ViewModelProvider.DefaultKey”;

String canonicalName = modelClass.getCanonicalName();

因为第二个参数是通过getDefaultViewModelProviderFactory()获取到的,前面说过ActivityHasDefaultViewModelProviderFactory的实现类,我们再来看看Activity中的getDefaultViewModelProviderFactory()方法

@NonNull

@Override

public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {

if (getApplication() == null) {

throw new IllegalStateException("Your activity is not yet attached to the "

  • “Application instance. You can’t request ViewModel before onCreate call.”);

}

if (mDefaultFactory == null) {

mDefaultFactory = new SavedStateViewModelFactory(

getApplication(),

this,

getIntent() != null ? getIntent().getExtras() : null);

}

return mDefaultFactory;

}

可以看到是通过一个SavedStateViewModelFactory来获取ViewModelProvider.Factory,命名也很清晰直观,就是保存状态的 ViewModel 工厂。

下面我们再来看看get(AccountViewModel::class.java)方法

@NonNull

@MainThread

public T get(@NonNull Class modelClass) {

String canonicalName = modelClass.getCanonicalName();

if (canonicalName == null) {

throw new IllegalArgumentException(“Local and anonymous classes can not be ViewModels”);

}

return get(DEFAULT_KEY + “:” + canonicalName, modelClass); //key的名字

}

@SuppressWarnings(“unchecked”)

@NonNull

@MainThread

public T get(@NonNull String key, @NonNull Class modelClass) {

ViewModel viewModel = mViewModelStore.get(key); //从 hashmap 中获取 viewmodel 实例

if (modelClass.isInstance(viewModel)) {

if (mFactory instanceof OnRequeryFactory) {

((OnRequeryFactory) mFactory).onRequery(viewModel);

}

return (T) viewModel;

} else {

//noinspection StatementWithEmptyBody

if (viewModel != null) {

// TODO: log a warning.

}

}

//使用 Factory 创建

if (mFactory instanceof KeyedFactory) {

viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);

} else {

viewModel = (mFactory).create(modelClass);

}

//存入 viewModelStore

mViewModelStore.put(key, viewModel);

return (T) viewModel;

}

简单来说就是从mViewModelStore获取 ViewModel,如果没有获取到,就使用 Factory 创建,然后存入mViewModelStore

这样逻辑就清楚了,ViewModelProvider(this).get(AccountViewModel::class.java)会把ViewModel存入ViewModelStore

因为 Activity 实现了ViewModelStoreOwner 接口,可以理解为 ViewModelStore(ViewModel 存储器)的拥有者,也就是说我们的 Activity/Fragment 是 ViewModel 存储器的拥有者。

public interface ViewModelStoreOwner {

/**

  • Returns owned {@link ViewModelStore}

  • @return a {@code ViewModelStore}

*/

@NonNull

ViewModelStore getViewModelStore();

}

然后我们来看看该方法在 Activity 中的实现

@NonNull

@Override

public ViewModelStore getViewModelStore() {

if (getApplication() == null) {

throw new IllegalStateException("Your activity is not yet attached to the "

  • “Application instance. You can’t request ViewModel before onCreate call.”);

}

if (mViewModelStore == null) {

//从getLastNonConfigurationInstance 获取

NonConfigurationInstances nc =

(NonConfigurationInstances) getLastNonConfigurationInstance();

if (nc != null) {

//恢复viewmodelstore

// Restore the ViewModelStore from NonConfigurationInstances

mViewModelStore = nc.viewModelStore;

}

//如果还是获取不到,就新建一个

if (mViewModelStore == null) {

mViewModelStore = new ViewModelStore();

}

}

return mViewModelStore;

}

从该方法中可以看到,Activity(ViewModelStoreOwner)内部最终会创建一个ViewModelStore,用来存储ViewModel,接下来我们来看getLastNonConfigurationInstance方法

//ComponentActivity.java

NonConfigurationInstances mLastNonConfigurationInstances;

@Nullable

public Object getLastNonConfigurationInstance() {

return mLastNonConfigurationInstances != null

? mLastNonConfigurationInstances.activity : null;

}

static final class NonConfigurationInstances {

Object custom;

ViewModelStore viewModelStore;

}

为什么使用 getLastNonConfigurationInstance 方法呢,我们先来看看 Activity 状态保存和恢复:

onSaveInstanceStateonRetainNonConfigurationInstance 的使用场景区别

我们知道,在屏幕旋转的时候会执行保存状态和恢复状态的方法

@Override

protected void onSaveInstanceState(Bundle outBundle) { //保存

super.onSaveInstanceState(outBundle);

}

@Override

protected void onRestoreInstanceState(Bundle savedInstanceState) { //恢复

super.onRestoreInstanceState(savedInstanceState);

}

//注意,如果你是继承 AppcompactActiviy,该方法已经在它的父类 ComponentActivity 定义为 final,子类无法重写

//继承 Activity 就可以

public Object onRetainNonConfigurationInstance() {

// TODO Auto-generated method stub

// 在这里设置需要保存的内容,在切换时不是bundle了,我们可以直接通过object来代替。

return super.onRetainNonConfigurationInstance();

}

@Nullable

@Override

public Object getLastNonConfigurationInstance() {

return super.getLastNonConfigurationInstance();

}

在 Android 10,他们的执行顺序都在onStoponDestory之间,而且onSaveInstanceStateonRetainNonConfigurationInstance先执行,一般情况我们保存的数据不是太大,适合放在 Bundle 中,这个时候使用onSaveInstanceState比较合适,如果要保存的数据不适合放在 Bundle 中(比如: 一个socket)或是数据比较大(比如 Bitmap),那么这个时间我们就应该使用onRetainNonConfigurationInstance(),而且我们使用onRetainNonConfigurationInstance()可以保存任何类型的对象,像AsyncTaskSQLiteDatabse,我们都可以进行保存。这些类型的数据可能会被一个新的Activity重新使用。

也就是说 Bundle 中只能放一些特定的类型,比如基本数据类型,数组,Serialable 对象,而onRetainNonConfigurationInstance中只要是个 Object 对象就可以了

同时当某个activity变得“容易”被系统销毁时,该activityonSaveInstanceState就会被执行,而onRetainNonConfigurationInstance更多的是时候是在配置改变时操作的,这个时候保存一些不会因为配置改变而发生改变的东西,而且onSaveInstanceState数据是序列化保存到磁盘中。而onRetainNonConfigurationInstance保存的数据是存在内存中

所以这里我们的 ViewModel 肯定是放在onRetainNonConfigurationInstance方法中,再来看看

//ComponentActivity.java

@Override

@Nullable

public final Object onRetainNonConfigurationInstance() {

Object custom = onRetainCustomNonConfigurationInstance();

ViewModelStore viewModelStore = mViewModelStore;

if (viewModelStore == null) {

// No one called getViewModelStore(), so see if there was an existing

// ViewModelStore from our last NonConfigurationInstance

NonConfigurationInstances nc =

(NonConfigurationInstances) getLastNonConfigurationInstance();

if (nc != null) {

viewModelStore = nc.viewModelStore;

}

}

if (viewModelStore == null && custom == null) {

return null;

}

//新建 NonConfigurationInstances,将 viewModelStore 保存

NonConfigurationInstances nci = new NonConfigurationInstances();

nci.custom = custom;

nci.viewModelStore = viewModelStore;

return nci;

}

可以看到在该方法中保存了viewModelStore,保存在了NonConfigurationInstances。而该getLastNonConfigurationInstance的真正实现是在 Activity.java 类中

//Activity.java

//静态内部类

static final class NonConfigurationInstances {

Object activity;

HashMap<String, Object> children;

FragmentManagerNonConfig fragments;

ArrayMap<String, LoaderManager> loaders;

VoiceInteractor voiceInteractor;

}

NonConfigurationInstances mLastNonConfigurationInstances;

@Nullable

public Object getLastNonConfigurationInstance() {

return mLastNonConfigurationInstances != null

? mLastNonConfigurationInstances.activity : null;

}

mLastNonConfigurationInstances的赋值是在attach方法中的mLastNonConfigurationInstances = lastNonConfigurationInstances;

final void attach(Context context, ActivityThread aThread,

Instrumentation instr, IBinder token, int ident,

Application application, Intent intent, ActivityInfo info,

CharSequence title, Activity parent, String id,

NonConfigurationInstances lastNonConfigurationInstances,

Configuration config, String referrer, IVoiceInteractor voiceInteractor,

Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {

attachBaseContext(context);

···

mLastNonConfigurationInstances = lastNonConfigurationInstances;

···

为什么ViewModel 在配置变化后依旧存在?

attach中的传入的参数lastNonConfigurationInstancesActivityClientRecord的一个变量,而ActivityClientRecord是存在ActivityThreadmActivities(ArrayMap)中,ActivityThread中的ActivityClientRecord不受activity重建的影响,所以ActivityThread中的lastNonConfigurationInstances同样也不受影响,所以ComponentActiviy中的NonConfigurationInstances也无影响,所以ViewModelStore不受影响,最终ViewModelActivity重新创建后调用getLastNonConfigurationInstance获取得到,这也是ViewModel一直存在的原因

在更早之前版本保存 ViewModel 是使用HolderFragment的,Fragment 中的setRetainInstance(boolean)在设置为 true 时可以是当前的 Fragment 在 Activity 重建时存储下来,所以可以在 Activity 中注入一个 Fragment,这样就可以达到保存 ViewModel 的功能了。详情可以参考这篇文章

Fragment 之间是如何通过 ViewModel 共享数据的?

从上面的分析,我们知道ViewModel存在ActivityViewModelStore中,多个 Fragment 依赖于同一个 Activity,这个时候拿到同一个ViewModel自然就不是问题了。

为什么旋转后所有的 LiveData 会重新执行一次通知

原因很简单,因为 LiveData 的事件是粘性事件,也就是说当 Activity 销毁后,因为 ViewModel 中的 LiveData 并没有销毁(有具体的值),在 Activity 重新创建后,LiveData 会将该值发送给当前 Activity 界面,达到恢复 Activity 界面状态的效果。

ViewModel 如何避免内存泄漏

ComponentActivity的构造器中可以看出,getLifecycle().addObserver添加了一个观察者观察界面是否销毁,一旦销毁,就清空ViewModelStore中的所有ViewModel

public ComponentActivity() {

Lifecycle lifecycle = getLifecycle();

//noinspection ConstantConditions

if (lifecycle == null) {

throw new IllegalStateException("getLifecycle() returned null in ComponentActivity’s "

  • "constructor. Please make sure you are lazily constructing your Lifecycle "

  • "in the first call to getLifecycle() rather than relying on field "

  • “initialization.”);

}

if (Build.VERSION.SDK_INT >= 19) {

getLifecycle().addObserver(new LifecycleEventObserver() {

@Override

public void onStateChanged(@NonNull LifecycleOwner source,

@NonNull Lifecycle.Event event) {

if (event == Lifecycle.Event.ON_STOP) {

Window window = getWindow();

final View decor = window != null ? window.peekDecorView() : null;

if (decor != null) {

decor.cancelPendingInputEvents();

}

}

}

});

}

getLifecycle().addObserver(new LifecycleEventObserver() {

@Override

public void onStateChanged(@NonNull LifecycleOwner source,

@NonNull Lifecycle.Event event) {

if (event == Lifecycle.Event.ON_DESTROY) {

if (!isChangingConfigurations()) {

getViewModelStore().clear(); //界面执行 onDestroy 方法是,清空 viewmodel

}

}

}

});

if (19 <= SDK_INT && SDK_INT <= 23) {

getLifecycle().addObserver(new ImmLeaksCleaner(this));

}

}

ViewModel 和协程一起使用

ViewModel 支持协程,ViewMdoelScope 为应用中的每个ViewModel定义了ViewMdoelScope,如果 ViewModel 已清除,则在此范围内启动的协程都会自动取消。如果您具有仅在 ViewModel 处于活动状态时才需要完成的工作,此时协程非常有用。例如,如果要为布局计算某些数据,则应将工作范围限定至 ViewModel,以便在 ViewModel 清除后,系统会自动取消工作以避免消耗资源。

viewModelScope.launch {

// Coroutine that will be canceled when the ViewModel is cleared.

}

Android Jetpack 的这些架构组件配合起来会非常强大,大大节省开发者的开发时间,同时还能轻松避免内存泄漏,简直就是开发利器。

总结

ViewModel 是个非常好用的 Android Jetpack 组件,可在发生屏幕旋转等配置更改后继续留存,同时还能在不同 Fragment 之间共享,在配合协程使用时也非常方便,还能轻松避免内存泄漏。

需要注意的是,不少开发者会将 ViewModel 实现 LifecycleObserver 接口,把 ViewModel 当做一个能感应生命周期变化的组件,在感知到生命周期的方法中执行loadData之类的操作,然后通过 LiveData 去通知 UI 做出对应的改变,这样的做法丧失了官方为我们设计的 ViewModel 的初衷,反而有点不伦不类了。而且 ViewModel 配合协程也非常方便,也有很多人将网络请求也放到 ViewModel 去调用,个人感觉也是很不合适的,可以参考官方的Demo进行设计。

文末的话


最近火热的Jetpack Compose是谷歌在2019Google i/o大会上发布的新的库,是用于构建原生Android UI的现代工具包。他有强大的工具和直观的Kotlin API,简化并加速了Android上的UI开发。可以帮助开发者用更少更直观的代码创建View,还有更强大的功能,以及还能提高开发速度。

客观地讲,Compose 确实是一套比较难学的东西,因为它毕竟太新也太大了,它是一个完整的、全新的框架,确实让很多人感觉「学不动」,这也是个事实。

如果你是因为缺少学习资料,而我正好薅到这本谷歌内部大佬根据实战编写的《Jetpack Compose最全上手指南》,从入门到精通,教程通俗易懂,实例丰富,既有基础知识,也有进阶技能,能够帮助读者快速入门,是你学习Jetpack Compose的葵花宝典,快收藏起来!!!

由于篇幅原因,如有需要以下完整学习笔记PDF,可以点击我的GitHub免费下载获取!

第一章 初识 Jetpack Compose

1. 为什么我们需要一个新的UI 工具?

2. Jetpack Compose的着重点

  • 加速开发

  • 强大的UI工具

  • 直观的Kotlin API

3. API 设计

4. Compose API 的原则

  • 一切都是函数

  • 顶层函数(Top-level function)

  • 组合优于继承

  • 信任单一来源

5. 深入了解Compose

  • Core

  • Foundation

  • Material

  1. 插槽API

第二章 Jetpack Compose构建Android UI

1. Android Jetpack Compose 最全上手指南

  • Jetpack Compose 环境准备和Hello World

  • 布局

  • 使用Material design 设计

  • Compose 布局实时预览

……

2. 深入详解 Jetpack Compose | 优化 UI 构建

  • Compose 所解决的问题

  • Composable 函数剖析

  • 声明式 UI

  • 组合 vs 继承

  • 封装

  • 重组

……

3. 深入详解 Jetpack Compose | 实现原理

  • @Composable 注解意味着什么?

  • 执行模式

  • Positional Memoization (位置记忆化)

  • 存储参数

  • 重组

……

第三章 Jetpack Compose 项目实战演练(附Demo)

1. Jetpack Compose应用1

  • 开始前的准备

  • 创建DEMO

  • 遇到的问题

2. Jetpack Compose应用2

3. Jetpack Compose应用做一个倒计时器

  • 数据结构

  • 倒计时功能

  • 状态模式

  • Compose 布局

  • 绘制时钟

4. 用Jetpack Compose写一个玩安卓App

  • 准备工作

  • 引入依赖

  • 新建 Activity

  • 创建 Compose

  • PlayTheme

  • 画页面

  • 底部导航栏

  • 管理状态

  • 添加页面

5. 用Compose Android 写一个天气应用

  • 开篇

  • 画页面

  • 画背景

  • 画内容

……

6. 用Compose快速打造一个“电影App”

  • 成品

  • 实现方案

  • 实战

  • 不足

……

由于篇幅原因,如有需要以上完整学习笔记PDF,可以点击我的GitHub免费下载获取!

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

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

我整理了一份阿里P7级别的最系统的Android开发主流技术,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你想深入系统学习Android开发,成为一名合格的高级工程师,可以收藏一下这些Android进阶技术选型

我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

Java语言与原理;
大厂,小厂。Android面试先看你熟不熟悉Java语言

高级UI与自定义view;
自定义view,Android开发的基本功。

性能调优;
数据结构算法,设计模式。都是这里面的关键基础和重点需要熟练的。

NDK开发;
未来的方向,高薪必会。

前沿技术;
组件化,热升级,热修复,框架设计

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多,CodeChina上可见;

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

多花了不少精力),包含知识脉络 + 分支细节。

[外链图片转存中…(img-mosaIczE-1715890694656)]

Java语言与原理;
大厂,小厂。Android面试先看你熟不熟悉Java语言

[外链图片转存中…(img-D3dQIW0z-1715890694657)]

高级UI与自定义view;
自定义view,Android开发的基本功。

[外链图片转存中…(img-AyqIm9qM-1715890694657)]

性能调优;
数据结构算法,设计模式。都是这里面的关键基础和重点需要熟练的。

[外链图片转存中…(img-u7HThbju-1715890694658)]

NDK开发;
未来的方向,高薪必会。

[外链图片转存中…(img-evSaTFRq-1715890694658)]

前沿技术;
组件化,热升级,热修复,框架设计

[外链图片转存中…(img-RyIz0e63-1715890694658)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多,CodeChina上可见;

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值