我是Room和Kotlin的新手,我正在尝试构建一个简单的Room的数据库并将其呈现在ViewPager中的RecyclerView中。
该活动包含一个ViewPager对象,该ViewPager包含一个Fragment,并且该Fragment包含一个RecyclerView: 活动-> ViewPager->片段-> 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
}
}
答案 0 :(得分:1)
您需要了解多线程的概念,才能了解代码为何不起作用。简而言之,您将得到一个空列表,因为您试图在工作线程上运行的插入和查询尚未完成时试图在主线程中获取列表。
我在代码中添加了注释,以解释执行顺序。注释中的数字是完成的顺序:
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答案中解释所有这些解决方案。我希望您能有所了解,并鼓励您探索其他解决方案。