从 Android 房间中检索双精度值导致 NullPointerException
Retrieving double value from Android Room cause NullPointerExcpetion
我有一个 android 房间 DB 和下一个 POJO
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,尽管数据库中存在此类数据(已在 Android Studio 数据库检查器中检查):
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 class。它单独使用 activity 将数据库中的数据动态插入 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 步:我在回答中创建了界面,但用 POJO 替换了 double class 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 ()));
});
最后,我能够检索到下一个示例数据:“食品”类别中所有交易的总和。
binding.totalValue.setText (String.valueOf (repository.getAllTransactionInCategory ("Food")));
为了遵循 Google MVVM 准则,您不应从 activity 访问存储库,因为它被视为数据库的一个层,不应由视图处理,而应由 ViewModel
另一个问题是您 return 在不使用侦听器的情况下从数据库中获取双精度值;由于从数据库中获取值需要一些时间,因此 getAllTransactionInCategory()
不会 return 预期值。这可以通过使用侦听器接口来解决,或者 returning a LiveData<Double>
并在 activity.
中观察它
应用:
因此,建议通过 ViewModel
:
访问它
道:return一个LiveData<Double>
@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);
}
并观察 activity 中的 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")));
}
}
更新:
"This can be solved by using a listener interface" can you elaborate, please
要使用侦听器,您需要一个具有回调方法的接口,该方法接受您想要从 Room return 接收的类型,在本例中为 double
public interface TransactionListener {
void onReturned(double count);
}
然后在房间return的值如下时使用这个接口的实现:
道:
@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));
}
Activity:
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));
}
});
}
}
我有一个 android 房间 DB 和下一个 POJO
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,尽管数据库中存在此类数据(已在 Android Studio 数据库检查器中检查):
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 class。它单独使用 activity 将数据库中的数据动态插入 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 步:我在回答中创建了界面,但用 POJO 替换了 double class 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 ()));
});
最后,我能够检索到下一个示例数据:“食品”类别中所有交易的总和。
binding.totalValue.setText (String.valueOf (repository.getAllTransactionInCategory ("Food")));
为了遵循 Google MVVM 准则,您不应从 activity 访问存储库,因为它被视为数据库的一个层,不应由视图处理,而应由 ViewModel
另一个问题是您 return 在不使用侦听器的情况下从数据库中获取双精度值;由于从数据库中获取值需要一些时间,因此 getAllTransactionInCategory()
不会 return 预期值。这可以通过使用侦听器接口来解决,或者 returning a LiveData<Double>
并在 activity.
应用:
因此,建议通过 ViewModel
:
道:return一个LiveData<Double>
@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);
}
并观察 activity 中的 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")));
}
}
更新:
"This can be solved by using a listener interface" can you elaborate, please
要使用侦听器,您需要一个具有回调方法的接口,该方法接受您想要从 Room return 接收的类型,在本例中为 double
public interface TransactionListener {
void onReturned(double count);
}
然后在房间return的值如下时使用这个接口的实现:
道:
@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));
}
Activity:
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));
}
});
}
}