首页 > 解决方案 > Transformations.map 和 MediatorLiveData 使用空对象引用使应用程序崩溃

问题描述

不知道发生了什么。我正在运行两个查询,然后使用 MediatorLiveData 和 Transformations.map 进行合并和转换。我将这个几乎完全相同的代码用于其他两个查询,没有问题。但是当我将它用于这些特定查询时,应用程序在开始时崩溃并出现以下错误。

请注意:我已经尝试观察 MediatorLiveData,我可以得到两个查询的结果而不会出错。只有当我尝试通过 Transformations.map 运行它们时,才会出现错误和应用程序崩溃。

这是我用来测试运行良好的 MediatorLiveData 的观察结果:

    viewModel.getAllValidEventsLiveDataMerger().observe(this, new Observer<AllValidEventsSnapshot>() {
            @Override
            public void onChanged(@Nullable AllValidEventsSnapshot allValidEventsSnapshot) {
                if (allValidEventsSnapshot.isComplete()) {
                    List<Event> nonRepeatEventList;
                    List<Event> repeatEventList;
                    List<Event> eventList = new ArrayList<>();
                    nonRepeatEventList = allValidEventsSnapshot.getValidNonRepeatableEventsSnapshot().toObjects(Event.class);
                    repeatEventList = allValidEventsSnapshot.getValidRepeatableEventsSnapshot().toObjects(Event.class);
                    eventList.addAll(nonRepeatEventList);
                    eventList.addAll(repeatEventList);
                    Log.d(TAG, "EVENTLIST: " + eventList.toString());
                }
            }
        }); 

这是通过 Transformations.map 的观察结果:

    viewModel.getAllValidEventsLiveData().observe(this, new Observer<List<Event>>() {
            @Override
            public void onChanged(@Nullable List<Event> eventList) {
                if (eventList != null) {
                    Log.d(TAG, "EventList: " + eventList.toString());
                }
            }
        }); 

这是代码:

ViewModel.java

private static final String TAG = "ViewModel";

private FirebaseRepository repository = new FirebaseRepository(getApplication());

public ViewModel(@NonNull Application application) {
    super(application);
}

    /*
     *
     * Selected Device Events Live Data
     *
     */

    private FirebaseQueryLiveData selectedDeviceEventsLiveData = new FirebaseQueryLiveData(repository.getSelectedDeviceEventsQuery());

    @NonNull
    public FirebaseQueryLiveData getSelectedDeviceEventsLiveData() {
        return selectedDeviceEventsLiveData;
    }

    /*
     *
     * Selected device nonRepeat valid events Live Data
     *
     */

    private FirebaseQueryLiveData validNonRepeatEventsLiveData = new FirebaseQueryLiveData(repository.getValidNonRepeatEventsQuery());

    @NonNull
    public FirebaseQueryLiveData getValidNonRepeatEventsLiveData() {
        return validNonRepeatEventsLiveData;
    }

    /*
     *
     * MediatorLiveData that merges all valid events (repeatable and nonRepeatable)
     *
     */

    //MediatorLiveData method that merges all valid repeatable and nonRepeatable liveData
    //into a eventList of Event Objects
    private MediatorLiveData<AllValidEventsSnapshot> allValidEventsLiveDataMerger() {
        final MediatorLiveData<AllValidEventsSnapshot> mediatorLiveData = new MediatorLiveData<>();
        final AllValidEventsSnapshot current = new AllValidEventsSnapshot();
        mediatorLiveData.addSource(validRepeatEventsLiveData, new Observer<QuerySnapshot>() {
            @Override
            public void onChanged(@Nullable QuerySnapshot querySnapshot) {
                current.setValidRepeatableEventsSnapshot(querySnapshot);
                mediatorLiveData.setValue(current);
            }
        });
        mediatorLiveData.addSource(validNonRepeatEventsLiveData, new Observer<QuerySnapshot>() {
            @Override
            public void onChanged(@Nullable QuerySnapshot querySnapshot) {
                current.setValidNonRepeatableEventsSnapshot(querySnapshot);
                mediatorLiveData.setValue(current);
            }
        });
        return mediatorLiveData;
    }

    //Accessor method to get the result of the mediatorMerge
    public MediatorLiveData<AllValidEventsSnapshot> getAllValidEventsLiveDataMerger() {
        return allValidEventsLiveDataMerger();
    }

    /*
     *
     * Transforming the MediatorLiveData that merges repeatable and nonRepeatable valid events
     * into one combined eventList
     *
     */

    //live data that transforms our MediatorLiveData repeatable and nonRepeatable eventList
    private final LiveData<List<Event>> allValidEventsLiveData =
            Transformations.map(allValidEventsLiveDataMerger(), new GetAllValidEvents());

    //sub-class that implements Function to convert our two valid eventLists into one
    private class GetAllValidEvents implements Function<AllValidEventsSnapshot, List<Event>> {

        @Override
        public List<Event> apply(AllValidEventsSnapshot input) {
            List<Event> eventList = new ArrayList<>();
            if (input != null && input.isComplete()) {
                List<Event> repeatEventList = input.getValidRepeatableEventsSnapshot().toObjects(Event.class);
                List<Event> nonRepeatEventList = input.getValidNonRepeatableEventsSnapshot().toObjects(Event.class);

                //merge valid repeat and nonRepeatable event lists
                eventList.addAll(repeatEventList);
                eventList.addAll(nonRepeatEventList);
            }
            return eventList;
        }
    }

    //accessor method to get our transformed users/devices/admin live data into the NavDrawer object
    public LiveData<List<Event>> getAllValidEventsLiveData() {
        return allValidEventsLiveData;
    } 

