Search code examples
javaandroidandroid-roomkotlin-coroutineswear-os

Android Wear - Run Room Database Instructions on a Separate Thread using Java?


I have written a simple app which detects photoplethysmogram (PPG) signals from a sensor on a smartwatch running Android Wear OS. I am currently attempting to record that data using Room so that I can access the data and perform analysis on it. I run into an issue when I attempt to insert data into the Database, giving me the following error:

2022-01-08 14:15:13.435 5482-5482/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.ppg_extraction_final, PID: 5482
    java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
        at androidx.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:469)
        at androidx.room.RoomDatabase.beginTransaction(RoomDatabase.java:553)
        at com.example.ppg_extraction_final.db.PpgDao_Impl.add(PpgDao_Impl.java:58)
        at com.example.ppg_extraction_final.ui.MainActivity.writePpgData(MainActivity.java:98)
        at com.example.ppg_extraction_final.ui.MainActivity.onSensorChanged(MainActivity.java:72)
        at android.hardware.SystemSensorManager$SensorEventQueue.dispatchSensorEvent(SystemSensorManager.java:833)
        at android.os.MessageQueue.nativePollOnce(Native Method)
        at android.os.MessageQueue.next(MessageQueue.java:326)
        at android.os.Looper.loop(Looper.java:160)
        at android.app.ActivityThread.main(ActivityThread.java:6680)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

I know that I am receiving this error because I am attempting to operate the database on the main thread, which will cause slowdowns with my UI. I also know that Kotlin coroutines provide a nice solution to this issue with relatively little boilerplate. However, having written my program in Java, I am a bit lost on the best method for correcting this error. Is it possible for me to implement the kotlin coroutines somehow in my current app structure? Is it worth migrating the entire app to kotlin? Is there another method to perform this action in java?

Here are the relevant parts of my MainActivity. PpgDatabase, PpgData, and PpgDao refer to my Database, Entity, and DAO classes, respectively. These are all in their own files. Please let me know if any of this code is needed!

public class MainActivity extends Activity implements SensorEventListener {

    private TextView textView;
    private SensorManager sensorManager;
    PpgDatabase ppgDatabase;
    PpgDao ppgDao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        com.example.ppg_extraction_final.databinding.ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        textView = findViewById(R.id.dataText);

        // Instantiate sensor manager
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

        // Obtain db instance
        ppgDatabase = DatabaseClient.getInstance(getApplicationContext()).getPpgDatabase();

        // Instantiate ppg dao
        ppgDao = ppgDatabase.getPpgDao();
    }

    public void onStartClick(View view) {
        // Begin listening for sensor events on start click
        // 65572 = PPG sensor type
        sensorManager.registerListener(this, sensorManager.getDefaultSensor(65572),
                sensorManager.SENSOR_DELAY_NORMAL);
    }

    public void onStopClick(View view) {
        // Stop listening for sensor events on stop click
        sensorManager.unregisterListener(this);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        // 65572 = PPG sensor type
        if (event.sensor.getType() == 65572)
            writePpgData(ppgDao, event);

    }

    public void writePpgData(PpgDao ppgDao, SensorEvent event) {
        // Function for adding sensor event to db
        PpgData ppgData = new PpgData();

        ppgData.setCh0(event.values[0]);
        ppgData.setCh1(event.values[1]);

        ppgDao.add(ppgData);
    }

}

I would appreciate any advice. Thanks!


Solution

  • I think Kotlin is preferred.

    But since this issue alone shouldn't block you for so long, I think you should be able to use Executors.

    See the Room Basic Sample by Google:

    Specially the Database initialization

    Here executors.diskIO() is used to interact with the DataBase on a background thread

    The executors are defined here.