当前位置: 首页 > news >正文

做微博分析的网站电商平台app大全

做微博分析的网站,电商平台app大全,wordpress get_search_form,安卓app制作软件1. 协程的定义 协程属于Kotlin中非常有特色的一项技术#xff0c;因为大部分编程语言中是没有协程这个概念的。 什么是协程呢#xff1f;它其实和线程是有点类似的#xff0c;可以简单地将它理解成一种轻量级的线程。要知道#xff0c;线程是非常重量级的#xff0c;它需要…1. 协程的定义 协程属于Kotlin中非常有特色的一项技术因为大部分编程语言中是没有协程这个概念的。 什么是协程呢它其实和线程是有点类似的可以简单地将它理解成一种轻量级的线程。要知道线程是非常重量级的它需要依靠操作系统的调度才能实现不同线程之间的切换。而使用协程却可以仅在编程语言的层面就能实现不同协程之间的切换从而大大提升了并发编程的运行效率。 举一个具体点的例子比如我们有如下foo()和bar()两个方法 fun foo() {a()b()c() } fun bar() {x()y()z() }在没有开启线程的情况下先后调用foo()和bar()这两个方法那么理论上结果一定是a()、b()、c()执行完了以后x()、y()、z()才能够得到执行。而如果使用了协程在协程A中去调用foo()方法协程B中去调用bar()方法虽然它们仍然会运行在同一个线程当中但是在执行foo()方法时随时都有可能被挂起转而去执行bar()方法执行bar()方法时也随时都有可能被挂起转而继续执行foo()方法最终的输出结果也就变得不确定了。 可以看出协程允许我们在单线程模式下模拟多线程编程的效果代码执行时的挂起与恢复完全是由编程语言来控制的和操作系统无关。这种特性使得高并发程序的运行效率得到了极大的提升试想一下开启10万个线程完全是不可想象的事吧而开启10万个协程就是完全可行的。 2. 协程的基本用法 Kotlin并没有将协程纳入标准库的API当中而是以依赖库的形式提供的。所以如果我们想要使 用协程功能需要先在app/build.gradle文件当中添加如下依赖库 dependencies {...implementation org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1//在Android项目中才会用到implementation org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1 }如何开启一个协程最简单的方式就是使用Global.launch函数如下所示 fun main() {GlobalScope.launch {println(codes run in coroutine scope)} }GlobalScope.launch函数可以创建一个协程的作用域这样传递给launch函数的代码块 Lambda表达式就是在协程中运行的了这里我们只是在代码块中打印了一行日志。那么现在运行main()函数日志能成功打印出来吗如果你尝试一下会发现没有任何日志输出。 这是因为Global.launch函数每次创建的都是一个顶层协程这种协程当应用程序运行结束时也会跟着一起结束。刚才的日志之所以无法打印出来就是因为代码块中的代码还没来得及运行应用程序就结束了。 要解决这个问题也很简单我们让程序延迟一段时间再结束就行了如下所示 fun main() {GlobalScope.launch {println(codes run in coroutine scope)}Thread.sleep(1000) }这里使用Thread.sleep()方法让主线程阻塞1秒钟现在重新运行程序你会发现日志可以正常打印出来了. 可是这种写法还是存在问题如果代码块中的代码在1秒钟之内不能运行结束那么就会被强制中断。观察如下代码 fun main() {GlobalScope.launch {println(codes run in coroutine scope)delay(1500)println(codes run in coroutine scope finished)}Thread.sleep(1000) }我们在代码块中加入了一个delay()函数并在之后又打印了一行日志。delay()函数可以让当前协程延迟指定时间后再运行但它和Thread.sleep()方法不同。delay()函数是一个非阻塞式的挂起函数它只会挂起当前协程并不会影响其他协程的运行。而Thread.sleep()方法会阻塞当前的线程这样运行在该线程下的所有协程都会被阻塞。注意delay()函数只能在协程的作用域或其他挂起函数中调用。 这里我们让协程挂起1.5秒但是主线程却只阻塞了1秒最终会是什么结果呢重新运行程序你会发现代码块中新增的一条日志并没有打印出来因为它还没能来得及运行应用程序就已经结束了. 那么有没有什么办法能让应用程序在协程中所有代码都运行完了之后再结束呢当然也是有的借助runBlocking函数就可以实现这个功能 fun main() {runBlocking {println(codes run in coroutine scope)delay(1500)println(codes run in coroutine scope finished)} }runBlocking函数同样会创建一个协程的作用域但是它可以保证在协程作用域内的所有代码和子协程没有全部执行完之前一直阻塞当前线程。需要注意的是runBlocking函数通常只应该在测试环境下使用在正式环境中使用容易产生一些性能上的问题。 虽说现在我们已经能够让代码在协程中运行了可是好像并没有体会到什么特别的好处。这是 因为目前所有的代码都是运行在同一个协程当中的而一旦涉及高并发的应用场景协程相比于线程的优势就能体现出来了。 那么如何才能创建多个协程呢很简单使用launch函数就可以了如下所示 fun main() {runBlocking {launch {println(launch1)delay(1000)println(launch1 finished)}launch {println(launch2)delay(1000)println(launch2 finished)}} }注意这里的launch函数和我们刚才所使用的GlobalScope.launch函数不同。首先它必须在协程的作用域中才能调用其次它会在当前协程的作用域下创建子协程。子协程的特点是如果外层作用域的协程结束了该作用域下的所有子协程也会一同结束。相比而言GlobalScope.launch函数创建的永远是顶层协程这一点和线程比较像因为线程也没有层级这一说永远都是顶层的。 这两个子协程实际却运行在同一个线程当中只是由编程语言来决定如何在多个协程之间进行调度让谁运行让谁挂起。调度的过程完全不需要操作系统参与这也就使得协程的并发效率会出奇得高。 那么具体会有多高呢我们来做下实验就知道了代码如下所示 fun main() {val start System.currentTimeMillis()runBlocking {repeat(100000) {launch {println(.)}}}val end System.currentTimeMillis()println(end - start) }这里使用repeat函数循环创建了10万个协程不过在协程当中并没有进行什么有意义的操作 只是象征性地打印了一个点然后记录一下整个操作的运行耗时。 这里仅仅耗时了961毫秒这足以证明协程有多么高效。试想一下如果开启的是 10万个线程程序或许已经出现OOM异常了。 随着launch函数中的逻辑越来越复杂可能你需要将部分代码提取到一个单独的函数中。这个时候就产生了一个问题我们在launch函数中编写的代码是拥有协程作用域的但是提取到一个单独的函数中就没有协程作用域了那么我们该如何调用像delay()这样的挂起函数呢 为此Kotlin提供了一个suspend关键字使用它可以将任意函数声明成挂起函数而挂起函数之间都是可以互相调用的如下所示 suspend fun printDot() {println(.)delay(1000) }但是suspend关键字只能将一个函数声明成挂起函数是无法给它提供协程作用域的。比如你现在尝试在printDot()函数中调用launch函数一定是无法调用成功的因为launch函数要求必须在协程作用域当中才能调用。 这个问题可以借助coroutineScope函数来解决。coroutineScope函数也是一个挂起函数因此可以在任何其他挂起函数中调用。它的特点是会继承外部的协程的作用域并创建一个子协程借助这个特性我们就可以给任意挂起函数提供协程作用域了。示例写法如下 suspend fun printDot() coroutineScope {launch {println(.)delay(1000)} }另外coroutineScope函数和runBlocking函数还有点类似它可以保证其作用域内的所有代码和子协程在全部执行完之前外部的协程会一直被挂起。 coroutineScope函数和runBlocking函数的作用是有点类似的但是coroutineScope函数只会阻塞当前协程既不影响其他协程也不影响任何线程因此是不会造成任何性能上的问题的。而runBlocking函数由于会挂起外部线程如果你恰好又在主线程中当中调用它的话那么就有可能会导致界面卡死的情况所以不太推荐在实际项目中使用。 3. 更多作用域构建器 GlobalScope.launch、runBlocking、launch、coroutineScope这几种作用域构建器它们都可以用于创建一个新的协程作用域。不过GlobalScope.launch和runBlocking函数是可以在任意地方调用的coroutineScope函数可以在协程作用域或挂起函数中调用而launch函数只能在协程作用域中调用。 前面已经说了runBlocking由于会阻塞线程因此只建议在测试环境下使用。而GlobalScope.launch由于每次创建的都是顶层协程一般也不太建议使用除非你非常明确就是要创建顶层协程。 为什么说不太建议使用顶层协程呢主要还是因为它管理起来成本太高了。举个例子比如我们在某个Activity中使用协程发起了一条网络请求由于网络请求是耗时的用户在服务器还没来得及响应的情况下就关闭了当前Activity此时按理说应该取消这条网络请求或者至少不应该进行回调因为Activity已经不存在了回调了也没有意义。 那么协程要怎样取消呢不管是GlobalScope.launch函数还是launch函数它们都会返回一个Job对象只需要调用Job对象的cancel()方法就可以取消协程了如下所示 val job GlobalScope.launch {// 处理具体的逻辑 } job.cancel()但是如果我们每次创建的都是顶层协程那么当Activity关闭时就需要逐个调用所有已创建协程的cancel()方法试想一下这样的代码是不是根本无法维护 因此GlobalScope.launch这种协程作用域构建器在实际项目中也是不太常用的。下面演示一下实际项目中比较常用的写法 val job Job() val scope CoroutineScope(job) scope.launch {// 处理具体的逻辑 } job.cancel()可以看到我们先创建了一个Job对象然后把它传入CoroutineScope()函数当中注意这里的CoroutineScope()是个函数虽然它的命名更像是一个类。CoroutineScope()函数会返回一个CoroutineScope对象这种语法结构的设计更像是我们创建了一个CoroutineScope的实例可能也是Kotlin有意为之的。有了CoroutineScope对象之后就可以随时调用它的launch函数来创建一个协程了。 现在所有调用CoroutineScope的launch函数所创建的协程都会被关联在Job对象的作用域 下面。这样只需要调用一次cancel()方法就可以将同一作用域内的所有协程全部取消从而大大降低了协程管理的成本。 调用launch函数可以创建一个新的协程但是launch函数只能用于执行一段逻辑却不能获取执行的结果因为它的返回值永远是一个Job对象。那么有没有什么办法能够创建一个协程并获取它的执行结果呢当然有使用async函数就可以实现。 async函数必须在协程作用域当中才能调用它会创建一个新的子协程并返回一个Deferred对象如果我们想要获取async函数代码块的执行结果只需要调用Deferred对象的await()方法即可代码如下所示 fun main() {runBlocking {val result async {5 5}.await()println(result)} }不过async函数的奥秘还不止于此。事实上在调用了async函数之后代码块中的代码就会立刻开始执行。当调用await()方法时如果代码块中的代码还没执行完那么await()方法会将当前协程阻塞住直到可以获得async函数的执行结果。 fun main() {runBlocking {val start System.currentTimeMillis()val result1 async {delay(1000)5 5}.await()val result2 async {delay(1000)4 6}.await()println(result is ${result1 result2}.)val end System.currentTimeMillis()println(cost ${end - start} ms.)} }这种写法明显是非常低效的因为两个async函数完全可以同时执行从而提高运行效率。现在对上述代码使用如下的写法进行修改 fun main() {runBlocking {val start System.currentTimeMillis()val deferred1 async {delay(1000)5 5}val deferred2 async {delay(1000)4 6}println(result is ${deferred1.await() deferred2.await()}.)val end System.currentTimeMillis()println(cost ${end - start} milliseconds.)} }现在我们不在每次调用async函数之后就立刻使用await()方法获取结果了而是仅在需要用到async函数的执行结果时才调用await()方法进行获取这样两个async函数就变成一种并行关系了。 withContext()函数是一个挂起函数大体可以将它理解成async函数的一种简化版写法示例写法如下 fun main() {runBlocking {val result withContext(Dispatchers.Default) {5 5}println(result)} }调用withContext()函数之后会立即执行代码块中的代码同时将外部协程挂起。当代码块中的代码全部执行完之后会将最后一行的执行结果作为withContext()函数的返回值返回因此基本上相当于val result async{ 5 5}.await()的写法。唯一不同的是withContext()函数强制要求我们指定一个线程参数, 关于这个参数我准备好好讲一讲。 协程是一种轻量级的线程的概念因此很多传统编程情况下需要开启多线程执行的并发任务现在只需要在一个线程下开启多个协程来执行就可以了。但是这并不意味着我们就永远不需要开启线程了比如说Android中要求网络请求必须在子线程中进行即使你开启了协程去执行网络请求假如它是主线程当中的协程那么程序仍然会出错。这个时候我们就应该通过线程参数给协程指定一个具体的运行线程。 线程参数主要有以下3种值可选Dispatchers.Default、Dispatchers.IO和Dispatchers.Main。Dispatchers.Default表示会使用一种默认低并发的线程策略当你要执行的代码属于计算密集型任务时开启过高的并发反而可能会影响任务的运行效率此时就可以使用Dispatchers.Default。Dispatchers.IO表示会使用一种较高并发的线程策略当你要执行的代码大多数时间是在阻塞和等待中比如说执行网络请求时为了能够支持更高的并发数量此时就可以使用Dispatchers.IO。Dispatchers.Main则表示不会开启子线程而是在Android主线程中执行代码但是这个值只能在Android项目中使用纯Kotlin程序使用这种类型的线程参数会出现错误。 事实上在我们刚才所学的协程作用域构建器中除了coroutineScope函数之外其他所有的函数都是可以指定这样一个线程参数的只不过withContext()函数是强制要求指定的而其他函数则是可选的。 到目前为止你已经掌握了协程中最常用的一些用法并且了解了协程的主要用途就是可以大幅度地提升并发编程的运行效率。但实际上Kotlin中的协程还可以对传统回调的写法进行优化从而让代码变得更加简洁那么接下来我们就开始学习这部分的内容。 4. 使用协程简化回调的写法 回调机制基本上是依靠匿名类来实现的但是匿名类的写法通常比较烦琐比如如下代码 HttpUtil.sendHttpRequest(address, object : HttpCallbackListener {override fun onFinish(response: String) {// 得到服务器返回的具体内容}override fun onError(e: Exception) {// 在这里对异常情况进行处理} })在多少个地方发起网络请求就需要编写多少次这样的匿名类实现。这不禁引起了我们的思考有没有更加简单一点的写法呢 在过去可能确实没有什么更加简单的写法了。不过现在Kotlin的协程使我们的这种设想成为了可能只需要借助suspendCoroutine函数就能将传统回调机制的写法大幅简化下面我们就来具体学习一下。 suspendCoroutine函数必须在协程作用域或挂起函数中才能调用它接收一个Lambda表达式参数主要作用是将当前协程立即挂起然后在一个普通的线程中执行Lambda表达式中的代码。Lambda表达式的参数列表上会传入一个Continuation参数调用它的resume()方法或resumeWithException()可以让协程恢复执行。 了解了suspendCoroutine函数的作用之后接下来我们就可以借助这个函数来对传统的回调写法进行优化。首先定义一个request()函数代码如下所示 suspend fun request(address: String): String {return suspendCoroutine { continuation -HttpUtil.sendHttpRequest(address, object : HttpCallbackListener {override fun onFinish(response: String) {continuation.resume(response)}override fun onError(e: Exception) {continuation.resumeWithException(e)}})} }可以看到request()函数是一个挂起函数并且接收一个address参数。在request()函数的内部我们调用了刚刚介绍的suspendCoroutine函数这样当前协程就会被立刻挂起而Lambda表达式中的代码则会在普通线程中执行。接着我们在Lambda表达式中调用HttpUtil.sendHttpRequest()方法发起网络请求并通过传统回调的方式监听请求结果。如果请求成功就调用Continuation的resume()方法恢复被挂起的协程并传入服务器响应的数据该值会成为suspendCoroutine函数的返回值。如果请求失败就调用Continuation的resumeWithException()恢复被挂起的协程并传入具体的异常原因。 这里不是仍然使用了传统回调的写法吗代码怎么就变得更加简化了这是因为不管之后我们要发起多少次网络请求都不需要再重复进行回调实现了。比如说获取百度首页的响应数据就可以这样写 suspend fun getBaiduResponse() {try {val response request(https://www.baidu.com/)// 对服务器响应的数据进行处理} catch (e: Exception) {// 对异常情况进行处理} }有没有觉得代码变得清爽了很多呢由于 getBaiduResponse()是一个挂起函数因此当它调用了request()函数时当前的协程就会被立刻挂起然后一直等待网络请求成功或失败后当前协程才能恢复运行。这样即使不使用回调的写法我们也能够获得异步网络请求的响应数据而如果请求失败则会直接进入catch语句当中。 不过这里你可能又会产生新的疑惑getBaiduResponse()函数被声明成了挂起函数这样它也只能在协程作用域或其他挂起函数中调用了使用起来是不是非常有局限性确实如此因为suspendCoroutine函数本身就是要结合协程一起使用的。不过通过合理的项目架构设计我们可以轻松地将各种协程的代码应用到一个普通的项目当中. 事实上suspendCoroutine函数几乎可以用于简化任何回调的写法比如之前使用Retrofit来发起网络请求需要这样写 val appService ServiceCreator.createAppService() appService.getAppData().enqueue(object : CallbackListApp {override fun onResponse(call: CallListApp, response: ResponseListApp) {// 得到服务器返回的数据}override fun onFailure(call: CallListApp, t: Throwable) {// 在这里对异常情况进行处理} })不用担心使用suspendCoroutine函数我们马上就能对上述写法进行大幅度的简化。由于不同的Service接口返回的数据类型也不同所以这次我们不能像刚才那样针对具体的类型进行编程了而是要使用泛型的方式。定义一个await()函数代码如下所示 suspend fun T CallT.await(): T {return suspendCoroutine { continuation -enqueue(object : CallbackT {override fun onResponse(call: CallT, response: ResponseT) {val body response.body()if (body ! null) continuation.resume(body)else continuation.resumeWithException(RuntimeException(response body is null))}override fun onFailure(call: CallT, t: Throwable) {continuation.resumeWithException(t)}})} }这段代码相比于刚才的request()函数又复杂了一点。首先await()函数仍然是一个挂起函数然后我们给它声明了一个泛型T并将await()函数定义成了Call的扩展函数这样所有返回值是Call类型的Retrofit网络请求接口就都可以直接调用await()函数了。 接着await()函数中使用了suspendCoroutine函数来挂起当前协程并且由于扩展函数的原因我们现在拥有了Call对象的上下文那么这里就可以直接调用enqueue()方法让Retrofit发起网络请求。接下来使用同样的方式对Retrofit响应的数据或者网络请求失败的情况进行处理就可以了。另外还有一点需要注意在onResponse()回调当中我们调用body()方法解析出来的对象是可能为空的。如果为空的话这里的做法是手动抛出一个异常你也可以根据自己的逻辑进行更加合适的处理。 有了await()函数之后我们调用所有Retrofit的Service接口都会变得极其简单比如刚才同样的功能就可以使用如下写法进行实现 suspend fun getAppData() {try {val appList ServiceCreator.createAppService().getAppData().await()// 对服务器响应的数据进行处理} catch (e: Exception) {// 对异常情况进行处理} }
http://www.dnsts.com.cn/news/264439.html

