【发布时间】:2018-04-18 23:13:34
【问题描述】:
在我的 Android 应用程序中,我有一个 Singleton (kotlin object),专门处理数据库操作(用于 Firebase 实时数据库)。在我的主要活动中,我设置了一个带有自定义片段的ViewPager,创建了一个多窗格布局。
其中一个片段包含一个RecyclerView,当数据库 Singleton 检测到数据发生变化时需要更新它。
我的逻辑是在我的数据库 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 并尝试使用我的单例和片段进行设置,但这解决了同样的问题。
【问题讨论】:
-
问题似乎在您的 MainActivity 第 18 行:
com.jagoancoding.foodstalladmin.MainActivity.onCreate(MainActivity.kt:18)。您也可以发布该代码吗? -
@gsb:我已经更新了。我之前省略了
: DatabaseListener,因为我认为这不是问题,现在我认为我的实现有问题? -
MainActivity.kt:18 是
DbUtil.getItems("shop")