自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(277)
  • 收藏
  • 关注

原创 5-协作、互斥锁和共享变量

一、协程间的协作和等待1、Java 线程之间等待和协程之间等待的类比协程之间互相等待,有两种方式Job 的 join() 函数如果用 async() 启动的协程,可以用返回的 Deferred 对象的 await() 函数我们还讲到了 Java 的 CountDownLatch 能实现一个线程等待多个线程的场景,在协程类比也可以用 Job 的 join() 和 Channel 来实现类似的效果做到协程间的协作等待。2、select():先到先得。

2024-12-08 02:05:00 926

原创 4.5-Channel 和 Flow:SharedFlow 和 StateFlow

1、数据流的收集和事件订阅的区别Flow 数据流的数据收集相比事件订阅场景更加通用,事件订阅的场景比普通的数据收集要多得多,但并不能简单的说它更有用,而是它更专、更垂直。实际上事件订阅就是一种特殊类型的数据收集,用数据收集的功能是能实现事件订阅的功能,这种事件订阅的 API 在 Flow 也有提供就是 SharedFlow。2、launchIn() 和 shareIn() 的区别。

2024-12-01 22:32:11 1007

原创 4.4-Channel 和 Flow:Flow 的创建、收集和操作符

自定义 Flow 操作符其实就是用一个现成的 Flow 对象来创建另一个 Flow 对象,自定义操作符其实就是一个 Flow 的扩展函数。自定义 Flow 操作符主要有以下步骤定义 Flow 的扩展函数,提供入参类型和返回值类型(具体类型或泛型)在函数体用 flow() 或 channelFlow() 创建一个空的 Flow 对象在函数体调用 collect() 收集上游的数据在 collect() 内调用 emit(),此时就连接了上游和下游基于上面的基础自定义 Flow 处理发送数据到下游。

2024-11-10 10:12:44 1146 1

原创 4.2-Channel 和 Flow:Channel 提供跨协程的事件流及工作模式

一、async() 和 Channel 的类比1、提供一次性结果给其他协程:async()async() 能提供一次性结果给其他协程,它将异步协程的启动和结果的获取拆分开,启动协程用 async(),获取结果用 await()。多次调用 await() 也是只能拿到同样的返回值,因为 async() 是一次性的。2、提供多次不同结果给其他协程:Channel可以把 Channel 理解为支持多条数据的 async()。使用 Channel 只需要三个步骤。

2024-10-28 11:24:55 437 1

原创 4.3-Channel 和 Flow:Flow 的功能定位、工作原理及应用场景

一、Flow 的功能与定位Flow 的功能定位可以将它看成是一个协程版的 Sequence,Sequence 具备惰性机制能做到边生产边消费的能力,但 Sequence 限制了只能自身的挂起函数使用,所以就需要 Flow,Flow 具备 Sequence 的能力并且支持其他挂起函数在 Flow 的使用。Flow 提供了一个支持挂起函数的数据流。二、Flow 的工作原理和应用场景1、Flow 的核心工作原理。

2024-10-27 23:29:17 914

原创 4.1-Channel 和 Flow:Channel 和 Flow 简介与对比

在开发里面最常用的是 StateFlow,StateFlow 提供状态订阅,可以存储状态,当状态的值发生改变的时候就会通知到所有订阅的位置,就能实现界面自动更新之类的自动化操作。StateFlow 内部是用 SharedFlow 实现,SharedFlow 也是一种订阅工具,但它提供的是事件订阅而不是状态订阅。事件订阅和状态订阅的区别事件订阅:在一个事件触发之后再进行事件的订阅,这个事件原则上就不用推送到订阅着那边,当然也可以配置成依然推送状态订阅:在状态更新之后发生的状态订阅,状态肯定会推送到订阅者。

2024-08-27 23:56:24 392 1

原创 3.6-CoroutineScope/CoroutineContext:CoroutineContext 的加减、获取和自定义 CoroutineContext

协程提供了多种 CoroutineContext,比如 ContinuationInterceptor、Job、CoroutineExceptionHandler,如果我们想给协程附加一些其他属性,可能就需要自定义一个新的 CoroutineContext。所谓的自定义 CoroutineContext 指的就是创建一个新的 CoroutineContext 类型。

