Android 协程块 UI 点击监听器

Android Coroutines block UI click listener

我使用 MVVM 作为架构,也是存储库模式。我有一个 Web 服务,还有一个房间数据库。使用协同程序阻止我单击的任何按钮。

有一个 list/detail 分别用片段和 activity 实现。

我可以弄清楚我实现协程和 Viewmodel 的方式出了什么问题。

class BuySharedViewModel(application: Application) : AndroidViewModel(application) {
private val repository: BuyRepository
var allBuys: LiveData<List<Buy>>


init {
    val buyDao = KunukRoomDatabase.getDatabase(application, viewModelScope).buyDao()
    val buyRemote = BuyRemote()
    repository = BuyRepository.getInstance(buyDao , buyRemote)
    //Use async because it return a result
    viewModelScope.launch { getAllBuys() }
    allBuys = buyDao.loadAllBuys()
}

private suspend fun getAllBuys() {
    repository.getBuys()

}
}

这是存储库,它从 Web 服务获取数据并将其添加到房间数据库,而 ViewModel 从房间数据库获取数据。

class BuyRepository (private val buyDao: BuyDao, private val buyRemote: BuyRemote) {
private val job = SupervisorJob()
private val scope = CoroutineScope(Dispatchers.Default + job)

companion object {
    //For singleton instantiation
    @Volatile private var instance: BuyRepository? = null

    fun getInstance(buyDao: BuyDao, buyRemote: BuyRemote) =
        instance ?: synchronized(this) {
            instance ?: BuyRepository(buyDao, buyRemote)
                .also { instance = it}

        }
}


suspend fun getBuys(){
    refresh()
}

private suspend fun refresh(){

    try {
        val list = scope.async { buyRemote.loadBuys() }
        list.await().forEach { buy -> insert(buy) }
    } catch (e: Throwable) {}
}


@WorkerThread
private fun insert(buy: Buy) {
    buyDao.insertBuy(buy)
}


}

片段工作,显示数据,当我点击该片段(recyclerView)中的一个项目时,activity 显示详细数据。但是 none 对 activity 的点击有效,就像它没有检测到点击一样。我猜它与协程有关,因为当我注释掉 BuySharedViewModel 中的代码 viewmodelScope.launch { getAllBuys()} 时,它起作用了,因为它从房间数据库中加载了先前调用的数据,并且点击有效。

详细视图中的代码如下:

class BuyDetailActivity : AppCompatActivity() {
private lateinit var sharedViewModel: BuySharedViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    lateinit var buy: Buy

    sharedViewModel = ViewModelProviders.of(this).get(BuySharedViewModel::class.java)

