首页 > 解决方案 > Kotlin MVVM Room App 无法恢复数据

问题描述

我正在开发一个 Kotlin 应用程序,它将 Room 实现为数据库,并将 Retrofit 实现为 API 连接库。

我猜 RoomObject 已正确添加到数据库中,所以当我尝试恢复数据以打印到屏幕上时,我的适配器不会提升请求数据的 recycleView。

我尝试做的片段是这样的:

日记片段.kt


package com.example.mvvm_retrofit_imagesearchapp.ui.diary

import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.example.mvvm_retrofit_imagesearchapp.R
import com.example.mvvm_retrofit_imagesearchapp.adapter.diary.AdapterDiary
import com.example.mvvm_retrofit_imagesearchapp.data.UnsplashPhoto
import com.example.mvvm_retrofit_imagesearchapp.databinding.FragmentDiaryBinding
import com.example.mvvm_retrofit_imagesearchapp.room.PhotoDatabase
import com.example.mvvm_retrofit_imagesearchapp.room.RoomRepository
import com.example.mvvm_retrofit_imagesearchapp.room.RoomResponse
import com.google.firebase.auth.FirebaseAuth
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_diary.*
import kotlinx.coroutines.launch


@AndroidEntryPoint
class DiaryFragment : Fragment(R.layout.fragment_diary), AdapterDiary.OnItemClickListenerDiary{
    private lateinit var viewModel: DiaryViewModel
    private var _binding: FragmentDiaryBinding? = null
    private val binding get() = _binding!!
    private lateinit var user: String
    private lateinit var mAuth : FirebaseAuth
    private var adapter: AdapterDiary? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mAuth = FirebaseAuth.getInstance();
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        _binding = FragmentDiaryBinding.bind(view)
        //Para Instanciar el ViewModel, utilizo el Construtor de clase.
        viewModel = DiaryViewModel(RoomRepository(context!!, PhotoDatabase.getDatabase(context!!)!!))
        //Al  Listener le paso una instancia a partir del observer.
        //mientras que al listener del BtnRetry, le paso el atributo de clase.
        mAuth.addAuthStateListener {
            //TODO() Debug to see if the object is null
            if(null != it.currentUser){
                user = it.currentUser!!.email.toString()
                checkUser(user)
            }else{
                Toast.makeText(context, "User still be null", Toast.LENGTH_LONG).show()
            }
        }

        val btnRetryDiary = binding.btnRetryDiary
        btnRetryDiary.setOnClickListener(View.OnClickListener {
            user = mAuth.currentUser?.email.toString()
            checkUser(user)
        })

    }
    fun checkUser(user:String){

        if(user!="null"){
            btnRetryDiary.visibility=View.GONE
            Toast.makeText(context, "Already Sign in !", Toast.LENGTH_LONG).show()

           //TODO() El adapter Detail se queda a null..
            val db = PhotoDatabase.getDatabase(context!!)
            viewModel.getPhotoUser(user)!!.observe(viewLifecycleOwner){

                    lifecycleScope.launch {
                        searchPhoto(db!!, user)
                    }
            }
            setHasOptionsMenu(true)
        }else{
            Toast.makeText(context, "¡¡ User esta a null !!", Toast.LENGTH_LONG).show()
        }
    }

    //TODO("Compila correctamente pero llamada al DAO devuelve un arraylist vacio")
    private suspend fun searchPhoto(db: PhotoDatabase, user: String) {
        val data = db.photoDao().selectPhoto(user)
        //TODO Cargar la imagen a partir de la url.
            data.observe(viewLifecycleOwner, Observer {
                //Pass List of entities to adapter.
                adapter = AdapterDiary(context!!.applicationContext, it, this)
                binding.apply {
                    recycleViewDiary.setHasFixedSize(true)
                    recycleViewDiary.itemAnimator = null
                    recycleViewDiary.adapter = adapter
                }
            })
    }
    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }

    override fun onItemClick(photo: RoomResponse) {
        val action = DiaryFragmentDirections.actionDiaryFragmentToDetailFragment(photo as UnsplashPhoto)
        findNavController().navigate(action)
    }
}

我的数据类如下:

RoomUnplashPhoto.kt

package com.example.mvvm_retrofit_imagesearchapp.data

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import com.example.mvvm_retrofit_imagesearchapp.utils.Global

@Entity(tableName= Global.databaseName, indices = [Index(value = ["uid"], unique = true)])
data class RoomUnsplashPhoto(
    @PrimaryKey() val id: Int? = null,
    var uid: String,
    @ColumnInfo(name = "description") var description: String?,
    @ColumnInfo(name = "url") var url: String?,
    @ColumnInfo(name="user") var user:String?,
)

