阿里P7大佬教你破解 Kotlin 协程(5) - 协程取消篇

[译] 2019 年的 Android 网络 —— Retrofit 与 Kotlin 协程 //Kotlin Coroutines implementation “org.jetbrains.kotlinx:kotlinx-coroutines-android:kotlinCoroutineVersion"implementation"org.jetbrains.kotlinx:kotlinx−coroutines−core:kotlinCoroutineVersion" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:k 阅读详情

破解 Kotlin 协程(5) - 协程取消篇

在这里插入图片描述

关键词:Kotlin 协程 协程取消 任务停止

协程的任务的取消需要靠协程内部调用的协作支持,这就类似于我们线程中断以及对中断状态的响应一样。

1. 线程的中断

我们先从大家熟悉的话题讲起。线程有一个被废弃的 stop 方法,这个方法会让线程立即死掉,并且释放它持有的锁,这样会让它正在读写的存储处于一个不安全的状态,因此 stop 被废弃了。如果我们启动了一个线程并让它执行一些任务,但很快我们就后悔了, stop 还不让用,那该怎么办?

val thread = thread {
    ...
}
thread.stop() // !!! Deprecated!!!

我们应该想办法让线程内部正在运行的任务跟我们合作把任务停掉,这样线程内部的任务停止之前还有机会清理一些资源,比如关闭流等等。

val thread = thread {
    try {
        Thread.sleep(10000)
    } catch (e: InterruptedException) {
        log("Interrupted, do cleaning stuff.")
    }
}
thread.interrupt()

像 sleep 这样的方法调用,文档明确指出它支持 InterruptedException,因此当线程被标记为中断状态时,它就会抛出 InterruptedException ,那么我们自然就可以捕获异常并做资源清理了。

所以请注意所谓的协作式的任务终止,协程的取消也就是 cancel 机制的思路也是如此。

2. 协程类似的例子

我们来看一个协程取消的例子:

fun main() = runBlocking {
    val job1 = launch { // ①
        log(1)
        delay(1000) // ②
        log(2)
    }
    delay(100)
    log(3)
    job1.cancel() // ③
    log(4)
}

这次我们用了一个不一样的写法,我们没有用 suspend main,而是直接用 runBlocking 启动协程,这个方法在 Native 上也存在,都是基于当前线程启动一个类似于 Android 的 Looper 的死循环,或者叫消息队列,可以不断的发送消息给它进行处理。 runBlocking 会启动一个 Job,因此这里也存在默认的作用域,不过这对于我们今天的讨论暂时没有太大影响。

这段代码 ① 处启动了一个子协程,它内部先输出 1,接着开始 delay, delay 与线程的 sleep 不同,它不会阻塞线程,你可以认为它实际上就是触发了一个延时任务,告诉协程调度系统 1000ms 之后再来执行后面的这段代码(也就是 log(2));而在这期间,我们在 ③ 处对刚才启动的协程触发了取消,因此在 ② 处的 delay还没有回调的时候协程就被取消了,因为 delay 可以响应取消,因此 delay 后面的代码就不会再次调度了,不调度的原因也很简单,② 处的 delay 会抛一个 CancellationException:

...
log(1)
try {
    delay(1000)
} catch (e: Exception) {
    log("cancelled. $e")
}
log(2)
...

那么输出的结果就不一样了:

06:54:56:361 [main] 1
06:54:56:408 [main] 3
06:54:56:411 [main] 4
06:54:56:413 [main] cancelled. kotlinx.coroutines.JobCancellationException: Job was cancelled; job=StandaloneCoroutine{Cancelling}@e73f9ac
06:54:56:413 [main] 2

大家看,这与线程的中断逻辑是不是非常的类似呢?

3. 完善我们之前的例子

之前我们有个例子,上一篇文章已经加入了异常处理逻辑,那么这次我们给它加上取消逻辑。之前是这样:

suspend fun getUserCoroutine() = suspendCoroutine<User> { continuation ->
    getUser(object : Callback<User> {
        override fun onSuccess(value: User) {
            continuation.resume(value)
        }

        override fun onError(t: Throwable) {
            continuation.resumeWithException(t)
        }
    })
}

加取消逻辑,那需要我们的 getUser 回调版本支持取消,我们看下我们的 getUser 是怎么实现的:

