Просмотр базы данных комнат с помощью RecyclerView - PullRequest
0 голосов
/ 02 марта 2019

Я новичок в Room и Kotlin и пытаюсь создать простую базу данных Room и представить ее в RecyclerView, который находится в ViewPager.

Activity содержит объект ViewPager, который ViewPagerсодержит фрагмент, и этот фрагмент содержит RecyclerView: Activity -> ViewPager -> Fragment -> RecyclerView

Проблемы, с которыми я сталкиваюсь, состоят в том, что я получаю нулевое значение, когда пытаюсь получить базу данных (послевставка).

код:

@Entity(tableName = "Guests_table")
data class Guest(@NonNull @ColumnInfo (name = "Name") var name: String,
             @ColumnInfo (name = "Phone number") var phoneNumber: String,
             @ColumnInfo (name = "Coming") var coming: Boolean = false,
             @ColumnInfo (name = "Participants") var participants: Int)
{
@PrimaryKey (autoGenerate = true)
var id: Long = 0

init
{
    phoneNumber = PhoneNumberUtils.formatNumber(phoneNumber, Locale.getDefault().country)
}
}

dao:

@Dao
interface GuestsDAO
{
    @Insert
    fun insert(guest: Guest)

    @Delete
    fun delete(guest: Guest)

    @Query ("DELETE FROM Guests_table")
    fun deleteAll()

    @Query ("SELECT * FROM Guests_table ORDER BY name ASC")
    fun getAllGuests(): List<Guest>
}

база данных:

@Database(entities = [Guest::class], version = 1)
abstract class InviterRoomDatabase: RoomDatabase()
{
    abstract fun guestsDao(): GuestsDAO

    companion object
    {
        private var INSTANCE: InviterRoomDatabase ?= null

        fun getDatabase(context: Context): InviterRoomDatabase?
        {
            if (INSTANCE == null)
            {
                synchronized(InviterRoomDatabase::class)
                {
                    INSTANCE = Room.databaseBuilder(context.applicationContext,InviterRoomDatabase::class.java,"Guests.db").build()
                }
            }

            return INSTANCE
        }

        fun destroyInstance()
        {
            INSTANCE = null
        }
    }
}

активность:

class EventActivity : AppCompatActivity()
{
    private lateinit var viewPager: ViewPager

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

        viewPager = findViewById(R.id.guests_view_pager)
        val pagerAdapter =  EventPagerAdapter(supportFragmentManager)
        viewPager.adapter = pagerAdapter
    }
}

адаптер:

class EventPagerAdapter(fragmentManager: FragmentManager): FragmentPagerAdapter(fragmentManager)
{

    override fun getCount(): Int
    {
        return 1
    }

    override fun getItem(position: Int): Fragment
    {
        return GuestListFragment.newInstance()
    }
}

фрагмент:

class GuestListFragment : Fragment()
{
    private var guestsDataBase: InviterRoomDatabase? = null
    private lateinit var dbWorkerThread: DBWorkerThread

    companion object
    {
        fun newInstance(): GuestListFragment
        {
            val fragment = GuestListFragment()
            return fragment
        }
    }


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? 
    {

        dbWorkerThread = DBWorkerThread("workerThread")
        dbWorkerThread.start()
        guestsDataBase = InviterRoomDatabase.getDatabase(this.context!!)
        insertGuestToDB(Guest("Joe","052352332",false,0))

        var rootView = inflater.inflate(R.layout.fragment_guest_list, container, false)
        var rv: RecyclerView = rootView.findViewById(R.id.guests_list_recycler_view)
        rv.layoutManager = LinearLayoutManager(context)

        var d = getAllDataFromDB()
        rv.adapter = GuestsRecycleViewAdapter.newInstance(d)

        return rootView
    }


    private fun insertGuestToDB(guest: Guest)
    {
        val task = Runnable {guestsDataBase?.guestsDao()?.insert(guest)}
        dbWorkerThread.postTask(task)
    }

