【问题标题】:Android LiveData null error when trying to update object尝试更新对象时出现 Android LiveData null 错误
【发布时间】:2018-01-22 15:44:05
【问题描述】:

我正在寻求帮助以更好地了解 Android 中的 LiveData。

我创建了一个应用程序,允许用户在数据库中查看、编辑或创建条目。

我在表单活动上使用了视图模型来在活动和片段之间共享信息,并使数据生命周期感知。

当我从数据库加载条目时,代码运行良好。任何更改都会被捕获并保存回数据库

但是,当我想使用我得到的相同代码创建一个新条目时 原因:java.lang.NullPointerException:当我尝试更新视图模型中的对象时,尝试调用虚拟方法...空对象引用。下面的示例尝试在用户输入新名称时设置名称值:

public void afterTextChanged(Editable s) {
    patternViewModel.getPattern().getValue().setName(s.toString());
    ((FlyPatternDetailActivity)getActivity()).setHasChanged(true);

    ((FlyPatternDetailActivity)getActivity()).setActionBar(s.toString());
}

我的问题是如果我没有从我的数据库中取回对象并且我将创建一个新条目,如何更新我的实体的 livedata 对象。

我的视图模型是:

public class PatternViewModel extends AndroidViewModel {

    private PatternRepository repo;

    private LiveData<FlyPattern> mObservablePattern;


    public PatternViewModel(@NonNull Application application) {
        super(application);
        if (mObservablePattern == null) {
            mObservablePattern = new MutableLiveData<>();
        }
        repo = PatternRepository.getInstance(((FlyTyingJournalApplicationClass) application).getDatabase());

    }


    public void loadPattern(final long id){
        if(id != -1)
            mObservablePattern = repo.getPattern(id);

    }
    public LiveData<FlyPattern> getPattern(){

        return mObservablePattern;
    }


    public void insertPattern() {
        repo.insertPattern(mObservablePattern.getValue());
    }

    public void updateFlyPattern(){
        repo.updatePattern(mObservablePattern.getValue());
    }

    public void deleteFlyPattern(){

        repo.deletePattern(mObservablePattern.getValue());
    }
}

我明白为什么会收到 nullpointException。

我的问题是如何实例化我的 mObservablePattern 以便我可以更新它。

如果 loadPattern 返回一个对象,它就会被实例化。

如果 loaddata 没有运行或确实从数据库返回了一个对象,那么我无法更新 mObservablePattern。

来自错误的堆栈跟踪:

 Process: com.rb.android.flytyingjournal, PID: 4957
  java.lang.NullPointerException: Attempt to invoke virtual method 'void com.rb.android.flytyingjournal.entities.FlyPattern.setType(java.lang.String)' on a null object reference
      at com.rb.android.flytyingjournal.flypatterndetails.PatternDetailsFragment$5.onItemSelected(PatternDetailsFragment.java:325)
      at android.widget.AdapterView.fireOnSelected(AdapterView.java:931)
      at android.widget.AdapterView.dispatchOnItemSelected(AdapterView.java:920)
      at android.widget.AdapterView.-wrap1(AdapterView.java)
      at android.widget.AdapterView$SelectionNotifier.run(AdapterView.java:890)
      at android.os.Handler.handleCallback(Handler.java:751)
      at android.os.Handler.dispatchMessage(Handler.java:95)
      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)

【问题讨论】:

  • 包括NullPointerException的完整堆栈跟踪
  • 不不重复。我知道我正在获得 NPE。我的问题是如果我没有从数据库中获取条目,如何实例化 Livedata 对象,然后我可以使用相同的视图模型创建一个新条目。

标签: android nullpointerexception android-livedata


【解决方案1】:

您正试图通过NULL 呼叫setName()。因为patternViewModel.getPattern().getValue() 返回保存在LiveData 中的值,在您的情况下可能是NULL。您可以添加一个空检查:

 if (patternViewModel.getPattern().getValue() == null) {
    FlyPattern flyPattern = new FlyPattern();
    flyPattern.setName("foo");
    patternViewModel.getPattern().setValue(flyPattern);
 } else {
    patternViewModel.getPattern().getValue().setName("foo");
 }

或者您可以在 ViewModel 中创建一个函数,例如setFlyPatternName() 并使用它来更新您的数据库。
在你的PatternViewModel.java

public void setFlyPatternName(String name) {
    if (mObservablePattern.getValue == null) {
        FlyPattern flyPattern = new FlyPattern();
        flyPattern.setName(name);
        mObservablePattern.setValue(flyPattern);
        repo.insertFlyPattern();
    } else {
        mObservablePattern.getValue().setName(name);
        repo.updateFlyPattern();
    }
}

编辑:这样做的正确方法实际上有点不同。 通常,您的存储库函数需要在后台线程上工作,因为您正在处理 i/o,但是如果您希望它们在 mainThread 上工作,您至少需要创建一个回调并将该回调对象传递给您的存储库函数,这将在插入/更新/删除数据时调用。而当回调函数被调用时,您需要在LiveData 上调用setValue() 并将数据设置为您的livedata 对象。

创建回调接口:

public interface InsertCallback {
    void onDataInserted(FlyPattern flyPattern);

    void onDataInsertFailed();
}

更改您的存储库插入函数的主体以接受 Callback 对象作为参数

public void insertFlyPattern(FlyPattern flyPattern, InsertCallback insertCallback) {
    // Do your insertion and if it is successful call insertCallback.onDataInserted(flyPattern), otherwise call insertCallback.onDataInsertFailed(); 
}

在您的 ViewModel 中实现 InsertCallback 及其方法

public class PatternViewModel extends AndroidViewModel implements InsertCallback {
//....

public void onDataInserted(FlyPattern flyPattern) {
    mObservablePattern.setValue(flyPattern);         
}

public void onDataInsertFailed() { 
    //Show error message
} 

}

【讨论】:

  • 由于 setValue 受保护且不能与 livedata 一起使用,因此无法解决此问题。
  • 改用MutableLiveData
  • 我做了修复我的新条目问题。但是现在我的数据没有加载到表单中。
  • 这是因为你需要调用setValue() 否则LiveData 的观察者不会被调用。我将编辑我的帖子并添加正确的做法。
  • 加载数据时如何回调?因此,如果找到分配给 MutableLiveData 的条目,则加载数据
猜你喜欢
  • 2023-01-30
  • 1970-01-01
  • 2015-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多