2024-08-25 01:22:28 689

原创 3.1-CoroutineScope/CoroutineContext:CoroutineScope 和 CoroutineContext 的概念

在使用协程的时候我们会经常跟 CoroutineScope 和 CoroutineContext 打交道,但是对它们的之间有什么区别、概念是什么还是很模糊。在这里就把它们先讲清楚。

2024-08-25 00:11:40 478

原创 3.5-CoroutineScope/CoroutineContext:CoroutineName

CoroutineName 的功能很直白就像名字提到的协程的名称,在使用的时候只需要把它正常的当成一个 CoroutineContext 就行了。

2024-08-25 00:07:11 270

原创 3.4-CoroutineScope/CoroutineContext:coroutineScope() 和 supervisorScope()

coroutineScope() 会创建一个子协程,和使用 launch 创建子协程很像。我们用 coroutineScope() 和 launch 对比并不是想着 [什么时候用 coroutineScope() 替换 launch 使用],只是因为它们内部的工作原理有很大的相似之处,但它们的应用场景是完全不同的。coroutineScope() 和 launch 有以下不同coroutineScope() 不能像 launch 一样定制 CoroutineContext。

2024-08-25 00:05:43 1072

原创 3.3-CoroutineScope/CoroutineContext:从挂起函数里获取 CoroutineContext

在 CoroutineScope 获取 CoroutineContext 很简单,只需要在使用的地方使用 coroutineContext 属性就能拿到。但是如果想在一个挂起函数里拿 coroutineContext,好像就只能作为 CoroutineScope 的扩展函数才能拿到。我们知道挂起函数肯定是运行在协程上的,也就是外面肯定会包一个 CoroutineScope,那么。

2024-08-24 22:35:14 280

原创 3.2-CoroutineScope/CoroutineContext:GlobalScope

没有 Job 的 CoroutineScope 有什么作用呢?没有 Job 说明它创建的协程就没有父协程,确切的说它创建的协程的 Job 就没有父 Job。可以看到 GlobalScope 重写了 coroutineContext,直接返回了 EmptyCoroutineContext。,因为 coroutineContext 就是一个空的上下文,自然也没有 Job。当然我们理解了它的作用和适用场景后,是可以放心的使用它的。

2024-08-24 22:32:10 413 2

原创 2.4-结构化并发:协程的结构化异常管理

try {//输出结果:// 抛出异常上面的代码很简单,用 try-catch 包住协程的执行,想要捕获协程的异常,但是最终发现并没有捕获到。为什么按上面这么写无法捕获到里面的异常呢?try {// 换成了线程,线程内部抛异常,也是没法捕获thread {//我们把协程换成了线程,在线程内部抛异常,外部的 try-catch 也是无法捕获异常的,都在不同的线程了肯定不能捕获到异常。

2024-08-03 01:27:16 1020

原创 2.3-结构化并发:协程的结构化取消

一、线程与协程的交互式取消1、线程的交互式取消:调用 thread.interrupt() 标记线程结束在线程内部配合使用 isInterrupted 判断线程是否已经中断在结束线程前做好收尾清理的工作,最后 return 结束线程一般 isInterrupted 会设置在线程内部执行耗时任务前。2、在线程处理中,有关休眠等待的操作比如 Thread.sleep(),即使在休眠状态也会立即抛出 InterruptedException 异常打断休眠。3、协程的交互式取消。

2024-07-24 18:10:46 1101

原创 2.2-结构化并发:父子协程关系的建立

创建 CoroutineScope 时也会创建 Job 对象,scope.launch 返回的 Job 对象,是 CoroutineScope 创建的 Job 对象的子协程,在 scope.launch 内部调用 this.launch 创建的 Job,是外部 Job 对象的子协程。真正决定父子协程关系的是启动协程时所使用的 Job 对象父协程会等待所有子协程都执行完之后再结束它自己自定义协程关系这个功能一定要慎重使用,不然很容易逻辑链条出现问题导致代码执行错乱。

2024-07-20 23:14:17 813

原创 2.1-结构化并发:[一个协程] 到底指的是什么

一个线程] 指的就是 Thread 对象:Thread 对象实现了对于 [线程] 这个抽象概念的管理,线程指的是 [在程序里单独运行的一条业务线],我们通过 Thread 对象可以完成对这个 [单独运行的业务线] 的管理,Thread 对象承载了线程的管理功能[一个协程] 从不同的视角分析 Job 和 CoroutineScope 都可以是协程对象:从管理协程流程的视角分析,把 Job 对象看成是一个 [协程对象],但是它只包含了跟协程的流程相关的功能;

