从单例调用Fragment方法

时间:2018-04-18 23:13:34

标签: android kotlin

在我的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和片段进行设置,但这解决了同样的问题。

1 个答案:

答案 0 :(得分:0)

我找到了解决方案。

调用http.get时,itemsListener没有初始化,这需要非空DbUtil.getItems()来调用其接口方法 - ItemsListener

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

onAttach()