是否可以在返回LiveData的Room @DAO中编写“挂起”功能?

时间:2019-06-14 18:30:21

标签: kotlin android-room kotlin-coroutines

我正尝试将@Dao对象的所有方法都转换为可挂起,以供Kotlin协程使用。

当我将suspended修饰符添加到工作函数定义中并尝试在Android Studio中构建时,它无法生成XXXXDao_Impl.java类

一个精简的例子:

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>>
}

失败的生成代码:

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);
  }
}

生成错误消息:

/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()

应用程序的build.gradle

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'
}

我是否缺少依赖项或注释处理步骤?

1 个答案:

答案 0 :(得分:1)

房间的当前实现不支持与LiveData一起使用的协程。作为一种解决方法,您可以像下面这样实现它:

@Dao
interface AccountDao{

@Query("SELECT * FROM account_master")
    suspend fun getAllAccounts(): List<Account>
}

在实现ViewModel类的过程中,您可以创建LiveData并为其分配一个值,该值是从数据库中检索的:

class MainViewModel : ViewModel() {
    private val dao: AccountDao = ...// initialize it somehow
    private var job: Job = Job()
    private val scope = CoroutineScope(job + Dispatchers.Main)
    lateinit var accounts: MutableLiveData<List<Account>>

    override fun onCleared() {
        super.onCleared()
        job.cancel()
    }

    fun getAccounts(): LiveData<List<Account>> {
        if (!::accounts.isInitialized) {
            accounts = MutableLiveData()

            scope.launch {
                accounts.postValue(dao.getAllAccounts())
            }

        }
        return accounts
    }
}

要使用Dispatchers.Main导入:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'