2024-07-17 01:28:26 114

原创 1.6-协程基础与关键知识:回到线程世界-runBlocking

runBlocking 启动协程不需要 CoroutineScope 是因为它既不需要上下文又不需要被取消runBlocking 的阻塞是线程级别的阻塞,它的定位是把挂起函数的代码转换成阻塞式代码,方便让传统的线程写法 API 使用。

2024-07-14 01:31:42 495

原创 1.5-协程基础与关键知识:连接线程的世界-回调型 API 协作

将线程 API 的回调式代码用 suspendCoroutine 或 suspendCancellableCoroutine 包住,就能实现将回调式代码转换为挂起函数在协程执行,需要调用提供的 continuation.resume 和 continuation.resumeWithException 分别处理正常返回结果和异常的情况。

2024-07-14 00:40:50 924

原创 1.4-协程基础与关键知识:并行协程的启动和交互

启动协程有两个函数:launch 和 async,async 可以并行启动协程,最后通过 await 等待结果返回,常用于多个处理同时执行后合并结果的操作如果你只是希望两个并行流程在顺序上有某种依赖而不依赖结果,可以用 join 函数,可以做到协程之间互相等待。

2024-07-13 22:48:12 519

原创 1.3-协程基础与关键知识:手动切线程-withContext

我们都知道用 suspend 关键字声明一个函数就是挂起函数,那我们什么时候会需要这样自定义一个挂起函数呢?所以自定义挂起函数正确的方式是:我需要一个挂起函数,因为代码需要用到别的挂起函数,所以我需要给函数加上 suspend 关键字声明为挂起函数。声明为挂起函数会限制函数的使用范围,就要遵循 kotlin 的规则来使用。并不存在 [挂起函数怎么写] 这个想法,而是你在写自定义函数的时候,如果里面会用到别的挂起函数,IDE 给我们提示要加上,那就给这个函数加上 supsend;

2024-07-12 00:06:17 417

原创 1.2-协程基础与关键知识:协程自动切回来-挂起函数

协程相比线程最大的优势在于,能够在切线程执行完之后再自动切回来,用同步代码的编写方式实现异步代码;而自动切回来的功能是通过挂起函数实现的挂起函数用 suspend 关键字声明,挂起函数在执行时,它所在的协程就被挂起了,或者说被暂停了;挂起函数挂起的时候并没有暂停,而是切到指定的线程执行在 kotlin 协程被挂起指的是它不再占用它正在工作的这个线程,将这个线程让出,暂时给其他资源使用。

2024-07-10 00:20:39 740

原创 1.1-协程基础与关键知识:切线程 launch

/ 传参指定线程池核心线程数量// 单线程的线程池...// 使用完后要关闭但用上面的方式在 IDE 会有警告提示,因为它被注解为 @DelicateCoroutinesApi,提示直接使用它是容易出错的,使用后要及时关闭t相比提到的三个常用的 CoroutineDispatcher,因为它们是全局的,所以就没有需要关闭的场景,不需要自己手动关闭。

2024-07-06 00:39:18 674

原创 Compose:自定义触摸反馈

Toast.makeText(this, "发生事件: customClick", Toast.LENGTH_SHORT).show()})onClick()在 Compose 触摸事件类型主要有三种:PointerEventType.Press:按下事件,对应的 MotionEvent.ACTION_DOWN,多指的 MotionEvent.ACTION_POINTER_DOWN 也是返回它。

2023-09-29 16:35:24 937

原创 Android 性能优化系列:启动优化进阶

应用的速度优化是我们使用最频繁,也是应用最重要的优化之一,它包括启动速度优化、页面打开速度优化、功能或业务执行速度优化等等,能够直接提升应用的用户体验。大部分人谈到速度优化,只能想到一些零碎的优化点,比如使用多线程、预加载等等,没有一个较为体系的优化方案。那么我们要怎么体系化的学习启动优化呢?能从哪些方面入手?从底层来看,CPU、缓存、任务调度是决定应用速度最本质的因素,CPU 和缓存都属于硬件层,任务调度机制则属于操作系统层。速度优化我们将围绕这三个因素详细说明优化方案。所有的程序最终会被编译成机器码指令

