Kotlin 协程阻塞 Android 中的主线程

2024-05-11

我是 Kotlin 和协程的新手。我有一个fun在我的活动及其内部,检查User用户名和密码,如果为真,则返回Users object.
一切都好。但是当我按下按钮时,我的活动被阻止并等待响应Users login.
我用这个有趣的:

private fun checkLogin() : Boolean {           
        runBlocking {
            coroutineScope {
                launch {
                    user = viewModel.getUserAsync(login_username.text.toString(), login_password.text.toString()).await()
                }
            }
            if(user == null){
                return@runBlocking false
            }
            return@runBlocking true
        }
        return false
    }  

这是我的 ViewModel :

class LoginViewModel(app: Application) : AndroidViewModel(app) {
    val context: Context = app.applicationContext
    private val userService = UsersService(context)

    fun getUserAsync(username: String, password: String) = GlobalScope.async {
        userService.checkLogin(username, password)
    }
}

用户服务:

class UsersService(ctx: Context) : IUsersService {
        private val db: Database = getDatabase(ctx)
        private val api = WebApiService.create()
        override fun insertUser(user: Users): Long {
            return db.usersDao().insertUser(user)
        }

        override suspend fun checkLogin(username: String, pass: String): Users? {
            return api.checkLogin(username, pass)
        }
    }

    interface IUsersService {
        fun insertUser(user: Users) : Long
        suspend fun checkLogin(username: String, pass: String): Users?
    }

这是我的 apiInterface:

interface WebApiService {

    @GET("users/login")
    suspend fun checkLogin(@Query("username") username: String,
                   @Query("password")password: String) : Users

如何解决等待从服务器检索数据时阻止我的活动的问题?


你几乎*不应该使用runBlocking在 Android 应用程序中。它仅适用于mainJVM 应用程序的功能或在测试中允许使用在应用程序退出之前完成的协程。否则它就违背了协程的目的,因为它会阻塞直到所有 lambda 返回。

您也不应该使用 GlobalScope,因为它会使 Activity 关闭时取消作业变得很棘手,并且它会在后台线程而不是主线程中启动协程。您应该为活动使用本地范围。您可以通过在活动中创建属性来做到这一点(val scope = MainScope())并取消它onDestroy() (scope.cancel())。或者如果您使用androidx.lifecycle:lifecycle-runtime-ktx库你可以只使用现有的lifecycleScope财产。

如果你总是await在返回之前你的异步作业,那么你的整个函数将阻塞,直到你得到结果,所以你已经采取了后台任务并使其阻塞主线程。

有几种方法可以解决这个问题。

  1. 让 ViewModel 公开一个挂起函数,然后 Activity 从协程中调用它。
class LoginViewModel(app: Application) : AndroidViewModel(app) {
    //...

    // withContext(Dispatchers.Default) makes the suspend function do something
    // on a background thread and resumes the calling thread (usually the main 
    // thread) when the result is ready. This is the usual way to create a simple
    // suspend function. If you don't delegate to a different Dispatcher like this,
    // your suspend function runs its code in the same thread that called the function
    // which is not what you want for a background task.
    suspend fun getUser(username: String, password: String) = withContext(Dispatchers.Default) {
        userService.checkLogin(username, password)
    }
}

//In your activity somewhere:
lifecycleScope.launch {
    user = viewModel.getUser(login_username.text.toString(), login_password.text.toString())
    // do something with user
}
  1. 通过正确的视图模型封装,活动实际上不必启动这样的协程。这user属性应该是 Activity 可以观察到的 ViewModel 中的 LiveData。因此,协程只需要从 ViewModel 中启动:
class LoginViewModel(app: Application) : AndroidViewModel(app) {
    //...
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> = _user

    init {
        fetchUser()
    }

