Android中AppDatabase初始化后仍为空,调用Dao方法报空指针异常
我正在开发一款获取设备位置并存储到数据库的Android应用,已在MainActivity的onCreate()方法中实例化并初始化AppDatabase,但调用LocationDao方法时,始终抛出错误:Attempt to invoke virtual method 'com.example.locator.Interfaces.LocationDao com.example.locator.AppDatabase.locationDao()' on a null object reference。问题发生在onLocationChanged方法中调用appDatabase.locationDao().insert(position)时,相关实现代码如下:
public class MainActivity extends AppCompatActivity implements LocationListener { ActivityMainBinding binding; AppDatabase appDatabase; private Handler handler; List<String> newPermissions; private LocationManager locationManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding=ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); appDatabase=AppDatabase.getDbIntance(this); newPermissions = new ArrayList<>(Arrays.asList(android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.RECEIVE_SMS, android.Manifest.permission.READ_SMS, android.Manifest.permission.SEND_SMS, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.READ_PHONE_STATE, Manifest.permission.CAMERA)); if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) newPermissions.add(Manifest.permission.FOREGROUND_SERVICE); if(!checkPermission(this, newPermissions)){ ActivityCompat.requestPermissions(this, newPermissions.toArray(new String[]{}), 200); } locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); // Request location updates if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return; } locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, new MainActivity()); handler = new Handler(); // Define the code block to be executed Runnable runnable = new Runnable() { @SuppressLint("SetTextI18n") @Override public void run() { Log.d("TAG", "run: " + "run every 30s"); if(appDatabase!=null){ if(appDatabase.locationDao().getAll()!=null){ MainActivity.this.runOnUiThread(() -> appDatabase.locationDao().getAll().observe(MainActivity.this, locations -> { Collections.reverse(locations); if(locations.size()>0){ binding.Longitude.setText("Longitude : "+locations.get(0).getLongitude()); binding.Latitude.setText("Latitude : "+locations.get(0).getLatitude()); binding.Altitude.setText("Altitude : "+locations.get(0).getAltitude()); binding.Accuracy.setText("Accuracy : "+locations.get(0).getAccuracy()); binding.send.setVisibility(View.VISIBLE); } })); binding.send.setOnClickListener(v->{}); } } // Repeat every 30 seconds handler.postDelayed(this, 30000L); } }; handler.post(runnable); } @Override public void onLocationChanged(@NonNull Location location) { Log.d("TAG", "onLocationChanged: checking .... "); if(location.getAccuracy()<=5f){ GpsLocation position=new GpsLocation(); if(position!=null){ Log.d("TAG", "onLocationChanged: accuracy <=5"); double latitude = location.getLatitude(); position.setLatitude(latitude); double longitude = location.getLongitude(); position.setLongitude(longitude); double altitude=location.getAltitude(); position.setAltitude(altitude); float accuracy=location.getAccuracy(); position.setAccuracy(accuracy); //TODO: send message with positions }else{ Log.d("TAG", "onLocationChanged: position is null"); } }else{ GpsLocation position=new GpsLocation(); if(position!=null){ Log.d("TAG", "onLocationChanged: accuracy >=5"); double latitude = location.getLatitude(); position.setLatitude(latitude); double longitude = location.getLongitude(); position.setLongitude(longitude); double altitude=location.getAltitude(); position.setAltitude(altitude); float accuracy=location.getAccuracy(); position.setAccuracy(accuracy); appDatabase.locationDao().insert(position);//错误发生位置 }else{ Log.d("TAG", "onLocationChanged: position is null"); } } } @Override public void onProviderEnabled(@NonNull String provider) { LocationListener.super.onProviderEnabled(provider); } @Override public void onProviderDisabled(@NonNull String provider) { LocationListener.super.onProviderDisabled(provider); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(requestCode == 200){ for(int g : grantResults){ if(g != PackageManager.PERMISSION_GRANTED){ Toast.makeText(this, "Permission non accorde", Toast.LENGTH_SHORT).show(); return; } } } } private boolean checkPermission(Context context, List<String> permissions){ for(String p : permissions){ if (ContextCompat.checkSelfPermission(context, p) != PackageManager.PERMISSION_GRANTED) return false; } return true; } } // AppDatabase类 @Database(entities = {GpsLocation.class} , version =2) public abstract class AppDatabase extends RoomDatabase { public abstract LocationDao locationDao(); public static AppDatabase instance; public static AppDatabase getDbIntance(Context c){ if (instance ==null){ instance = Room.databaseBuilder(c.getApplicationContext() , AppDatabase.class , "LocationDb" ) .fallbackToDestructiveMigration() .allowMainThreadQueries() .build(); } return instance; } }
核心原因
你在调用locationManager.requestLocationUpdates时,传入的参数是new MainActivity(),这会创建一个全新的MainActivity实例——这个新实例的appDatabase字段从未被初始化,所以当它的onLocationChanged方法被触发时,就会抛出空指针异常。
修复步骤
替换位置更新监听器
将locationManager.requestLocationUpdates的最后一个参数从new MainActivity()改为this,因为当前MainActivity已经实现了LocationListener接口,直接传入自身即可:locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);补全权限申请后的位置注册逻辑
当前代码中如果权限未通过,onCreate会直接return,导致位置更新未被注册。需要在onRequestPermissionsResult中,当所有权限通过后补注册位置更新:@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(requestCode == 200){ boolean allGranted = true; for(int g : grantResults){ if(g != PackageManager.PERMISSION_GRANTED){ Toast.makeText(this, "权限未授予", Toast.LENGTH_SHORT).show(); allGranted = false; break; } } // 所有权限通过后注册位置更新 if(allGranted){ if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this); } } } }
额外优化建议
- 你启用了
allowMainThreadQueries(),这会让数据库操作在主线程执行,容易引发ANR(应用无响应)。建议改用协程或其他异步方式执行数据库操作。 onLocationChanged方法的两个分支代码高度重复,可以提取为公共方法减少冗余。
内容的提问来源于stack exchange,提问作者Patrick Renaud

