I'm trying to convert all methods of my @Dao objects to be suspendable for use from Kotlin coroutines.
When I add the suspend modifier to working function definitions and try to Build in Android Studio it fails to generate the XXXXDao_Impl.java class
A very stripped down example:
import androidx.lifecycle.LiveData
import androidx.room.*
@Database(entities = [SomeData::class], version = 1)
abstract class SomeDatabase : RoomDatabase() {
abstract fun getDao(): SomeDataDao
}
@Entity
data class SomeData(@PrimaryKey val id: Int, val name: String)
@Dao
interface SomeDataDao {
@Query("SELECT * FROM SomeData") // Works
fun getAllSync(): List<SomeData>
@Query("SELECT * FROM SomeData") // Works
suspend fun getAllSuspend(): List<SomeData>
@Query("SELECT * FROM SomeData") // Works
fun getAllSyncLiveData(): LiveData<List<SomeData>>
@Query("SELECT * FROM SomeData") // Fails to generate code
suspend fun getAllSuspendLiveData(): LiveData<List<SomeData>>
}
The generated code which is failing:
package com.example.android.rawquerysuspendtest;
import android.database.Cursor;
import androidx.lifecycle.LiveData;
import androidx.room.CoroutinesRoom;
import androidx.room.RoomDatabase;
import androidx.room.RoomSQLiteQuery;
import androidx.room.util.CursorUtil;
import androidx.room.util.DBUtil;
import java.lang.Exception;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.SuppressWarnings;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import kotlin.coroutines.Continuation;
@SuppressWarnings({"unchecked", "deprecation"})
public final class SomeDataDao_Impl implements SomeDataDao {
private final RoomDatabase __db;
public SomeDataDao_Impl(RoomDatabase __db) {
this.__db = __db;
}
@Override
public List<SomeData> getAllSync() {
final String _sql = "SELECT * FROM SomeData";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
__db.assertNotSuspendingTransaction();
final Cursor _cursor = DBUtil.query(__db, _statement, false);
try {
final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
final List<SomeData> _result = new ArrayList<SomeData>(_cursor.getCount());
while(_cursor.moveToNext()) {
final SomeData _item;
final int _tmpId;
_tmpId = _cursor.getInt(_cursorIndexOfId);
final String _tmpName;
_tmpName = _cursor.getString(_cursorIndexOfName);
_item = new SomeData(_tmpId,_tmpName);
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
_statement.release();
}
}
@Override
public Object getAllSuspend(final Continuation<? super List<SomeData>> p0) {
final String _sql = "SELECT * FROM SomeData";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
return CoroutinesRoom.execute(__db, false, new Callable<List<SomeData>>() {
@Override
public List<SomeData> call() throws Exception {
final Cursor _cursor = DBUtil.query(__db, _statement, false);
try {
final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
final List<SomeData> _result = new ArrayList<SomeData>(_cursor.getCount());
while(_cursor.moveToNext()) {
final SomeData _item;
final int _tmpId;
_tmpId = _cursor.getInt(_cursorIndexOfId);
final String _tmpName;
_tmpName = _cursor.getString(_cursorIndexOfName);
_item = new SomeData(_tmpId,_tmpName);
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
_statement.release();
}
}
}, p0);
}
@Override
public LiveData<List<SomeData>> getAllSyncLiveData() {
final String _sql = "SELECT * FROM SomeData";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
return __db.getInvalidationTracker().createLiveData(new String[]{"SomeData"}, false, new Callable<List<SomeData>>() {
@Override
public List<SomeData> call() throws Exception {
final Cursor _cursor = DBUtil.query(__db, _statement, false);
try {
final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
final List<SomeData> _result = new ArrayList<SomeData>(_cursor.getCount());
while(_cursor.moveToNext()) {
final SomeData _item;
final int _tmpId;
_tmpId = _cursor.getInt(_cursorIndexOfId);
final String _tmpName;
_tmpName = _cursor.getString(_cursorIndexOfName);
_item = new SomeData(_tmpId,_tmpName);
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
}
}
@Override
protected void finalize() {
_statement.release();
}
});
}
@Override
public Object getAllSuspendLiveData(final Continuation<? super LiveData<List<SomeData>>> p0) {
final String _sql = "SELECT * FROM SomeData";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
return CoroutinesRoom.execute(__db, false, new Callable<LiveData<List<SomeData>>>() {
@Override
public LiveData<List<SomeData>> call() throws Exception {
final Cursor _cursor = DBUtil.query(__db, _statement, false);
try {
return _result;
} finally {
_cursor.close();
_statement.release();
}
}
}, p0);
}
}
The build error message:
/Projects/Android/RawQuerySuspendTest/app/build/tmp/kapt3/stubs/debug/com/example/android/rawquerysuspendtest/SomeDataDao.java:24: error: Not sure how to convert a Cursor to this method's return type (androidx.lifecycle.LiveData<java.util.List<com.example.android.rawquerysuspendtest.SomeData>>).
public abstract java.lang.Object getAllSuspendLiveData(@org.jetbrains.annotations.NotNull()
The build.gradle for the app
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.example.android.rawquerysuspendtest"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
kapt "androidx.room:room-compiler:2.1.0-rc01"
implementation "androidx.room:room-runtime:2.1.0-rc01"
implementation "androidx.room:room-ktx:2.1.0-rc01"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha01'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha01'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha01'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-alpha01'
}
Am I missing a dependency or annotation processing step?