fun getUser(callback: Callback<User>) {
    val call = OkHttpClient().newCall(
            Request.Builder()
                    .get().url("https://api.github.com/users/bennyhuo")
                    .build())

    call.enqueue(object : okhttp3.Callback {
        override fun onFailure(call: Call, e: IOException) {
            callback.onError(e)
        }

        override fun onResponse(call: Call, response: Response) {
            response.body()?.let {
                try {
                    callback.onSuccess(User.from(it.string()))
                } catch (e: Exception) {
                    callback.onError(e) // 这里可能是解析异常
                }
            }?: callback.onError(NullPointerException("ResponseBody is null."))
        }
    })
}

我们发了个网络请求给 Github,让它把一个叫 bennyhuo 的用户信息返回来,我们知道 OkHttp 的这个 Call是支持 cancel 的, 取消后,网络请求过程中如果读取到这个取消的状态,就会把请求给停止掉。既然这样,我们干脆直接改造 getUser 好了,这样还能省掉我们自己的 Callback 回调过程:

suspend fun getUserCoroutine() = suspendCancellableCoroutine<User> { continuation ->
    val call = OkHttpClient().newCall(...)

    continuation.invokeOnCancellation { // ①
        log("invokeOnCancellation: cancel the request.")
        call.cancel()
    }

    call.enqueue(object : okhttp3.Callback {
        override fun onFailure(call: Call, e: IOException) {
            log("onFailure: $e")
            continuation.resumeWithException(e)
        }

        override fun onResponse(call: Call, response: Response) {
            log("onResponse: ${response.code()}")
            response.body()?.let {
                try {
                    continuation.resume(User.from(it.string()))
                } catch (e: Exception) {
                    continuation.resumeWithException(e)
                }
            } ?: continuation.resumeWithException(NullPointerException("ResponseBody is null."))
        }
    })
}

我们这里用到了 suspendCancellableCoroutine,而不是之前的 suspendCoroutine,这就是为了让我们的挂起函数支持协程的取消。该方法将获取到的 Continuation 包装成了一个 CancellableContinuation,通过调用它的 invokeOnCancellation 方法可以设置一个取消事件的回调,一旦这个回调被调用,那么意味着 getUserCoroutine 调用所在的协程被取消了,这时候我们也要相应的做出取消的响应,也就是把 OkHttp 发出去的请求给取消掉。

那么我们在调用它的时候,如果遇到了取消,会怎么样呢?

val job1 = launch { //①
    log(1)
    val user = getUserCoroutine()
    log(user)
    log(2)
}
delay(10)
log(3)
job1.cancel()
log(4)

注意我们启动 ① 之后仅仅延迟了 10ms 就取消了它,网络请求的速度一般来讲还不会这么快,因此取消的时候大概率 getUserCoroutine 被挂起了,因此结果大概率是:

07:31:30:751 [main] 1
07:31:31:120 [main] 3
07:31:31:124 [main] invokeOnCancellation: cancel the request.
07:31:31:129 [main] 4
07:31:31:131 [OkHttp https://api.github.com/...] onFailure: java.io.IOException: Canceled

我们发现,取消的回调被调用了,OkHttp 在收到我们的取消指令之后,也确实停止了网络请求,并且回调给我们一个 IO 异常,这时候我们的协程已经被取消,在处于取消状态的协程上调用 Continuation.resume 、 Continuation.resumeWithException 或者 Continuation.resumeWith 都会被忽略,因此 OkHttp 回调中我们收到 IO 异常后调用的 continuation.resumeWithException(e) 不会有任何副作用。

4. 再谈 Retrofit 的协程扩展

4.1 Jake Wharton 的 Adapter 存在的问题

我在破解 Kotlin 协程 - 入门篇(https://www.bennyhuo.com/2019/04/01/basic-coroutines/) 提到了 Jake Wharton 大神为 Retrofit 写的 协程 Adapter,

implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'

它确实可以完成网络请求,不过有细心的小伙伴发现了它的问题:它怎么取消呢?我们把使用它的代码贴出来:

interface GitHubServiceApi {
    @GET("users/{login}")
    fun getUserCoroutine(@Path("login") login: String): Deferred<User>
}

定义好接口,创建 Retrofit 实例的时候传入对应的 Adapter:

val gitHubServiceApi by lazy {
    val retrofit = retrofit2.Retrofit.Builder()
            .baseUrl("https://api.github.com")
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(CoroutineCallAdapterFactory()) // 这里添加 Adapter
            .build()

    retrofit.create(GitHubServiceApi::class.java)
}

用的时候就这样:

val deferred = gitHubServiceApi.getUserCoroutine("bennyhuo")
try {
    showUser(deferred.await())
} catch (e: Exception) {
    showError(e)
}

如果要取消,我们可以直接调用 deferred.cancel(),例如:

log("1")
val deferred = gitHubServiceApi.getUserCoroutine("bennyhuo")
log("2")
withContext(Dispatchers.IO){
    deferred.cancel()
}
try {
    showUser(deferred.await())
} catch (e: Exception) {
    showError(e)
}

运行结果如下:

12:59:54:185 [DefaultDispatcher-worker-1] 1
12:59:54:587 [DefaultDispatcher-worker-1] 2
kotlinx.coroutines.JobCancellationException: Job was cancelled; job=CompletableDeferredImpl{Cancelled}@36699211

这种情况下,其实网络请求确实是被取消的,这一点我们可以看下源码的处理:

...
override fun adapt(call: Call<T>): Deferred<T> {
      val deferred = CompletableDeferred<T>()

      deferred.invokeOnCompletion { // ①
        if (deferred.isCancelled) {
          call.cancel()
        }
      }

      call.enqueue(object : Callback<T> {
        ...
      }     
}
...

注意 ① 处, invokeOnCompletion 在协程进入完成状态时触发,包括异常和正常完成,那么在这时候如果发现它的状态是已经取消的,那么结果就直接调用 Call 的取消即可。

这看上去确实很正常啊~ 不过 @阿永 在公众号的评论里面提到了一个 Case,仔细一看还真是有问题。我们给出示例来复现这个 Case:

val job = GlobalScope.launch {
    log("1")
    val deferred = gitHubServiceApi.getUserCoroutine("bennyhuo")
    log("2")
    deferred.invokeOnCompletion {
        log("invokeOnCompletion, $it, ${deferred.isCancelled}")
    }
    try {
        showUser(deferred.await())
    } catch (e: Exception) {
        showError(e)
    }
    log(3)
}
delay(10)
job.cancelAndJoin()

我们启动一个协程,在其中执行网络请求,那么正常来说,这时候 getUserCoroutine 返回的 Deferred 可以当做一个子协程,它应当遵循默认的作用域规则,在父作用域取消时被取消掉,但现实却并不是这样:

13:06:54:332 [DefaultDispatcher-worker-1] 1
13:06:54:829 [DefaultDispatcher-worker-1] 2
kotlinx.coroutines.JobCancellationException: Job was cancelled; job=StandaloneCoroutine{Cancelling}@19aea38c
13:06:54:846 [DefaultDispatcher-worker-1] 3
13:06:56:937 [OkHttp https://api.github.com/...] invokeOnCompletion, null, false

我们看到在调用 deferred.await() 的时候抛了个取消异常,这主要是因为 await() 所在的协程已经被我们用 cancelAndJoin() 取消,但从随后 invokeOnCompletion 的回调结果来看, getUserCoroutine 返回的 Deferred 并没有被取消,再仔细一看,时间上这个回调比前面的操作晚了 2s,那必然是网络请求返回之后才回调的。

所以问题究竟在哪里?在 CoroutineCallAdapterFactory 的实现中,为了实现异步转换,手动创建了一个 CompletableDeferred:

override fun adapt(call: Call<T>): Deferred<T> {
  val deferred = CompletableDeferred<T>() // ①
  ...
}

这个 CompletableDeferred 本身就是一个 Job 的实现,它的构造可接受一个 Job 实例作为它的父协程,那么问题来了,这里并没有告诉它父协程究竟是谁,因此也就谈不上作用域的事儿了,这好像我们用 GlobalScope.launch 启动了一个协程一样。如果大家在 Android 当中使用 MainScope,那么同样因为前面说到的这个原因,导致 CompletableDeferred 没有办法被取消。

@阿永 在公众号评论中提到这个问题,并提到了一个比较好的解决方案,下面我们为大家详细介绍。感谢 @阿永。

说到这里我们再简单回顾下,作用域主要有 GlobalScope、 coroutineScope、 supervisorScope,对于取消,除了 supervisorScope 比较特别是单向取消,即父协程取消后子协程都取消,Android 中 MainScope就是一个调度到 UI 线程的 supervisorScope; coroutineScope 的逻辑则是父子相互取消的逻辑;而 GlobalScope 会启动一个全新的作用域,与它外部隔离,内部遵循默认的协程作用域规则。

那么有没有办法解决这个问题呢?

直接解决还是比较困难的,因为 CompletableDeferred 构造所处的调用环境不是 suspend 函数,因而也没有办法拿到(很可能根本就没有!)父协程。

4.2 如何正确的将回调转换为协程

前面我们提到既然 adapt 方法不是 suspend 方法,那么我们是不是应该在其他位置创建协程呢?

其实我们前面在讲 getUserCoroutine 的时候就不断为大家展示了如何将一个回调转换为协程调用的方法:

suspend fun getUserCoroutine() = suspendCancellableCoroutine<User> { continuation ->
    ...
}

suspendCancellableCoroutine 跟最初我们提到的 suspendCoroutine 一样,都是要获取当前协程的 Continuation 实例,这实际上就相当于要继承当前协程的上下文,因此我们只需要在真正需要切换协程的时候再去做这个转换即可:

public suspend fun <T : Any> Call<T>.await(): T {
    return suspendCancellableCoroutine { continuation ->
        enqueue(object : Callback<T> {
            override fun onResponse(call: Call<T>?, response: Response<T?>) {
                continuation.resumeWith(runCatching { // ①
                    if (response.isSuccessful) {
                        response.body()
                            ?: throw NullPointerException("Response body is null: $response")
                    } else {
                        throw HttpException(response)
                    }
                })
            }

            override fun onFailure(call: Call<T>, t: Throwable) {
                if (continuation.isCancelled) return // ②
                continuation.resumeWithException(t)
            }
        })

        continuation.invokeOnCancellation {
            try {
                cancel()
            } catch (ex: Throwable) {  // ③
                //Ignore cancel exception 
            }
        }
    }
}

大家看着这段代码会不会很眼熟?这与我们 getUserCoroutine 的写法几乎如出一辙,不过有几处细节值得关注,我用数字标注了他们的位置:

  • ① 处 runCatching 可以将一段代码的运行结果或者抛出的异常封装到一个 Result 类型当中,Kotlin 1.3 开始新增了Continuation.resumeWith(Result) 这个方法, 这个点比起我们前面的写法更具 Kotlin 风格。
  • ② 处在异常抛出时,判断了是否已经被取消。实际上如果网络请求被取消,这个回调确实会被调到,那么由于取消的操作是协程的由 Continuation 的取消发起的,因此这时候没必要再调用 continuation.resumeWithException(t) 来将异常再抛回来了。尽管我们前面其实也提到过,这时候继续调用 continuation.resumeWithException(t) 也没有任何逻辑上的副作用,但性能上多少还是会有一些开销。
  • ③ 处,尽管 Call.cancel 的调用比较安全,但网络环境和状态难免情况复杂,因此对异常进行捕获会让这段代码更加健壮。如果 cancel 抛异常而没有捕获的话,那么等同于协程体内部抛出异常,具体如何传播看所在作用域的相关定义了。

需要指出的是,这段代码片段源自 gildor/kotlin-coroutines-retrofit(https://github.com/gildor/kotlin-coroutines-retrofit) ,大家也可以直接添加依赖进行使用:

compile 'ru.gildor.coroutines:kotlin-coroutines-retrofit:1.1.0'

这个框架代码量很少,但经过各路 Kotlin 协程专家的锤炼,逻辑手法很细腻,值得大家学习。

5. 小结
Kotlin 协程学习资料可以扫描下方二维码免费领取!

# 《**史上最详Android版kotlin协程入门进阶实战** **》**

第一章 Kotlin协程的基础介绍

​ ● 协程是什么

​ ● 什么是Job 、Deferred 、协程作用域

​ ● Kotlin协程的基础用法

img

第二章 kotlin协程的关键知识点初步讲解

​ ● 协程调度器

​ ● 协程上下文

​ ● 协程启动模式

​ ● 协程作用域

​ ● 挂起函数

img

第三章 kotlin协程的异常处理

​ ● 协程异常的产生流程

​ ● 协程的异常处理

img

第四章 kotlin协程在Android中的基础应用

​ ● Android使用kotlin协程

​ ● 在Activity与Framgent中使用协程

​ ● ViewModel中使用协程

​ ● 其他环境下使用协程

img

第五章 kotlin协程的网络请求封装

​ ● 协程的常用环境

​ ● 协程在网络请求下的封装及使用

​ ● 高阶函数方式

​ ● 多状态函数返回值方式

Kotlin 协程系列二:Coroutine取消和超时 本系列为翻译和解读 Kotlin 协程的官方文档,对应官方文档版本1.5.3 时间是2021-10 约定:全部的正文均对应文档原文,个人解读以引用的形式插入 官方文档链接 https://kotlinlang.org/docs/coroutines-guide.html 如果对协程完全没任何概念,强烈推荐先看这篇文章 https://xie.infoq.cn/article/351ddc94200d03948c41fbabd 如果你想写代码实操,可以参考这个配置环境 https://openxu.blog. 阅读详情

相关推荐

安卓原生开发为何是初创企业的生存基础设施

安卓原生开发是移动应用开发的基础技术路径,指基于Android SDK、直接调用系统API构建高性能、高可控性客户端的能力。其核心原理在于绕过Web容器或跨端中间层,实现对硬件传感器、后台服务、离线存储及厂商推送等底层能力的稳定访问。相比H5、小程序或跨平台方案,原生开发在弱网适配、消息送达率、启动性能和隐私合规等方面具备不可替代的技术价值。典型应用场景包括社区团购实时订单同步、智能硬件蓝牙联动、车载Android Auto集成、金融类App安全保活等强交互、低延迟、高可靠性需求领域。对于资源有限的初创企业

weixin_30776545的博客 452

Retrofit+Kotlin Coroutine重构Android网络框架

前言 Kotlin的Coroutine 可以使异步编程的代码更优雅,可以去除传统的异步编程中的Callback,使得代码看起来就是它想表达的直观的逻辑。使用异步编程最多的地方就是网络请求,所以如果能将Coroutine与Retrofit结合起来,去除Retrofit中的Callback。那么我们的网络请求的代码将会变的非常的易读。 Kotlin Coroutine 简介 Kotlin coroutines let you convert callback-based code to sequential

taotao110120119的专栏 2931

StarUML官方安装与工程化建模全指南:UML 2.5+DDD实战

UML(统一建模语言)是软件工程中用于可视化、构造和文档化系统结构与行为的基础建模标准;其核心原理在于通过类图、时序图、组合关系等图形化元素,精准表达对象间语义约束与生命周期依赖。StarUML作为深度支持UML 2.5规范的开源建模工具,凭借模型即代码、双向工程同步、强语义校验等技术价值,已成为DDD领域驱动设计、微服务架构治理与Clean Architecture落地的关键基础设施。它广泛应用于高校课程设计、企业级系统建模、API契约生成及团队协作文档化等场景,尤其在实体类图标、单向导航组合、服务订阅交

aiqixiao2017的博客 560

Kotlin 协程 - 协程取消篇

协程的任务的取消需要靠协程内部调用的协作支持,这就类似于我们线程中断以及对中断状态的响应一样。 1. 线程的中断 我们先从大家熟悉的话题讲起。线程有一个被废弃的 stop 方法,这个方法会让线程立即死掉,并且释放它持有的锁,这样会让它正在读写的存储处于一个不安全的状态,因此 stop 被废弃了。如果我们启动了一个线程并让它执行一些任务,但很快我们就后悔了,stop 还不让用,那该怎么办? val thread = thread { ... } thread.stop() // !!! Depre.

pureChilder的博客 2876

关于动态存储器dram中rank,bank,device之间的关系

DEVICE_WIDTH表示设备的带宽(单位bit),每个列(column)包含DEVICE_WIDTH 位(bit),每一行(row)包含NUM_COLS个column。   而每一个bank包含NUM_ROWS个行(row)。所以一个设备(device)的存储容量为PER_DEVICE_STORAGE =NUM_ROWS*NUM_COLS*DEVICE_WIDTH*NUM_BANKS (i

wyj7260的专栏 4733

3D Slicer5.7在win11下的源码编译安装

本博客主要参考官方安装文档以及部分优快云的博客,部分内容的书写会直接参考官方文档的内容。

Topsort的博客 4761

[译] 2024 年的 Android 网络 —— Retrofit 与 Kotlin 协程

找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。多练。(视频优势是互动感强,容易集中注意力)你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

2301_76268112的博客 1184

基于YOLOv11的工业装配完整性智能检测系统

在现代工业生产中,产品质量控制是确保企业竞争力的关键环节。其中,装配完整性检查——即检测产品是否安装了所有应有的部件,是质量检测中的重要一环。传统的人工检测方法存在效率低、易疲劳、主观性强等问题。本文提出了一种基于深度学习的目标检测算法YOLOv11的工业装配完整性智能检测系统,通过计算机视觉技术自动识别和验证产品装配的完整性。本文将详细阐述从数据准备、模型训练到系统部署的完整流程,提供可执行的代码实现,并介绍一个适用于该任务的参考数据集。

YOLO 553

阿里P7大佬教你破解 Kotlin 协程(7) - 序列生成器篇

​ ● 协程是什么​ ● 什么是Job 、Deferred 、协程作用域​ ● Kotlin协程的基础用法。

Android_XG的博客 211

群晖、飞牛NAS容器进入终端失败?一步步教你解决Docker Bash错误!(OCI runtime exec failed: exec failed)

群晖、飞牛NAS容器进入终端失败?一步步教你解决Docker Bash错误!(OCI runtime exec failed: exec failed)

XiaoqiangClub的博客 2998

《UDS协议从入门到精通》系列——图解0x85:控制DTC的设置

如何让ECU停止或恢复DTC状态位更新?有哪些注意点?图解+示例教你快速上手UDS 0x85服务!!

车载系统攻城狮 3864

阿里P7亲自讲解!Android开发者值得深入思考的几个问题,使用指南

O(∩_∩)O~今天又给大家带来了21道Android高级面试题,搞懂了这些相信你去面试的时候一定可以镇住面试官 为此我把这些知识整理成了一个983页PDF,从基础到进阶。含有BATJ.字节跳动面试专题,算法专题,高端技术专题,混合开发专题,java面试专题,Android,Java小知识,到性能优化线程.View.OpenCV.NDK等应有尽有。还有辅之相关的视频+学习笔记 Java相关 容器(HashMap、HashSet、LinkedList、ArrayList、数组等) 内存模型 .

m0_57081438的博客 154

【YOLO系列】YOLOv12详解:模型结构、损失函数、训练方法及代码实现

本文提出YOLOv12,一种以注意力为中心的实时目标检测框架,通过创新性改进解决传统YOLO模型在精度与速度上的失衡问题。核心贡献包括:1)轻量混合注意力模块(LHA)结合通道与空间注意力,在不增加计算负担下提升特征提取能力;2)区域注意机制与R-ELAN网络优化特征聚合,显著改善小目标检测(AP_S提升8.3%);3)动态多光谱数据增强策略增强模型鲁棒性。实验表明,YOLOv12在COCO等基准上实现SOTA性能,同时保持2.1 TOPS/W的高能效比,适用于边缘设备部署。该工作为实时目标检测提供了新的注

一碗白开水一 1808

v1.4 发布在即,诞生近 10 年的 Kotlin 取代 Java 了吗?

热文推荐:尘埃落定!清华才子王垠加入华为职级22,前阿里P10赵海平加入字节跳动,职级或为4+百度网盘“破解版”,Pandownload开发者被抓Kotlin 诞生于 2010 年,出自...

Java和Android架构 927

RSA算法详解:万字文章详解RSA的加密与解密

为了彻彻底底的研究明白RSA算法,真的是。。煞(lei)费(si)苦(wo)心(le),首先我希望自己前前后后、彻彻底底的琢磨明白,其次我也希望我把他分享出来以后,大家都能读得懂,这篇文章真的是我迄今为止写的又臭又长的一篇文章了,但是我能保证的是,你读完了以后,绝对不会再问RSA是啥玩意儿了。。

布啦啦 7668

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Android开发工程师技术栈与面试全解析

Android开发作为移动互联网的核心技术领域,其技术栈涵盖从语言特性到系统架构的多层次知识。Kotlin已成为主流开发语言,但Java的JVM原理和并发模型仍是性能优化的关键基础。现代Android开发强调声明式UI(如Jetpack Compose)与响应式架构(如MVVM),同时需要掌握内存管理、线程优化等底层机制。在工程实践层面,模块化设计、CI/CD流水线和性能监控体系构建是高级工程师的核心能力。大厂面试往往深度考察Handler机制、自定义View优化等系统原理,以及解决复杂工程问题的实战经验。

weixin_32667955的博客 321

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

面试大厂屡屡被拒,感觉五年青春打了水漂!现在Android开发到底需要会些什么……

需要注意 onMeasure,onLayout,onDraw 的调用时机,以及 Touch 事件分发机制。

2301_76348344的博客 709

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

2024Android开发社招面试总结,2024阿里巴巴安卓面试真题解析

动画的种类:前者只有透明度,旋转,平移,伸缩4种属性,而对于后者,只要是该控件的属性,且有setter该属性的方法就都可以对该属性执行一种动态变化的效果。可操作的对象:前者只能对UI组件执行动画,但属性动画几乎可以对任何对象执行动画(不管它是否显示在屏幕上)。动画播放顺序。

2401_84874163的博客 462

android开发基础作用,阿里巴巴发布“限量版”Android零基础宝典

动画的种类:前者只有透明度,旋转,平移,伸缩4种属性,而对于后者,只要是该控件的属性,且有setter该属性的方法就都可以对该属性执行一种动态变化的效果。可操作的对象:前者只能对UI组件执行动画,但属性动画几乎可以对任何对象执行动画(不管它是否显示在屏幕上)。动画播放顺序。

2401_84408780的博客 926

哭了,腾讯两面挂,复习三月终拿offer,Java基础+网络+性能优化等面试题干货整理

因为最近还有高很多朋友都在准备面试,所以,我这边也整理了非常多的面试专题资料,还有百度、阿里、腾讯、字节跳动等大厂的面经。希望可以帮助到大家。上面的面试题答案都整理成了文档笔记。另外我们还整理了一些面试资料&最新2020收集的一些大厂的面试真题(都整理成文档还有配套的视频),有需要的可以点击这里:https://github.com/Android,进圈暗号:简书粉丝,免费领取。写在最后为了这次面试,也收集了很多的面试题!以下是部分面试题截图。

2401_84439874的博客 997

Google 再见 Java

热文推荐:尘埃落定!清华才子王垠加入华为职级22,前阿里P10赵海平加入字节跳动,职级或为4+百度网盘“破解版”,Pandownload开发者被抓去年 5 月份的 Google I/O ...

Java和Android架构 241

爬虫程序优化要点_爬虫程序的改进措施有哪些内容(1),网络安全快速转战Kotlin教程

Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,不要看完;在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。分析目标服务器的爬虫策略,控制爬虫请求频率甚至包括验证码、加密数据的破解,同时使用优质代理或爬虫代理,寻找业务独享、网络稳定、高并发、低延迟的代理产品,确保目标服务器没法进行反爬限制及预警,

qd520_1314的博客 978

Android工程师必备知识,2024Android面试,2024年最新android系统开发面试

现在新技术层出不穷,如果每次出新的技术,我们都深入的研究的话,很容易分散精力。新的技术可能很久之后我们才会在工作中用得上,当学的新技术无法学以致用,很容易被我们遗忘,到最后真的需要使用的时候,又要从头来过(虽然上手会更快)。我觉得身为技术人,针对新技术应该是持拥抱态度的,入了这一行你就应该知道这是一个活到老学到老的行业,所以面对新技术,不要抵触,拥抱变化就好了。Flutter 明显是一种全新的技术,而对于这个新技术在发布之初,花一个月的时间学习它,成本确实过高。

wfegre的博客 1120

3.6还没用上,Android Studio 4.0 稳定版已发布了

热文推荐:尘埃落定!清华才子王垠加入华为职级22,前阿里P10赵海平加入字节跳动,职级或为4+百度网盘“破解版”,Pandownload开发者被抓作者:恋猫de小郭链接:https://...

Java和Android架构 1268

fir.im Weekly - 深度揭秘 App 启动全过程

世纪寒潮席卷全中国,可谓普天之下莫低0℃。无论怎样的严寒都抵挡不了程序员们的创造的激情... 本期的 fir.im Weekly ,最新的iOS/Android 开发资源,GitHub 源码、前端技术干货分享,来看看 : ) 由 App 的启动说起 来自Jaminzzhang的博客分享。 “Code,Build,Run,一个活生生的App跃然方寸屏上,这一切是如何发生的?从用户点击App到执行m...

weixin_30588907的博客 175
上一篇: Framework 面试过不了?Android 字节跳动大牛的解析字节面试官常问的 Framework 问题
下一篇: 史上最全的Android面试题集锦
不秃头de程序猿
博客等级 码龄5年 4089粉丝 1478原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值