2023-07-09 14:33:36 3311 1

原创 Compose:状态更新

我们先上一段代码,你觉得下面代码最终执行结果是什么:Text() 最终显示的结果还是 name,并没有在 3s 后更新为设置的值。为什么会这样呢?实际上上面的代码经过编译器编译后,代码类似是这样的:被通知刷新的 Text(name) 会被处理为例如被一个 WrapperFunction 包裹起来标记,在后续 name 更新的时候就会执行 WrapperFunction 代码块中的代码;因为 name 也被包裹在 WrapperFunction,所以 name 已经不是原先的那个 name,而是一个新的

2023-07-02 16:13:13 1047

原创 Android 性能优化系列:崩溃原因及捕获

java crash 由 JVM 触发处理,最终走到 /data/system/dropbox 目录用文件保存native crash 由管道通信建立 socket 接收通知,最终走到 /data/system/dropbox 目录用文件保存anr 由多种情况(事件、前后台服务)触发器处理,最终走到 /data/system/dropbox 目录用文件保存所有的 crash 处理在 Android 系统内部都会将对应的数据收集到 /data/system/dropbox 目录下。

2023-06-27 16:39:32 2746 1

原创 Compose:自定义布局

当我们讲到原生的自定义布局,一般是指的自己实现一个 View 或者 ViewGroup 的子类,可能会重写 onMeasure() 和 onLayout() 达到我们想要的效果。但是在 Compose 所有的组件都是用纯 kotlin 代码,在定义自定义布局时,如果是单纯的摆放 Composable,可以认为是不算的:Compose 能通过 LayoutModifier 定制一个组件的测量布局效果:如果要自定义可以管控子组件的 Composable,Compose 提供了一个 Layout() 的 Co

2023-05-03 17:52:57 626 2

原创 Compose:自定义绘制

Compose 的绘制底层其实还是使用的 Canvas,不过相比原生的自定义绘制,Compose 提供了更上层的 API,这套 API 能让我们更简单更直接实现和原生一样的效果。并对其进行了详细的分析,所有的绘制步骤包括组件自身的绘制、手写的自定义绘制都是在 DrawModifier。我们可以用 DrawModifier 实现不同的绘制顺序。上面是一个文本,然后使用 Modifier.drawBehind() 绘制黄色的背景。可以发现使用 Compose 绘制和原生不同的是,

2023-04-29 13:12:02 431

原创 换肤实现及LayoutInflater原理

不知道你在接到项目需求需要实现换肤功能时,有没有想过到底为什么需要换肤?虽然这是一篇技术文章,但我们不妨从产品和运营的角度想一想,实现换肤它究竟有什么样的价值?在 Android 10 从系统层面已经为我们提供了深色主题(也就是夜间模式),它也可以认为是换肤的一种,官方文档对深色主题列举了以下优势:可大幅减少耗电量(具体取决于设备的屏幕技术)为弱视以及对强光敏感的用户提高可视性让所有人都可以在光线较暗的环境中更轻松地使用设备系统提供给我们只有日间和夜间模式,从用户的角度它满足了在日间和夜间两种场景下更好的使

2023-04-26 14:02:00 1134

原创 Okio 网络提速

Okio 简单来说就是一个对原生 IO 封装的三方库,Okio 最重要的是对缓冲区的设计。Okio 读写效率高是因为动态的缓冲区构建,缓冲区以 Segment 作为读写单位,每 8k 数据分配一个 Segment,最多 8 个 Segment 保证了至少在 64k 数据内只有一次数据拷贝一次上下文切换,减少用户空间和内核空间的交互次数,以达到在常规读写业务达到网络读写提速的目的。

2023-04-12 12:54:49 1001

原创 Jetpack:DataBinding

该篇文章从布局绑定、注册监听、数据驱动 UI 更新各个流程都做了详细的源码分析,根据上面的内容我们再做对各流程做一个总结梳理。通过 Activity 的 setContentView() 加载布局根据布局根节点的 tag 属性 layout/activity_xxx_0 创建对应布局的 ViewDataBindingViewDataBinding 创建时遍历布局所有的 View 存到数组中,数组的 View 赋值给 ViewDataBinding 的控件变量。