    private fun getAllDataFromDB(): List<Guest>
    {
        var data: List<Guest> = emptyList()
        val task = Runnable { data = guestsDataBase?.guestsDao()?.getAllGuests()!!}

        dbWorkerThread.postTask(task)

        return data
    }
}

1 Ответ

0 голосов
/ 03 марта 2019

Вам необходимо понять концепцию многопоточности, чтобы понять, почему ваш код не работает.Вкратце, вы получаете пустой список, потому что вы пытаетесь получить список в главном потоке, в то время как вставка и выполнение запроса в рабочем потоке еще не завершены.

Я поместил комментарий в код, чтобы объяснитьпорядок исполнения.Число в комментарии является порядком завершения:


override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? 
{
    // [1] onCreateView starts on main thread.
    dbWorkerThread = DBWorkerThread("workerThread")
    dbWorkerThread.start()
    guestsDataBase = InviterRoomDatabase.getDatabase(this.context!!)
     // [2] calls insertGuestToDB on main thread.
    insertGuestToDB(Guest("Joe","052352332",false,0))

    var rootView = inflater.inflate(R.layout.fragment_guest_list, container, false)
    var rv: RecyclerView = rootView.findViewById(R.id.guests_list_recycler_view)
    rv.layoutManager = LinearLayoutManager(context)

    // [4] calls getAllDataFromDB on main thread.
    var d = getAllDataFromDB()
    // [6] get "d" on main thread. "d" is an empty list. 
    rv.adapter = GuestsRecycleViewAdapter.newInstance(d)

    return rootView
}


private fun insertGuestToDB(guest: Guest)
{
    // [2] insertGuestToDB executed on main thread.
    val task = Runnable {
        guestsDataBase?.guestsDao()?.insert(guest)
        // [7] insertion finished on worker thread.     
    }
    dbWorkerThread.postTask(task)
    // [3] insertGuestToDB finishes on main thread.
}

private fun getAllDataFromDB(): List<Guest>
{
    // [4] getAllDataFromDB on main thread. "data" points to an empty list
    var data: List<Guest> = emptyList()
    val task = Runnable {
        data = guestsDataBase?.guestsDao()?.getAllGuests()!!
        // [8] query finishes on worker thread, but "data" is now lost.
    }
    dbWorkerThread.postTask(task)

    // [5] returns on main thread. "data" still points to an empty list
    return data
}


Вам нужно дождаться завершения dbWorkerThreadсвою работу перед назначением data списка adapter.Взгляните на одно из самых простых решений:


var rv: RecyclerView

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? 
{

    dbWorkerThread = DBWorkerThread("workerThread")
    dbWorkerThread.start()

    var rootView = inflater.inflate(R.layout.fragment_guest_list, container, false)
    rv= rootView.findViewById(R.id.guests_list_recycler_view)
    rv.layoutManager = LinearLayoutManager(context)

    guestsDataBase = InviterRoomDatabase.getDatabase(this.context!!)
    insertGuestToDbAndUpdateRv(Guest("Joe","052352332",false,0))

    return rootView
}


private fun insertGuestToDbAndUpdateRv(guest: Guest)
{
    val task = Runnable {
        // 1. Insert on a worker thread.
        guestsDataBase?.guestsDao()?.insert(guest)

        // 2. Query on a worker thread.
        var data = guestsDataBase?.guestsDao()?.getAllGuests()!!
        this@GuestListFragment.getActivity().runOnUiThread {
            // 3. Once they are done, update ui on main thread.
            rv.adapter = GuestsRecycleViewAdapter.newInstance(data)
        }
    }
    dbWorkerThread.postTask(task)
}


Имейте в виду, что это определенно не лучший способ решения этой проблемыпроблема.Это очень распространенная проблема, и существует множество различных решений, которые гораздо более удобочитаемы, гибки и удобны в обслуживании.Просто другие решения требуют более глубокого понимания, а многопоточность - это настолько широкая тема, что я не могу объяснить все из них в одном ответе SO.Я надеюсь, что вы получите общую идею, и призываю вас изучить другие решения для этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...