RoomResponse.kt

package com.example.mvvm_retrofit_imagesearchapp.room

import com.google.gson.annotations.SerializedName

class RoomResponse {
    @SerializedName("url")
    var url: String? = null
    @SerializedName("user")
    var user: String? = null
}

最后,我要求用户添加照片的片段是这样的:

细节片段.kt

@AndroidEntryPoint
class DetailFragment : Fragment(R.layout.fragment_detail){

    private val args by navArgs<DetailFragmentArgs>()
    private var userIdAdd: Int = 0
    private lateinit var db : PhotoDatabase
    //TODO(Insertar el user llamadno al DAO)
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val binding = FragmentDetailBinding.bind(view)
        binding.apply {
            val photo = args.photo
            var room_photo : RoomUnsplashPhoto? = null
            try{
                room_photo = RoomUnsplashPhoto(0, photo.id!!,photo.description,photo.urls.full, photo.user.name )
            }catch (e:NumberFormatException){
                Toast.makeText(context, "¡ Falla la conversion !", Toast.LENGTH_LONG).show()
                Log.d("NumberFormat", e.toString())
            }

            Glide.with(this@DetailFragment)
                .load(photo.urls.regular)
                .error(R.drawable.ic_error)
                .override((Target.SIZE_ORIGINAL)/4)
                .listener(object : RequestListener<Drawable> {
                    override fun onLoadFailed(e: GlideException?,model: Any?,target: Target<Drawable>?,isFirstResource: Boolean): Boolean {
                        progressBar.isVisible = false
                        return false
                    }

                    override fun onResourceReady(resource: Drawable?,model: Any?,target: Target<Drawable>?,dataSource: DataSource?,isFirstResource: Boolean): Boolean {
                        progressBar.isVisible = false
                        textviewDesc.isVisible = photo.description != null
                        textviewCreator.isVisible = true
                        return false
                    }

                })
                .into(imageView)

            textviewDesc.text = photo.description

            val uri = Uri.parse(photo.user.attributionUrl)
            val intent = Intent(Intent.ACTION_VIEW, uri)

            //set the listener to Room

            val db = context?.let {
                Room.databaseBuilder(
                    it.applicationContext,
                    PhotoDatabase::class.java, Global.databaseName
                ).build()
            }

            btnAdd.setOnClickListener(View.OnClickListener {

                //Pruebo a sacar el id del item introducido en un hilo aparte para
                // compararlo en un 2º hilo con la respuesta de la insercion que sera el uid.
                GlobalScope.launch {
                    userIdAdd = getUserAdd(db!!, room_photo!!)
                }
                GlobalScope.launch {
                    Looper.getMainLooper()

                    if (room_photo != null) {
                        var message:String = insertPhoto(room_photo, db)
                        activity?.runOnUiThread(Runnable {
                            Toast.makeText(context, message, Toast.LENGTH_LONG).show()
                        })

                    }else {
                        activity?.runOnUiThread(Runnable {
                            Toast.makeText(context, "El room_photo es null", Toast.LENGTH_LONG)
                                .show()
                        })
                    }
                }
            })

            textviewCreator.apply {
                text = "Photo by ${photo.user.name} on Unsplash"
                setOnClickListener {
                    context.startActivity(intent)
                }
                paint.isUnderlineText = true
            }
        }
    }

    private suspend fun getUserAdd(db:PhotoDatabase, room_photo: RoomUnsplashPhoto):Int {
        var addedId = 0
        withContext(Dispatchers.IO){
            addedId = db?.photoDao()?.insertPhoto(room_photo).toInt()
        }
        return addedId
    }

    fun insertPhoto(photo: RoomUnsplashPhoto, db: PhotoDatabase?): String {
        //Pass all the data to the RoomDatabase.
        var message: String = ""
        val resp = db?.photoDao()?.insertPhoto(RoomUnsplashPhoto(
            0,
            photo.uid,
            photo.description,
            photo.url,
            photo.user
        ))
        if (resp != null) {
            if(resp.absoluteValue.toInt() === (userIdAdd.absoluteValue)){
                message = "Foto añadida correctamente a su diario."

            }else{
                message = "No se ha podido añadir la foto a su diario"
            }
        }
        return message
    }
}

我希望你能提供帮助,如果这样,请提前感谢!

标签: androidkotlinandroid-room

解决方案


推荐阅读