    private fun fetchUser(username: String, password: String) = viewModelScope.launch {
        val result = withContext(Dispatchers.Default) {
            userService.checkLogin(username, password)
        }
        _user.value = result
    }
}

//In your activity somewhere:
viewModel.user.observe(this) { user ->
    // do something with user
}

*如果您正在使用不支持协程的代码库,并且它要求您重写或实现一个非挂起函数,该函数将从主线程调用并可以处理阻塞、长时间运行的代码,那么它会可以接受使用runBlocking作为这两个世界之间的桥梁,以便能够在与该库交互时使用基于协程的代码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kotlin 协程阻塞 Android 中的主线程 的相关文章

  • PickContact 需要 android.permission.READ_CONTACTS 或 grantUriPermission()

    首先一些信息 我首先在 Cordova 中创建了该应用程序 请参阅Cordova 权限需要 android permission READ CONTACTS 或 grantUriPermission https stackoverflow
  • android studio 和 netbeans 中输入扫描器和解析的不同行为

    我正在使用 NetBeans 测试基本代码 和 Android Studio 实际应用程序 读取相同的管道分隔文件 并得到不同的结果 这是有问题的代码 String URL http CalendarUTF8 Dec2016 txt try
  • ViewPager 中的按钮滚动到特定页面

    我的 ViewPager 内的布局之一有一个按钮 R layout add site 我想要点击按钮的选项 它会滚动到我的特定页面 我已经可以选择滑动到特定页面 但我想同时拥有这两个选项 现在我确信有办法做到这一点 但由于某种原因 我无法弄
  • 如何将本地主机 IP 地址转发到 Android 模拟器?

    我知道可以将端口从我的开发机器转发到 Android 模拟器 但是这是如何完成的呢 我在 android developers 网站上找到了解决方案 但我不明白他们的说明是什么意思 有人对此有明确的指示吗 我的开发机器运行的是 Window
  • 如何禁用列表视图中的项目?

    我有一个列表视图 它是通过数据库中的记录填充的 现在我必须使一些记录可见但不可选择 我怎样才能做到这一点 这是我的代码 public class SomeClass extends ListActivity private static L
  • 尝试运行我的 Espresso 测试时 RecyclerViewMatcher 中出现 NullPointerException

    我想跑Espresso Testing on my Android project 第 1 步 找到我的RecyclerView 第 2 步 检查上的项目RecyclerView 第一步成功运行 但第二步应该检查recycler view项
  • 无法在 lapism/SearchView 中的 null 对象上调用 void android.view.View.setElevation(float)

    我正在使用库 lapism SearchView https github com lapism SearchView https github com lapism SearchView 在我的项目中 但我仅在 Android 5 0 上
  • SwipeListView 47 度:以编程方式滑动第一项

    我想滑动第一个项目滑动列表视图 https github com 47deg android swipelistview on Activity启动以向用户显示SwipeListView是可滑动的 如何使用此 UI 元素以编程方式执行此操作
  • 减少 android studio 2.1 构建时间

    我之前使用的是 android studio 1 5 刚刚升级到 2 1 但是更新后我的构建花费了更多时间 从 15 秒到现在 2 分钟 我已经尝试过的事情如上所述 在 设置 中启用离线工作 org gradle daemon true o
  • 如何过滤EditText的输入?

    我想过滤一个的输入EditText 只允许使用数字和字母 首先我使用TextWatcher处理最后一个输入字符 但是当您移动光标或将某些内容粘贴到EditText 这个方法失败了 现在我想知道有没有办法过滤非法输入并反馈给用户 Add In
  • 使用 sdk 3.0 在 Facebook 墙上发布

    我试图在由其 ID 定义的用户墙上发布一条消息 但作为响应 我收到错误 未知方法 我的代码是 final Bundle params new Bundle params putByteArray message Test getBytes
  • RecyclerView onItemClickListener 不工作

    我正在研究回收视图并尝试对 recyclerview 的每个项目使用点击侦听器界面 这是我的活动课程 public class LegacyHomeActivity extends ActivityBaseDrawer private Li
  • Android 拍摄后画质低

    我有一个触发图像捕获的按钮 private void capturePicture if ActivityCompat checkSelfPermission getContext Manifest permission CAMERA Pa
  • 如果没有当前类的引用,svgLoader 无法工作