相关文章:

  • 卖东西的网站模板免费下载软件工程师中级证书
  • 上海网站建设推广江北网站建设的技术
  • 吉林省建设厅网站杨学武长春网站建设联系吉网传媒优
  • 应聘网站建设工程师seo和sem是什么
  • 社区网站建设资金申请海曙网站制作
  • 广东省住建厅官方网站怎么样做免费的百度seo
  • 宁波公司网站制作做微信公众号海报的网站
  • 灰系网站个人网站开发技术
  • 小程序商店怎么注销建站优化系统
  • wordpress网站转移安徽省建设工程网站
  • 成都美誉网站设计网站建设 6万
  • 宁波建网站可按需定制贵州毕节建设局网站官网
  • 济南网站制作案例linux wordpress 伪静态
  • 常德网站网站建设网站开发背景和意义
  • 襄阳做网站排行榜wordpress怎么建app
  • 在那个网站做ppt可以赚钱长尾关键词爱站
  • 深圳卓富通做网站深圳创业补贴政策2024最新
  • app网站建设手机APP软件开发杭州小蜜蜂网站建设
  • 做网站要素网站建设丶金手指下拉13
  • dede做手机网站企业网站开发要学什么
  • 网站 错误代码wordpress 插件外链
  • 网站设计需求分析报告哪个网站虚拟主机好
  • 深圳做购物网站wordpress登录链接
  • 消防电气火灾监控系统网站开发东莞网站关键词优化效果
  • 怎么做网站的百度排名网站建设软件哪个最好
  • 互联网站的建设维护营销合肥专业的房产网站建设
  • 网站导航建设注意事项合肥专业做网站公司
  • 对网站建设的看法网络安全工程师是干嘛的
  • 国外创意网站响应式网站建设智能优化
  • 万全网站建设免费获客软件