【问题标题】:Retrieving double value from Android Room cause NullPointerExcpetion从 Android Room 检索双精度值导致 NullPointerException
【发布时间】:2021-08-10 01:35:23
【问题描述】:

我有一个带有下一个 POJO 的 android room DB

  Entity(tableName = "transactions")
    public class BoardItem {
        @PrimaryKey(autoGenerate = true)
        int itemID;
        String account;
        String date;
        String category;
        double value:
//getters and setters
    }

对于指定类别的所有值的负载总和,我尝试使用下一个DAO

@Query ("SELECT category, SUM (value) as total FROM transactions WHERE category =:transactionCategory GROUP BY category ")
double getAllTransactionInCategory(String transactionCategory);

以及存储库 android 房间中的下一个代码:

public class RoomTransactionsRepository {
    private final RoomTransactionsDAO mTransactionsDao;
    private final LiveData<List<TransactionBoardItem>> mAllTransactions;

    RoomTransactionsRepository(Application application){
            RoomTransactionsDatabase db = RoomTransactionsDatabase.getDatabase(application);
            mTransactionsDao = db.transactions_dao();
            mAllTransactions = mTransactionsDao.getAllTransactions();
    }

    LiveData<List<TransactionBoardItem>> getAllTransactions(){
        return mAllTransactions;
    }

    double getAllTransactionInCategory (String category){
        return mTransactionsDao.getAllTransactionInCategory (category);
    }

    //Livedata code...

但是在尝试以其他方式在文本视图中显示数据后,它会抛出下一个 NullPointerException,尽管 db 中有这样的数据(在 Android Studio db 检查器中检查):

2021-05-20 21:38:06.800 6673-6673/com.example.dbexample E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.dbexample, PID: 6673
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.dbexample/com.example.dbexample.transactions.TransactionStatisticActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'double com.example.dbexample.transactions.RoomTransactionsRepository.getAllTransactionInCategory(java.lang.String)' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3792)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3968)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2307)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:246)
        at android.app.ActivityThread.main(ActivityThread.java:8506)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1139)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'double com.example.dbexample.transactions.RoomTransactionsRepository.getAllTransactionInCategory(java.lang.String)' on a null object reference
        at com.example.dbexample.transactions.TransactionStatisticActivity.onCreate(TransactionStatisticActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:8198)
        at android.app.Activity.performCreate(Activity.java:8182)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3765)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3968) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2307) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:246) 

为了显示,我使用下一个代码:

public class StatisticActivity extends AppCompatActivity {
    RoomTransactionsRepository repository;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // init code 

         binding.totalValue.setText (String.valueOf (repository.getAllTransactionInCategory ("Food")));
    }
}

当我尝试显示要从数据库中提取的双精度值时,我在 DAO 或存储库代码中做错了什么导致 NullPointerException? 提前致谢!

更新:VievModel 类。它在单独的活动中用于将数据从 DB 动态插入到 RecyclerView 中。

public class RoomTransactionsViewModel extends AndroidViewModel {
    private final RoomTransactionsRepository mRepo;
    private final LiveData<List<TransactionItem>> mAllTransactions;

    public RoomTransactionsViewModel(Application application){
        super(application);
        mRepo = new RoomTransactionsRepository(application);
        mAllTransactions  = mRepo.getAllTransactions();
    }

    LiveData<List<TransactionBoardItem>> getAllTransactions(){
        return mAllTransactions;
    }

    public void insert(TransactionItem item){mRepo.insertTransaction(item);}
    public void deleteAll(){mRepo.deleteAllTransactions();}
    
}

更新 2:我修改了答案,但使用了稍微不同的方法来解决我的问题。
第 1 步:我为要提取的行创建 POJO:

public class TransactionStatisticItem {
    //`@ColumnInfo(name = "")` should equal to name of row in your DB
    @ColumnInfo(name = "value")
    double  transactionValue;//+

   //getters

    public TransactionStatisticItem(double transactionValue, String transactionCategory) {
        this.transactionValue = transactionValue;
        this.transactionCategory = transactionCategory;
    }
    @ColumnInfo(name = "category")
    String transactionCategory;//+
}  

第 2 步:我按照答案创建了界面,但将 double 替换为 POJO 类 void onReturned(TransactionStatisticItem item);
第 3 步:根据答案更新存储库代码,但有更改:

//To retrieve data asynchronous from DB you can use : ExecutorService (java) / Coroutines (kotlin) or AsyncTask (deprecated in API level 30)
 public static final ExecutorService singleThreadExecutor =
            Executors.newSingleThreadExecutor();

    void getAllTransactionInCategory (String category, TransactionStatisticListener listener){
            singleThreadExecutor.execute(() -> listener.onReturned(mTransactionsDao.getAllTransactionInCategory (category)));
        }

第 4 步:在 ViewModel 中,我添加下一行

 void getAllTransactionInCategory (String category, TransactionStatisticListener listener){
        mRepo.getAllTransactionInCategory (category, listener);
    }