    这是我的主类 通过这个类 我通过 url 从 api 获取数据 并创建一个适配器类来维护它 但发生了意外错误 这是MainActivity java public class MainActivity extends AppCompatAc
  • 多少次函数调用会导致堆栈溢出

    你好 Android Java 开发者 当一个函数调用一个函数并且该函数调用另一个函数等等时 有多少次调用 堆栈长度 会让我陷入堆栈溢出 有一般经验法则吗 我问的原因是因为我现在对于我的 5 人纸牌游戏来说哪个更有效 设计明智 解决方案一
  • 旋转后,精灵和矩形位置在 Libgdx 中未对齐

    我有以下代码 其中紧密映射的精灵 矩形和多边形在 libgdx 中以相同角度旋转 旋转后矩形与精灵不对齐 虽然精灵在绘制时会旋转 但旋转后坐标和尺寸保持不变 矩形的情况并非如此 请参阅下面的代码和结果图 public void rotate
  • 如何在屏幕上拖动图像

    谁能帮助我如何为可以在屏幕上拖动的图像编写程序 你能给我一个示例代码吗 多谢 我怎样才能为它制作游戏得分 我将感谢您的回复 imageView setOnLongClickListener new OnLongClickListener p
  • 在Android中创建自定义按钮类

    我正在尝试为我的 Android 应用程序创建自定义按钮类 public class TicTacButton extends Button 我已经在里面设置了所有构造函数TicTacButton并创建了自定义方法和属性 在我的主要活动中
  • Android - 下载 JSON 数据并保存到共享首选项

    我正在从 PHP 服务读取 JSON 数据 每当该 JSON 的版本发生变化时 我想将其存储在 Android 上 用新数据替换旧数据 JSON 仅用于填充 Spinner 我的问题是 JSON 有 36KB 可以将其存储在共享首选项中有一
  • 如何解决“布局有超过 80 个视图,对性能不利”?

    我正在做一个有点复杂的布局 只是我无法修复 LINT 指示的错误 黑莓浏览次数超过 80 对性能不利 这是布局

随机推荐

  • 有 Google Keep API 吗? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 Google Keep 有 API 吗 我想为 Google Keep 制作一个 Windows 8 应
  • 自动检测log4j静态初始化错误的方法

    请注意 这更像是 Bash 问题 而不是 Java 问题 请参阅下面的注释 在每个类中配置log4j时 我们执行以下操作 public class Example private static final Logger log Logger
  • 将 Foq 与 F# 函数类型结合使用

    例如 我使用 F 类型定义来防止函数之间的硬依赖 type IType1 int gt int type IType2 int gt string let func1 i int int i i let func2 i int string
  • 无法使用 jQuery 添加两个小数

    我试图将两个小数值相加 但返回的总和是纯整数 怎么了 我找不到它 欢迎任何帮助 jQuery delivery method ship select change function var cost jQuery this val jQue
  • ObjC 中的 self 是什么?我应该什么时候使用它?

    什么是self在 Objective C 中是什么意思 我应该何时何地使用它 是否类似于this在Java中 self指的是您正在使用的当前类的实例 是的 它类似于this在爪哇 如果您想对该类的当前实例执行操作 则可以使用它 例如 如果您
  • 如何禁用向左滚动?

    I got a div 元素 parent 包含多个子元素 item 我想启用滚动父元素一个方向 left OR正确的 否则什么都不会发生 看我的代码 parent scroll function gt gt gt scroll event
  • 使用 iconv 将 UTF-16BE 转换为无 BOM 的 UTF-8

    我正在尝试使用 iconv 将 UTF 16BE 编码文件 字节顺序标记 0xFE 0xFF 转换为 UTF 8 如下所示 iconv f UTF 16BE t UTF 8 myfile txt 然而 生成的输出具有 UTF 8 字节顺序标
  • Git 子模块在 Windows 上更新缓慢