2023-03-21 18:08:35 413 2

原创 Compose:附带效应 SideEffect 和 Compose 中的协程

DisposableEffect 的使用场景比如界面埋点,还有设置进入界面时加上全局监听,退出界面时移除全局监听等等,具体还是看业务场景。SideEffect 从字面意思就是附带效应的意思,这里的附带效应指的是额外的作用。再具体到 Compose,,就是 SideEffect 的加强版。在 Compose 中。

2023-03-19 15:05:44 737 1

原创 Compose:ParentDataModifier

ParentDataModifier 的底层存储方式和 DrawModifier 以及 PointerInputModifier 相同,都是存储在链表中,整个链表在指定的数组索引位置:因为 Compose 是一个纯 UI 的框架,无论是 DrawModifier、PointerInputModifier 两者之间都没有关联,同理 ParentDataModifier,不过 ParentDataModifier 是和测量布局有一点关联用于辅助的 Modifier。我们在 LinearLayout 设置某个控

2023-03-15 23:29:51 104

原创 Compose:其他的 Modifier

OnRemeasuredModifier 和 OnPlacedModifier 分别对应的 onMeasure() 和 onLayout() 测量布局过程,也就是说它们是分开的,先执行测量然后再有布局,在 OnPlacedModifier 虽然也能拿到测量结果,但 OnRemeasuredModifier 会比 OnPlacedModifier 先回调测量结果。可以看到在 talkback 模式下文本也是可以被点击响应到,此时点击方块是没反应的,因为在无障碍场景点击它没有意义。

2023-03-15 23:29:40 464

原创 Compose:DrawModifier

这次 Modifier.drawWithContent() 后面有一个 background() 的 DrawModifier,background() 是 Modifier.drawWithContent() 原有的内部内容,在调用 Modifier.drawWithContent() 但没有填写任何内容,这将会擦除 background()。,如果是添加了 Modifier.background(),那么就是调用 background() 这个 DrawModifier 的 draw() 绘制自身。

2023-03-15 23:26:54 160

原创 Compose:PointerInputModifier

PointerInputModifier 是用来做触摸反馈的 Modifier,但实际上命名为 PointerInput 比 Touch 更合适,因为 PointerInput 还代指包含鼠标等输入反馈处理,只是在 Android 基本都是触摸事件。在讲 PointerInputModifier 之前,我们先了解下 Compose 的点击要怎么处理。

2023-03-15 23:26:46 633

原创 Compose:LayoutModifier

虽然提到测量和布局分别在 remeasure() 和 replace() 分别处理,按上面的源码分析,

2023-03-15 23:23:18 238

原创 设计原则与设计模式概览

创建型模式的核心:让你们知道怎么去 new 对象。让你考虑内存性能和扩展性,以及后续代码维护的工作量。再简单梳理下创建型模式的使用场景:单例模式:适用于类只允许创建一个对象(一般是进程内只有一个对象),被创建的类一般不需要太多的代码改动,并且不依赖外部系统时简单工厂:适用于代码不复杂、类型不多改动较少的对象创建场景工厂方法:对象创建条件比较复杂,要组合其他类对象和判断条件,做各种初始化操作的时候。

2023-02-17 10:24:16 1096

原创 Android 组件化与路由跳转实现

路由是在 Android 组件化开发中必不可少的组件,两个独立的模块不能有直接引用,界面的跳转都经过路由中转实现。在讲路由之前,我们需要理解一个概念:什么是路由?路由本来是网络的概念,而放在 Android 开发中,我们可以将项目的每个 module 看成不同的网络,路由就是连接各个 module 的中转站,这个中转站可以对页面跳转的参数等统一处理后再进行转发。或许你会有这样的疑惑:隐式跳转也能实现跨 module,也可以实现相应的功能;还有也可以用反射来实现,为什么要用路由?

2023-01-09 20:16:15 1729 1

原创 TCP 协议深度剖析

关于为什么是三次握手不能两次握手,网上很多文章提到说是消耗资源,

2023-01-01 16:32:30 438

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除