查询:

        //getValidNonRepeatEvents query using FirebaseQueryLiveData class
    public Query getValidNonRepeatEventsQuery () {
        query = FirebaseFirestore.getInstance()
                .collection("devices")
                .document(docID)
                .collection("events")
                .whereGreaterThanOrEqualTo("eventDate", firstDayThisWeekObj);

        return query;
    }

    //getValidRepeatEvents query using FirebaseQueryLiveData class
    public Query getValidRepeatEventsQuery () {
        query = FirebaseFirestore.getInstance()
                .collection("devices")
                .document(docID)
                .collection("events")
                .whereEqualTo("repeats", true);

        return query;
    } 

AllValidEventsSnapshot.java 对象类

   public class AllValidEventsSnapshot {

    private QuerySnapshot validRepeatableEventsSnapshot;
    private QuerySnapshot validNonRepeatableEventsSnapshot;

    //default constructor
    public AllValidEventsSnapshot() {
    }

    public QuerySnapshot getValidRepeatableEventsSnapshot() {
        return validRepeatableEventsSnapshot;
    }

    public void setValidRepeatableEventsSnapshot(QuerySnapshot validRepeatableEventsSnapshot) {
        this.validRepeatableEventsSnapshot = validRepeatableEventsSnapshot;
    }

    public QuerySnapshot getValidNonRepeatableEventsSnapshot() {
        return validNonRepeatableEventsSnapshot;
    }

    public void setValidNonRepeatableEventsSnapshot(QuerySnapshot validNonRepeatableEventsSnapshot) {
        this.validNonRepeatableEventsSnapshot = validNonRepeatableEventsSnapshot;
    }

    public boolean isComplete() {
        return (validRepeatableEventsSnapshot != null && validNonRepeatableEventsSnapshot != null);
    }
} 

FirebaseQueryLiveData.java

    public class FirebaseQueryLiveData extends LiveData<QuerySnapshot> {
    public static final String TAG = "FbaseQueryLiveData";

    private Query query;
    private final MyValueEventListener listener = new MyValueEventListener();
    private ListenerRegistration listenerRegistration;

    private boolean listenerRemovePending = false;
    private final Handler handler = new Handler();

    public FirebaseQueryLiveData(Query query) {
        this.query = query;
    }

    private final Runnable removeListener = new Runnable() {
        @Override
        public void run() {
            listenerRegistration.remove();
            listenerRemovePending = false;
        }
    };

    @Override
    protected void onActive() {
        super.onActive();

        Log.d(TAG, "onActive");
        if (listenerRemovePending) {
            handler.removeCallbacks(removeListener);
        }
        else {
            listenerRegistration = query.addSnapshotListener(listener);
        }
        listenerRemovePending = false;
    }

    @Override
    protected void onInactive() {
        super.onInactive();

        Log.d(TAG, "onInactive: ");
        // Listener removal is schedule on a two second delay
        handler.postDelayed(removeListener, 2000);
        listenerRemovePending = true;
    }

    private class MyValueEventListener implements EventListener<QuerySnapshot> {
        @Override
        public void onEvent(@Nullable QuerySnapshot querySnapshot, @Nullable FirebaseFirestoreException e) {
            if (e != null){
                Log.e(TAG, "Can't listen to query snapshots: " + querySnapshot + ":::" + e.getMessage());
                return;
            }
            setValue(querySnapshot);
        }
    }
}

下面是我得到的 traceStack 错误:

    2018-12-16 14:29:15.238 27558-27558/com.vuedeu.vuedeu E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.vuedeu.vuedeu, PID: 27558
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.vuedeu.vuedeu/activities.MainActivity}: java.lang.RuntimeException: Cannot create an instance of class viewModels.ViewModel
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6119)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
     Caused by: java.lang.RuntimeException: Cannot create an instance of class viewModels.ViewModel
        at android.arch.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:207)
        at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:134)
        at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:102)
        at activities.MainActivity.onCreate(MainActivity.java:193)
        at android.app.Activity.performCreate(Activity.java:6679)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6119) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:430)
        at android.arch.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:199)
        at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:134) 
        at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:102) 
        at activities.MainActivity.onCreate(MainActivity.java:193) 
        at android.app.Activity.performCreate(Activity.java:6679) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6119) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Object.equals(java.lang.Object)' on a null object reference
        at android.arch.core.internal.SafeIterableMap.get(SafeIterableMap.java:47)
        at android.arch.core.internal.SafeIterableMap.putIfAbsent(SafeIterableMap.java:65)
        at android.arch.lifecycle.MediatorLiveData.addSource(MediatorLiveData.java:87)
        at viewModels.ViewModel.allValidEventsLiveDataMerger(ViewModel.java:102)
        at viewModels.ViewModel.<init>(ViewModel.java:126)
        at java.lang.reflect.Constructor.newInstance0(Native Method) 
        at java.lang.reflect.Constructor.newInstance(Constructor.java:430) 
        at android.arch.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:199) 
        at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:134) 
        at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:102) 
        at activities.MainActivity.onCreate(MainActivity.java:193) 
        at android.app.Activity.performCreate(Activity.java:6679) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154)  

任何解决此问题的帮助将不胜感激!

谢谢!

标签: androidgoogle-cloud-firestoreandroid-architecture-components

解决方案


解决了!因此,由于我的 queryLiveData 方法放置在 Transformations.map 方法之后,因此似乎导致了空指针异常。在上面的示例中,为了清楚起见,我将 queryLiveData 方法放在了 Transformations.map 之上(删除了过程中不相关的方法),但没有意识到我已经以不同的顺序放置了这些方法。

所以,总结一下:上面的代码没有问题。我只需要将 queryLiveData 方法放在 Transforms.map 方法之上(实际上如上所示)。关于订单的奇怪之处在于 mediatorLiveData 似乎并不关心订单,但 Transformations.map 却关心。


推荐阅读