协程初始化一个lateinit变量,但不初始化另一个。

huangapple go评论77阅读模式
英文:

Coroutine initializes one lateinit var but not the other

问题

todoAdapter is initialized successfully because it is declared as a lateinit var, which means it can be initialized later in your code. On the other hand, dropdownAdapter is declared as private lateinit var dropdownAdapter: ArrayAdapter<User>, but it is not initialized before it's being used.

In your code, you are trying to initialize dropdownAdapter inside your coroutine block with the line:

  1. dropdownAdapter = ArrayAdapter<User>(this@MainActivity, R.layout.item_user, userResponse.body()!!)

However, if any exception is thrown before this line (for example, if there's an issue with the network request or if an exception is thrown in the try block for userResponse), the dropdownAdapter will remain uninitialized, and you'll encounter the kotlin.UninitializedPropertyAccessException when you try to use it.

To fix this issue, you should initialize dropdownAdapter before your coroutine block or handle exceptions that might occur during its initialization appropriately to ensure that it is initialized in all cases.

英文:

I am making two retrofit calls from lifecycle scope. These two calls use async and await to get data from api.

Code :

  1. package com.example.retrofittodo
  2. import androidx.appcompat.app.AppCompatActivity
  3. import android.os.Bundle
  4. import android.util.Log
  5. import android.widget.ArrayAdapter
  6. import androidx.core.view.isVisible
  7. import androidx.lifecycle.lifecycleScope
  8. import androidx.recyclerview.widget.LinearLayoutManager
  9. import com.example.retrofittodo.databinding.ActivityMainBinding
  10. import kotlinx.coroutines.async
  11. import retrofit2.HttpException
  12. import retrofit2.Response
  13. import java.io.IOException
  14. const val TAG = &quot;MainActivity&quot;
  15. class MainActivity : AppCompatActivity() {
  16. private lateinit var binding: ActivityMainBinding
  17. private lateinit var todoAdapter: TodoAdapter
  18. private lateinit var dropdownAdapter: ArrayAdapter&lt;User&gt;
  19. override fun onCreate(savedInstanceState: Bundle?) {
  20. super.onCreate(savedInstanceState)
  21. binding = ActivityMainBinding.inflate(layoutInflater)
  22. setContentView(binding.root)
  23. // Get data
  24. lifecycleScope.launchWhenCreated {
  25. binding.progressBar.isVisible = true
  26. val todosDeferred = async { RetrofitInstance.api.getTodos(null) }
  27. val usersDeferred = async { RetrofitInstance.api.getUsers() }
  28. val todoResponse = try {
  29. todosDeferred.await()
  30. } catch (e: IOException) {
  31. // Most likely no internet connection or maybe output stream closed
  32. Log.e(TAG, &quot;IOException, you might not have internet connection&quot;)
  33. binding.progressBar.isVisible = false
  34. return@launchWhenCreated
  35. } catch (e: HttpException) {
  36. // If response code does not start with digit 2 then something is unusual
  37. Log.e(TAG, &quot;HttpException, unexpected response&quot;)
  38. binding.progressBar.isVisible = false
  39. return@launchWhenCreated
  40. }
  41. val userResponse = try {
  42. usersDeferred.await()
  43. } catch (e: IOException) {
  44. // Most likely no internet connection or maybe output stream closed
  45. Log.e(TAG, &quot;IOException, you might not have internet connection&quot;)
  46. binding.progressBar.isVisible = false
  47. return@launchWhenCreated
  48. } catch (e: HttpException) {
  49. // If response code does not start with digit 2 then something is unusual
  50. Log.e(TAG, &quot;HttpException, unexpected response&quot;)
  51. binding.progressBar.isVisible = false
  52. return@launchWhenCreated
  53. }
  54. if (checkResponse(todoResponse)) {
  55. todoAdapter.todos = todoResponse.body()!! //succeeds
  56. }
  57. else {
  58. Log.e(TAG, &quot;Todo response not successful&quot;)
  59. }
  60. if (checkResponse(userResponse)) {
  61. dropdownAdapter = ArrayAdapter&lt;User&gt;(this@MainActivity, R.layout.item_user, userResponse.body()!!) // fails
  62. }
  63. else {
  64. Log.e(TAG, &quot;User response not successful&quot;)
  65. }
  66. binding.progressBar.isVisible = false
  67. }
  68. setupRecyclerView()
  69. binding.dropdownUser.setAdapter(dropdownAdapter)
  70. }
  71. private fun &lt;T : Any&gt; checkResponse(response: Response&lt;List&lt;T&gt;&gt;) =
  72. response.isSuccessful &amp;&amp; response.body() != null
  73. private fun setupRecyclerView() = binding.rvTodos.apply {
  74. todoAdapter = TodoAdapter()
  75. adapter = todoAdapter
  76. layoutManager = LinearLayoutManager(this@MainActivity)
  77. }
  78. }

todoAdapter is initialized successfully but dropdownAdapter is not initialized.

Logcat :

  1. E/AndroidRuntime: FATAL EXCEPTION: main
  2. Process: com.example.retrofittodo, PID: 8917
  3. java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.retrofittodo/com.example.retrofittodo.MainActivity}: kotlin.UninitializedPropertyAccessException: lateinit property dropdownAdapter has not been initialized
  4. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3635)
  5. at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)
  6. at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
  7. at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
  8. at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
  9. at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
  10. at android.os.Handler.dispatchMessage(Handler.java:106)
  11. at android.os.Looper.loopOnce(Looper.java:201)
  12. at android.os.Looper.loop(Looper.java:288)
  13. at android.app.ActivityThread.main(ActivityThread.java:7839)
  14. at java.lang.reflect.Method.invoke(Native Method)
  15. at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
  16. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
  17. Caused by: kotlin.UninitializedPropertyAccessException: lateinit property dropdownAdapter has not been initialized
  18. at com.example.retrofittodo.MainActivity.onCreate(MainActivity.kt:119)
  19. at android.app.Activity.performCreate(Activity.java:8051)
  20. at android.app.Activity.performCreate(Activity.java:8031)
  21. at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)
  22. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3608)
  23. at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)&#160;
  24. at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)&#160;
  25. at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)&#160;
  26. at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)&#160;
  27. at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)&#160;
  28. at android.os.Handler.dispatchMessage(Handler.java:106)&#160;
  29. at android.os.Looper.loopOnce(Looper.java:201)&#160;
  30. at android.os.Looper.loop(Looper.java:288)&#160;
  31. at android.app.ActivityThread.main(ActivityThread.java:7839)&#160;
  32. at java.lang.reflect.Method.invoke(Native Method)&#160;
  33. at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)&#160;
  34. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)&#160;
  35. W/System: A resource failed to call close.

