【问题标题】:Calling a Fragment method from a singleton从单例调用 Fragment 方法
【发布时间】: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")

标签: android kotlin


【解决方案1】:

我找到了解决办法。

这与调用DbUtil.getItems()itemsListener未初始化有关,这需要一个非空的ItemsListener来调用它的接口方法-onItemsChange()

解决方法是在调用DbUtil.getItems之前设置DbUtil.itemsListener。我更新了片段中的onAttach() 方法:

override fun onAttach(context: Context?) {
    super.onAttach(context)
    DbUtil.itemsListener = this
    DbUtil.getItems()
}

【讨论】:

    猜你喜欢
    • 2019-09-29
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多