如何使用 android Kotlin 从实时数据库中读取异步数据?

How to read asynchronous data from real-time database using android Kotlin?

这是我使用 android Kotlin 从实时数据库读取异步数据的代码:

class suDetails : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_su_details)



        su_image.setOnClickListener {
            readData(object : MyCallback {
                override fun onCallback(imageUrl: String?) {
                    if (imageUrl != null) {
                        val imageViewer = Intent(baseContext, suDetails::class.java)
                        imageViewer.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                        imageViewer.putExtra("su_image", imageUrl)
                        startActivity(imageViewer)
                    }
                }
            })
        }

    }

    fun readData(myCallback: MyCallback) {

        val su_resource =intent
        val su_res = su_resource.getStringExtra("su_userid")

        val suRef = FirebaseDatabase.getInstance().getReference().child("Users").child(su_res!!)
        suRef.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                if(dataSnapshot.exists()){
                    su_layout.visibility = View.VISIBLE
                    val userData = dataSnapshot.getValue(profile_model::class.java)

                    val imageUrl = userData!!.getImageUrl()
                    Picasso.get().load(imageUrl).placeholder(R.drawable.ic_baseline_image_200).into(su_image)
                    su_name.text = userData.getnameOfsu()

                    Toast.makeText(baseContext, imageUrl, Toast.LENGTH_LONG).show()
                    
                    myCallback.onCallback(imageUrl)

                }
            }

            override fun onCancelled(error: DatabaseError) {

            }
        })
    }

    interface MyCallback {
        fun onCallback(value: String?)
    }

}

我已经参考了其他问题来从实时数据库中读取异步数据,但是当我尝试该解决方案时,我无法在 ImageViewtextView 中显示任何数据。我只看到空白屏幕。

Tyler V 回答后的新代码:

class suDetails : AppCompatActivity() {

    private var currentImageUrl: String = ""
    private var su_res: String = ""

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_su_details)



        su_res = intent.getStringExtra("su_userid").toString()

        // get views
        val su_name = findViewById<TextView>(R.id.su_name)
        val su_image = findViewById<ImageView>(R.id.su_image)

        // onClick launches another activity - if the image
        // hasn't loaded yet nothing happens
        su_image.setOnClickListener { viewCurrentImage() }

        // start the async loading right away - once it is loaded the
        // su_layout view will be visible and the view data
        // will be populated. It might be good to show a progress bar
        // while it's loading
        readData()
    }

    fun readData() {
        println("LOG: called readData")

        Toast.makeText(baseContext, su_res, Toast.LENGTH_LONG).show()

        println("LOG: getting data for ${su_res}")

        val suRef = FirebaseDatabase.getInstance()
            .getReference()
            .child("Users")
            .child(su_res)

        suRef.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                if (dataSnapshot.exists()) {
                    println("LOG: data snapshot exists")

                    su_layout.visibility = View.VISIBLE
                    val userData = dataSnapshot.getValue(profile_model::class.java)

                    currentImageUrl = userData?.getImageUrl() ?: ""
                    su_name.text = userData?.getnameOfsu() ?: ""

                    println("LOG: Got user data ${currentImageUrl}")

                    if (currentImageUrl.isNotEmpty()) {
                        Picasso.get()
                            .load(currentImageUrl)
                            .placeholder(R.drawable.ic_baseline_image_200)
                            .into(su_image)
                    }
                } else {
                    println("LOG: user not found in database")
                }
            }

            override fun onCancelled(error: DatabaseError) {
                println("LOG: cancelled")
            }
        })
    }

    private fun viewCurrentImage() {
        if (currentImageUrl.isEmpty()) return
            Toast.makeText(baseContext, currentImageUrl, Toast.LENGTH_LONG).show()
            val imageViewer = Intent(baseContext, ImageViewer::class.java)
            imageViewer.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            imageViewer.putExtra("su_image", currentImageUrl)
            startActivity(imageViewer)

    }
}

this related question 的最佳答案向您展示了如何进行回调,但这并没有真正回答如何使用异步数据的问题,并且与此类问题。

我没有发现您的回调有任何特别的错误 - 但它默默地吞下了许多可能的错误情况(例如,如果用户不存在)。下面的例子有一些打印语句,应该有助于更好地确定发生了什么。

比额外的回调接口更简洁的方法是 make a separate method 来处理异步结果。这是一个经过清理的示例,说明它的外观 - pseudo-code 您的示例的某些部分丢失了。为了帮助调试,如果您不理解代码的哪些部分是 运行,或者如果某些内容看起来不像您期望的那样,您应该养成使用日志或打印语句的习惯。

private var currentImageUrl: String = ""
private var userId: String = ""
private lateinit var su_name: TextView
private lateinit var su_image : ImageView
private lateinit var su_layout : ConstraintLayout

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_su_details)

    // get views
    su_name = findViewById<TextView>(R.id.su_name)
    su_image = findViewById<ImageView>(R.id.su_image)
    su_layout = findViewById<ConstraintLayout>(R.id.su_layout)

    su_layout.visibility = View.INVISIBLE

    // get user id from intent
    userId = intent.getStringExtra("su_userid").orEmpty()

    // TODO: Handle what to do if userId is empty here!
    if( userId.isEmpty() ) {
        finish()
    }

    // onClick launches another activity - if the image
    // hasn't loaded yet nothing happens
    su_image.setOnClickListener { viewCurrentImage() }

    // start the async loading right away - once it is loaded the
    // su_layout view will be visible and the view data 
    // will be populated. It might be good to show a progress bar
    // while it's loading
    startLoading()
}

private fun startLoading() {
    println("LOG: getting data for ${userId}")

    val suRef = FirebaseDatabase.getInstance()
                  .getReference()
                  .child("Users")
                  .child(userId)

    suRef.addValueEventListener(object : ValueEventListener {
        override fun onDataChange(dataSnapshot: DataSnapshot) {
            if(dataSnapshot.exists()) {
                println("LOG: data snapshot exists")
                val userData = dataSnapshot.getValue(profile_model::class.java)
                showData(userData)
            }
            else {
                println("LOG: user not found in database")
            }
        }

        override fun onCancelled(error: DatabaseError) {
            println("LOG: cancelled")
        }
    })
}

private fun showData(userData: profile_model?) {
    su_layout.visibility = View.VISIBLE

    currentImageUrl = userData?.getImageUrl() ?: ""
    su_name.text = userData?.getnameOfsu() ?: "Error"

    println("LOG: Got user data ${currentImageUrl}")

    if( currentImageUrl.isNotEmpty() ) {
        Picasso.get()
          .load(currentImageUrl)
          .placeholder(R.drawable.ic_baseline_image_200)
          .into(su_image)
    }
}

private fun viewCurrentImage() {
    if( currentImageUrl.isEmpty() ) return

    val imageViewer = Intent(this, suDetails::class.java)
    imageViewer.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    imageViewer.putExtra("su_image", currentImageUrl)
    startActivity(imageViewer)
}