带有空对象引用的Transformations.map和MediatorLiveData崩溃应用

时间:2018-12-16 23:53:29

标签: android google-cloud-firestore android-architecture-components

不确定发生了什么。我正在运行两个查询,然后使用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)  

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

谢谢!

1 个答案:

答案 0 :(得分:2)

解决了!看来是由于我的queryLiveData方法放在Transformations.map方法之后而引起了空指针异常。在上面的示例中,为了清楚起见,我将queryLiveData方法放在Transformations.map上方(删除了流程中不相关的方法),但没有意识到我将这些方法放在了不同的顺序中。

因此,总而言之:上面的代码可以正常工作。我只需要将queryLiveData方法放在Transforms.map方法之上(上面实际上已显示)。关于顺序的奇怪之处在于,mediatorLiveData似乎并不关心顺序,而Transformations.map确实关心。