    val position = intent.getIntExtra("position", 0)
    sharedViewModel.allBuys.observe(this, Observer<List<Buy>> { buys ->
        buy = buys[position]
        val binding: com.example.drake.kunuk.databinding.ActivityBuyDetailBinding =
            DataBindingUtil.setContentView(this, com.example.drake.kunuk.R.layout.activity_buy_detail)
        binding.buy = buy


        val agentNumber = buy.agentNumber?:"+50937438713"
        bnvContactAgent.setOnNavigationItemSelectedListener { item ->

            when (item.itemId) {
                com.example.drake.kunuk.R.id.action_call -> {
                    val callNumberUri = Uri.parse("tel:$agentNumber")
                    val callIntent = Intent(Intent.ACTION_DIAL, callNumberUri)
                    startActivity(callIntent)
                }
                com.example.drake.kunuk.R.id.action_sms -> {
                    val smsNumberUri = Uri.parse("sms:$agentNumber")
                    val smsIntent = Intent(Intent.ACTION_SENDTO, smsNumberUri)
                    startActivity(smsIntent)
                }
                com.example.drake.kunuk.R.id.action_email -> {
                    val uriText = "mailto:drakecolin@gmail.com" +
                            "?subject=" + Uri.encode("I'm interested in $agentNumber") +
                            "&body=" + Uri.encode("Hello, ")

                    val uri = Uri.parse(uriText)

                    val sendIntent = Intent(Intent.ACTION_SENDTO)
                    sendIntent.data = uri
                    startActivity(Intent.createChooser(sendIntent, "Send email"))
                }
            }
            false
        }

这是我的片段代码:

class BuyFragment : Fragment() {
companion object {
    fun newInstance() = BuyFragment()
}

private lateinit var viewModel: BuySharedViewModel
private val buyList = ArrayList<Buy>()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Get a new or existing ViewModel from the ViewModelProvider.
    viewModel = ViewModelProviders.of(this).get(BuySharedViewModel::class.java)

    // Add an observer on the LiveData returned by loadAllBuys.
    // The onChanged() method fires when the observed data changes and the activity is
    // in the foreground.
    viewModel.allBuys.observe(this, Observer<List<Buy>> { buys ->
        // Update the cached copy of the words in the adapter.
        buys?.let { (rvBuy.adapter as BuyAdapter).setBuys(it) }
        progressBar.visibility = View.GONE
    })
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    return inflater.inflate(R.layout.buy_fragment, container, false)
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    rvBuy.layoutManager = LinearLayoutManager(context)
    rvBuy.adapter = BuyAdapter(activity!!.applicationContext,
        R.layout.buy_card, buyList)
    progressBar.visibility = View.VISIBLE


}
}

这是 BuyDao 的代码:

@Dao

接口 BuyDao { @Insert(onConflict = OnConflictStrategy.REPLACE) 有趣的插入购买(可变参数购买:购买)

@Update
fun updateBuy(vararg buys: Buy)

@Delete
 fun deleteBuys(vararg buys: Buy)

@Query("SELECT * FROM buys")
fun loadAllBuys(): LiveData<List<Buy>>

@Query("DELETE FROM buys")
suspend fun deleteAll()

}

viewModelScope 默认使用 Dispatchers.Main 并且它会阻止您的 UI.

试试这个:

viewmodelScope.launch(Dispatchers.IO) { getAllBuys()}

编辑:

问题是您在更新实时数据时在 BottomNavigation 上设置了监听器,这导致了这个奇怪的问题。

将您的 BuyDetailActivity 代码替换为:

class BuyDetailActivity : AppCompatActivity() {
    private lateinit var sharedViewModel: BuySharedViewModel
    private var agentNumber = ""

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityBuyDetailBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_buy_detail)
        binding.buy = Buy()
        lateinit var buy: Buy

        sharedViewModel = ViewModelProviders.of(this).get(BuySharedViewModel::class.java)

        val position = intent.getIntExtra("position", 0)
        sharedViewModel.allBuys.observe(this, Observer<List<Buy>> { buys ->
            buy = buys[position]
            binding.buy = buy
            binding.executePendingBindings()

            agentNumber = buy.agentNumber

            // set animation duration via code, but preferable in your layout files by using the animation_duration attribute
            expandableTextView.setAnimationDuration(750L)

            // set interpolators for both expanding and collapsing animations
            expandableTextView.setInterpolator(OvershootInterpolator())

            // or set them separately.
            expandableTextView.expandInterpolator = OvershootInterpolator()
            expandableTextView.collapseInterpolator = OvershootInterpolator()

            // toggle the ExpandableTextView
            buttonToggle.setOnClickListener {
                buttonToggle.setText(if (expandableTextView.isExpanded) com.example.drake.kunuk.R.string.more else com.example.drake.kunuk.R.string.less)
                expandableTextView.toggle()
            }

            // but, you can also do the checks yourself
            buttonToggle.setOnClickListener {
                if (expandableTextView.isExpanded) {
                    expandableTextView.collapse()
                    buttonToggle.setText(com.example.drake.kunuk.R.string.more)
                } else {
                    expandableTextView.expand()
                    buttonToggle.setText(com.example.drake.kunuk.R.string.less)
                }
            }

            //Open photoView activity when clicked
            ivHouseDetail.setOnClickListener {
                applicationContext
                    .startActivity(
                        Intent(
                            applicationContext,
                            ViewPagerActivity::class.java
                        )
                            .putExtra("imageList", buy.propertyImage)
                            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    )
            }
        })

        findViewById<BottomNavigationView>(R.id.bnvContactAgent)?.setOnNavigationItemSelectedListener { item ->

            when (item.itemId) {
                R.id.action_call -> {
                    Log.e("BIRJU", "Action call")
                    val callNumberUri = Uri.parse("tel:$agentNumber")
                    val callIntent = Intent(Intent.ACTION_DIAL, callNumberUri)
                    startActivity(callIntent)
                }
                R.id.action_sms -> {
                    Log.e("BIRJU", "Action SMS")
                    val smsNumberUri = Uri.parse("sms:$agentNumber")
                    val smsIntent = Intent(Intent.ACTION_SENDTO, smsNumberUri)
                    startActivity(smsIntent)
                }
                R.id.action_email -> {
                    Log.e("BIRJU", "Action Email")
                    val uriText = "mailto:drakecolin@gmail.com" +
                            "?subject=" + Uri.encode("I'm interested in $agentNumber") +
                            "&body=" + Uri.encode("Hello, ")
                    val uri = Uri.parse(uriText)
                    val sendIntent = Intent(Intent.ACTION_SENDTO)
                    sendIntent.data = uri
                    startActivity(Intent.createChooser(sendIntent, "Send email"))
                }
            }
            false
        }
    }
}