У меня есть приложение, предназначенное для записи данных датчиков каждые 10 мс и хранения в SQLite db на телефоне. Я делаю вставки db как задачу async, потому что они происходят так быстро, и их так много, что они заметно замедляют навигацию. Тем не менее, я иногда сталкиваюсь с проблемами при попытке остановить запись.Android: правильная остановка задачи async
В одном из моих фрагментов есть кнопка начала остановки. Нажмите его для записи. Нажмите ее еще раз, чтобы остановить запись. onClick
выглядит следующим образом:
@Override
public void onClick(View v) {
if (!recordingStarted){
recordingStarted = true;
mainActivity.startService(new Intent(mainActivity, SensorService.class));
startButton.setText(getResources().getString(R.string.start_button_label_stop));
Snackbar.make(coordinatorLayout, "Recording...", Snackbar.LENGTH_SHORT).show();
} else {
mainActivity.stopService(new Intent(mainActivity, SensorService.class));
startButton.setEnabled(false);
Snackbar.make(coordinatorLayout, "Recording stopped.", Snackbar.LENGTH_SHORT).show();
}
}
Когда запись началась, SensorService
класс вызывается. Это просто регистрирует слушателей, запускает службу, поэтому я могу собирать данные, когда экран выключен, вычисляет некоторые сенсорные вещи и т. Д. Это где моя задача асинхронная. Единственные интересные части этого класса являются:
public class SensorService extends Service implements SensorEventListener {
public BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "onReceive("+intent+")");
if (!intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
return;
}
Runnable runnable = new Runnable() {
public void run() {
Log.i(TAG, "Runnable executing...");
unregisterListener();
registerListener();
}
};
new Handler().postDelayed(runnable, SCREEN_OFF_RECEIVER_DELAY);
}
};
public void onSensorChanged(SensorEvent event) {
sensor = event.sensor;
int i = sensor.getType();
if (i == MainActivity.TYPE_ACCELEROMETER) {
accelerometerMatrix = event.values;
} else if (i == MainActivity.TYPE_GYROSCOPE) {
gyroscopeMatrix = event.values;
} else if (i == MainActivity.TYPE_GRAVITY) {
gravityMatrix = event.values;
} else if (i == MainActivity.TYPE_MAGNETIC) {
magneticMatrix = event.values;
}
long curTime = System.currentTimeMillis();
long diffTime = (curTime - lastUpdate);
// only allow one update every POLL_FREQUENCY.
if(diffTime > POLL_FREQUENCY) {
lastUpdate = curTime;
//cut a bunch of stuff here to save space
//insert into database
new InsertSensorDataTask().execute();
}
}
@Override
public void onCreate() {
super.onCreate();
dbHelper = new DBHelper(getApplicationContext());
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
accelerometer = sensorManager.getDefaultSensor(MainActivity.TYPE_ACCELEROMETER);
gyroscope = sensorManager.getDefaultSensor(MainActivity.TYPE_GYROSCOPE);
gravity = sensorManager.getDefaultSensor(MainActivity.TYPE_GRAVITY);
magnetic = sensorManager.getDefaultSensor(MainActivity.TYPE_MAGNETIC);
PowerManager manager =
(PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
}
@Override
public void onDestroy() {
unregisterReceiver(receiver);
unregisterListener();
wakeLock.release();
dbHelper.close();
stopForeground(true);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startForeground(Process.myPid(), new Notification());
registerListener();
wakeLock.acquire();
return START_STICKY;
}
private class InsertSensorDataTask extends AsyncTask<String, String, Boolean> {
@Override
protected Boolean doInBackground(String... params) {
try {
dbHelper.insertData(Short.parseShort(MainActivity.subInfo.get("subNum")), System.currentTimeMillis(),
accelerometerMatrix[0], accelerometerMatrix[1], accelerometerMatrix[2],
accelerometerWorldMatrix[0], accelerometerWorldMatrix[1], accelerometerWorldMatrix[2],
gyroscopeMatrix[0], gyroscopeMatrix[1], gyroscopeMatrix[2]);
return true;
} catch (SQLException e) {
Log.e(TAG, "insertData: " + e.getMessage(), e);
return false;
}
}
}
}
Когда я нажал на кнопку остановки, которая будет немедленно вызывать stopService
в onClick
, который я считаю, будет вызывать onDestroy
в SensorService
. Однако я сталкиваюсь с условиями остановки, прослушиватели незарегистрированы, базы данных закрыты, но в фоновом режиме все еще выполняются задачи асинхронного программирования. Я предполагаю, что они до сих пор завершают свои окончательные задачи, прежде чем остановить их. Это приводит меня в зону исключения, потому что тогда асинхронный код пытается вставить данные в базу данных, которая теперь закрыта. Я мог бы просто поймать их и проигнорировать их, но я хочу выяснить правильный способ справиться с такой ситуацией.
Как мне реорганизовать мой код, чтобы разрешить выполнение задач async? Поскольку это не одна большая работа, а несколько тысяч небольших вставных заданий, я бы предположил, что они могут остановиться довольно быстро, поэтому я удивлен тем, что я продолжаю сталкиваться с этими проблемами с закрытыми исключениями db.
Есть ли способ рассказывать, когда все асинхронные задачи выполняются? Может быть, я могу использовать это как условие в onDestroy
, прежде чем что-нибудь закрывается?
Или стоило ли вообще отказаться от асинхронных задач? Я в основном просто хочу избежать использования этих вставок db в основной резьбе UI
положить тост в OnDestroy AsynTask! вы узнаете, остановится ли это или нет. – Jois