Why is it so ? I cannot think of any reason why I can modify one variable in the coroutine but not the other.

答案1

得分: 0

This line might fail:

  1. binding.dropdownUser.setAdapter(dropdownAdapter)

因为您在launchWhenCreated中异步初始化了dropdownAdapter,但在它能够初始化之前,您尝试使用它,例如.setAdapter(dropdownAdapter)

您可以选择:

  1. setupRecyclerView中创建dropdownAdapter,就像todoAdapter一样,然后稍后填充userResponse.body
  2. 或者在launchWhenCreated中初始化dropdownAdapter之后,调用binding.dropdownUser.setAdapter(dropdownAdapter)

此外,在从todosResponse返回之前,您应该取消用户API的调用,因为您不会使用它:usersDeferred.cancel()

英文:

Probably this line fails:

  1. binding.dropdownUser.setAdapter(dropdownAdapter)

Because, you initialize dropdownAdapter asynchronously in launchWhenCreated, but before it can get initialized, you try to use it like .setAdapter(dropdownAdapter).

You could either:

  1. Create dropdownAdapter in setupRecyclerView, just like todoAdapter, and fill the userResponse.body later.
  2. Or call binding.dropdownUser.setAdapter(dropdownAdapter) inside launchWhenCreated, after you initialize dropdownAdapter

Also, before you return from todosResponse with return@launchWhenCreated, you should cancel users API, because you won't use it: usersDeferred.cancel()

huangapple
  • 本文由 发表于 2023年4月19日 18:04:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76053222.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定