    Git 子模块在 Windows 上似乎非常慢 为了测试性能 我创建了 3 个裸存储库并向它们提交了 3 条独立消息 未存储文件 然后 我将每个裸存储库作为子模块添加到新的 git 存储库中 并执行子模块更新 花费了 5 秒多的时间 当使用
  • 如何在生产中安全地更改会话 cookie 域或名称?

    我们最近意识到我们的会话 cookie 正在被写入我们网站的完全限定域名 www myapp com 例如 MYAPPCOOKIE 79D5DB83 domain www myapp com 我们希望将其切换为可以跨子域共享的cookie
  • 从Oracle表中删除重复行

    我正在 Oracle 中测试某些内容并使用一些示例数据填充表 但在此过程中我不小心加载了重复记录 因此现在我无法使用某些列创建主键 如何删除所有重复行并只保留其中一行 Use the rowid伪列 DELETE FROM your tab
  • List、IList、IEnumerable、IQueryable、ICollection,哪个返回类型最灵活?

    我之前已经在这里看到过这个问题 但我不满意我理解的完整后果 问题是使用 linq to sql 返回的数据层应该使用什么返回类型以获得最大的灵活性和查询能力 这是我读过 发现的 IEnumerable 是有限的 只允许向前读操作 IEnum
  • 更新 Azure Blob 上的 LastModified

    我正在移植代码以使用 C 中的 Azure 存储 SDK 传统上 我称其为更新修改文件的上次写入 修改时间 File SetLastWriteTimeUtc fileName lastWriteTimeUtc 要更新 blob 的上次修改时
  • 如何创建记录而不将其保存在数据库中

    我正在使用InventoryOdoo 12 的插件 但我的问题可能发生在任何模块上 在这个插件中 一个StockMove模型有一个move line ids field In the Detailed Operations对话框中 我们可以
  • Mojo 配置的自定义类型转换器?

    我需要使用自定义类型 例如LunarDate 在我的 Mojo 对象中 class MyMojo extends AbstractMojo parameter LunarDate lunarDate 我想配置参数
  • 内核与系统中的 Windows 进程

    我有一些与内核和用户模式下的 Windows 进程相关的问题 如果我有一个 hello world 应用程序和一个公开新系统调用 foo 的 hello world 驱动程序 我很好奇一旦处于内核模式 我能做什么和不能做什么 对于初学者来说
  • Sourcetree 2.1.2.5 - 显示“未提交的更改”,但没有任何待处理的内容

    我有一个以前没有遇到过的问题 即使我没有什么可提交的 并尝试将我的分支重置为 Sourcetree 显示的最新提交Uncommitted changes 根据 Atlassian 论坛的说法 通常有两个原因 您的工作目录中有很多很多未暂存的
  • 在 MySQL 表中存储用户密码的最佳 PHP 哈希方法?

    我已经阅读 Stack Overflow 问题大约 15 分钟了 每一个问题似乎都与我之前读到的问题相矛盾 Bcrypt SHA1 MD5 等 我目前对我的密码进行 MD5 但我想让我的数据库在发生泄露时更加安全 我知道这个问题已经被问了一
  • 如何使用 PHP 以任意顺序进行字符搜索(12 个字母,其中 6 个字母构成一个单词)?

    我整天都在想这个问题 似乎无法找出一种记忆有效且快速的方法 问题是 例如 我有这些信 e f j l n rr t t u w x 12 个字母 我正在找这个词 海龟 6 个字母 如何使用 php 找到完整范围 12 个单词 中所有可能的单
  • 当列的数据类型为 int 时,如何用字符串替换 null

    我有一个包含 3 列的表和如下示例数据 所有列都是数据类型int 我有这个查询 select foodid dayid from Schedule 我要更换dayid用字符串 ifdayid null 为此我尝试了这个查询 select f
  • Kotlin 协程阻塞 Android 中的主线程

    我是 Kotlin 和协程的新手 我有一个fun在我的活动及其内部 检查User用户名和密码 如果为真 则返回Users object 一切都好 但是当我按下按钮时 我的活动被阻止并等待响应Users login 我用这个有趣的 priva