第 5 步:最后在 Activity 中添加下一个代码:

RoomTransactionsViewModel model = new ViewModelProvider (this).get(RoomTransactionsViewModel.class);

        model.getAllTransactionInCategory("Food", item -> {
            binding.totalValue.setText (String.valueOf (item.getTransactionValue ()));
            binding.category.setText (String.valueOf (item.getTransactionCategory ()));
        });

最后,我能够检索到下一个示例数据:“食品”类别中所有交易的总和。

【问题讨论】:

  • 你能说明你在哪里初始化RoomTransactionsRepository 和 ViewModel 实例
  • @Zain 更新了,是这个吗? ViewModel 涉及单独的活动,以提供在 RecyclerView 中动态插入 POJO 项的可能性。

标签: java android database nullpointerexception android-room


【解决方案1】:
binding.totalValue.setText (String.valueOf (repository.getAllTransactionInCategory ("Food")));

为了遵循 Google MVVM 指南,您不应从 Activity 访问 Repository,因为它被视为数据库的一个层,不应由 View 处理,而是由 ViewModel 处理

另一个问题是你在不使用监听器的情况下从数据库返回一个双精度值;因为从数据库中取值需要一些时间,所以getAllTransactionInCategory() 不会返回预期值。这可以通过使用监听器接口来解决,或者返回一个LiveData&lt;Double&gt; 并在活动中观察它。

应用:

因此,建议通过ViewModel 访问它:

道:返回一个LiveData&lt;Double&gt;

@Query ("SELECT category, SUM (value) as total FROM transactions WHERE category =:transactionCategory GROUP BY category ")
LiveData<Double> getAllTransactionInCategory(String transactionCategory);

视图模型:

public class RoomTransactionsViewModel extends AndroidViewModel {
   
   // ...
    
    
    LiveData<Double> getAllTransactionInCategory (String category){
        return mRepo.getAllTransactionInCategory (category);
    }
}

存储库:

public class RoomTransactionsRepository {

    //.....
    
    LiveData<Double> getAllTransactionInCategory (String category){
        return mTransactionsDao.getAllTransactionInCategory (category);
    }

并观察活动中的LiveData

public class StatisticActivity extends AppCompatActivity {
    RoomTransactionsRepository repository;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // init code 
        
        
        RoomTransactionsViewModel model = new ViewModelProvider(this).get(RoomTransactionsViewModel.class);
        
        
        model.getAllTransactionInCategory("Food").observe(this, new Observer<Double>() {
            @Override
            public void onChanged(Double value) {
                binding.totalValue.setText (value);
            }
        });
        
      //  binding.totalValue.setText (String.valueOf (model.getAllTransactionInCategory ("Food")));
        
        
        
    }
}

更新:

“这可以通过使用监听器接口来解决”你能详细说明一下吗

要使用监听器,您需要一个具有回调方法的接口,该方法接受您希望从 Room 返回的类型,在本例中为 double

public interface TransactionListener {
     void onReturned(double count);
}

然后在Room返回值的时候使用这个接口的实现如下:

道:

@Query ("SELECT category, SUM (value) as total FROM transactions WHERE category =:transactionCategory GROUP BY category ")
double getAllTransactionInCategory(String transactionCategory);

视图模型:

public class RoomTransactionsViewModel extends AndroidViewModel {
   
   // ...
    
    
    void getAllTransactionInCategory (String category, TransactionListener listener){
        mRepo.getAllTransactionInCategory (category, listener);
    }
}

存储库:

public class RoomTransactionsRepository {

    //.....
    
    void getAllTransactionInCategory (String category, TransactionListener listener){
 
        listener.onReturned(mTransactionsDao.getAllTransactionInCategory (category));
    }

活动:

public class StatisticActivity extends AppCompatActivity {
    RoomTransactionsRepository repository;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // init code 
        
        
        RoomTransactionsViewModel model = new ViewModelProvider(this).get(RoomTransactionsViewModel.class);
        
            
        model.getAllTransactionInCategory("Food", new TransactionListener() {
            @Override
            public void onReturned(double count) {
                binding.totalValue.setText (String.valueOf (count));
            }
        });
        
    }
}

【讨论】:

  • 谢谢! “这可以通过使用监听器接口来解决”你能详细说明一下吗?我还更新了帖子,提供了有关我想要的更多信息。
  • @GremlinShX 请检查答案中的更新部分。
  • 谢谢您,我以您的回答作为回答我自己问题的基础,但是随着更改,您可以检查问题中的 update3。还有一件事,你有一个错误:ViewModel 类中的 void getAllTransactionInCategory 不应该使用“return”关键字。
猜你喜欢
  • 2012-11-07
  • 2012-11-03
  • 1970-01-01
  • 2012-06-19
  • 2021-11-21
  • 2016-07-15
  • 1970-01-01
  • 2013-02-25
  • 1970-01-01
相关资源
最近更新 更多