在我的Android应用中,我有一个Singleton(kotlin object
)专门处理数据库操作(适用于Firebase实时数据库)。在我的主要活动中,我使用自定义片段设置了ViewPager
,创建了一个多窗格布局。
当数据库Singleton检测到数据发生变化时,其中一个片段包含需要更新的RecyclerView
。
我的逻辑是在我的数据库Singleton中创建interface
,并使片段实现该接口。但我无法做到这一点。我收到这个错误:
java.lang.ExceptionInInitializerError
at com.jagoancoding.foodstalladmin.MainActivity.onCreate(MainActivity.kt:18)
at android.app.Activity.performCreate(Activity.java:5933)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2282)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2391)
at android.app.ActivityThread.access$900(ActivityThread.java:147)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1296)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5256)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Object.hashCode()' on a null object reference
at com.google.android.gms.internal.zzejp.hashCode(Unknown Source)
at java.util.Collections.secondaryHash(Collections.java:3405)
at java.util.HashMap.get(HashMap.java:300)
at com.google.android.gms.internal.zzeju.zzi(Unknown Source)
at com.google.firebase.database.Query.zzb(Unknown Source)
at com.google.firebase.database.Query.addValueEventListener(Unknown Source)
at com.jagoancoding.foodstalladmin.DbUtil.<clinit>(DbUtil.kt:22)
这是我的单身人士:
object DbUtil : DatabaseListener {
const val TAG = "DatabaseUtil"
lateinit var itemsListener: ItemsListener
interface ItemsListener {
fun onItemsChange(items: ArrayList<Item>)
}
val rootRef: DatabaseReference = FirebaseDatabase.getInstance().reference
val shopRef: DatabaseReference = rootRef.child(DataUtil.ROOT_SHOP).apply {
addValueEventListener(readFoodItemsListener)
}
val usersRef: DatabaseReference = rootRef.child(DataUtil.ROOT_USERS)
// Automatically update local list when data is changed upstream
private val readFoodItemsListener = object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot?) {
if (p0!!.exists()) {
p0.children.mapNotNullTo(DataUtil.allFoodItems) { it.getValue(Item::class.java) }
//TODO: Update Items list in fragment
itemsListener.onItemsChange(DataUtil.allFoodItems)
}
}
override fun onCancelled(p0: DatabaseError?) {
Log.e(TAG, "shopRef:onCancelled: ", p0?.toException())
}
}
override fun getItems(dataRoot: String) {
// Get Database food items to local list
shopRef.addListenerForSingleValueEvent(readFoodItemsListener)
}
override fun onItemAdd(item: Item) {
// Set up the unique ID
val itemId = shopRef.child(item.name).push().key
shopRef.child(itemId).setValue(Item(itemId, item.name, item.price))
}
override fun onItemUpdate(item: Item, name: String, price: Double) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onItemDelete(item: Item) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
// ... other methods ...
}
我的片段:
class ItemsPage : Fragment(), DbUtil.ItemsListener {
val LOG_TAG = "ItemsPageFragment"
private lateinit var itemsRV: RecyclerView
private lateinit var itemFAB: FloatingActionButton
private lateinit var loadPB: ProgressBar
private lateinit var mAdapter: ItemsAdapter
private var items = arrayListOf<Item>()
override fun onAttach(context: Context?) {
super.onAttach(context)
DbUtil.itemsListener = this
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val rootView = inflater.inflate(R.layout.items_page, container, false)
mAdapter = ItemsAdapter(items)
itemsRV = rootView.findViewById<RecyclerView>(R.id.rv_items).apply {
adapter = mAdapter
layoutManager = LinearLayoutManager(rootView.context)
}
itemFAB = rootView.findViewById<FloatingActionButton>(R.id.fab_add_item).apply {
setOnClickListener { DbUtil.onItemAdd(DataUtil.demoFood[0]) }
}
loadPB = rootView.findViewById<ProgressBar>(R.id.pb_items).apply {
visibility = View.GONE
}
return rootView
}
override fun onItemsChange(items: ArrayList<Item>) {
loadPB.visibility = View.VISIBLE
this.items = items
mAdapter.notifyItemRangeChanged(0, items.size - 1)
loadPB.visibility = View.GONE
}
}
我的活动:
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
// Fragments
private lateinit var mPager: ViewPager
private lateinit var mAdapter: PageAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DbUtil.getItems("shop")
mAdapter = PageAdapter(supportFragmentManager)
mPager = findViewById<ViewPager>(R.id.vp_main).apply { adapter = mAdapter }
}
}
我尝试在我的Activity上实现接口并以这种方式调用我的Fragment方法,但是我得到了与上面相同的错误。
我还阅读this answer并尝试使用我的Singleton和片段进行设置,但这解决了同样的问题。
答案 0 :(得分:0)
我找到了解决方案。
调用http.get
时,itemsListener
没有初始化,这需要非空DbUtil.getItems()
来调用其接口方法 - ItemsListener
。
解决方案是在调用onItemsChange()
之前设置DbUtil.itemsListener
。我在片段中更新了DbUtil.getItems
方法:
onAttach()