18

I am implementing Room persistence lib in kotlin for my database implementation.

Following are my Entity, Dao and Database classes:

Food.kt

@Entity
class Food(@ColumnInfo(name = "food_name") var foodName: String,
           @ColumnInfo(name = "food_desc") var foodDesc: String,
           @ColumnInfo(name = "protein") var protein: Double,
           @ColumnInfo(name = "carbs") var carbs: Double,
           @ColumnInfo(name = "fat") var fat: Double)
{
    @ColumnInfo(name = "id")
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
    @ColumnInfo(name = "calories")
    var calories: Double = 0.toDouble()
}

PersonalizedFood.kt

@Entity(primaryKeys = arrayOf("food_id","date"))
class PersonalizedFood(@ColumnInfo(name = "quantity") var quantity: Int,
                       @ColumnInfo(name = "unit") var unit: String,
                       @ColumnInfo(name = "date") var date: Date){

    @ColumnInfo(name = "food_id")
    var foodId:Long = 0
}

FoodDao.kt

@Dao
interface FoodDao {

    companion object{
        const val ID = "id"
        const val NAME = "name"
        const val PROTEIN = "protein"
        const val DESC = "desc"
        const val CARBS = "carbs"
        const val FAT = "fat"

        const val DATE = "date"
        const val FOOD_ID = "food_id"

        const val ALL_FOOD_LIST = "food"
        const val PERSONALISED_FOOD_LIST = "personalised_food"
    }

    /**
     * Returns food details of a food given by food_id
     */
    @Query("SELECT * FROM $ALL_FOOD_LIST WHERE $ID=:food_id")
    fun getFoodDetails(food_id:Long):Food

    /**
     * Inserts food items in all_food_list
     */
    @Insert
    fun addFoodList(list:ArrayList<Food>)

    @Insert(onConflict = REPLACE)
    fun saveFood(food:PersonalizedFood)

    @Query("SELECT * FROM $PERSONALISED_FOOD_LIST WHERE $FOOD_ID=:foodId and $DATE=:date")
    fun getFood(foodId:Int, data:Date):PersonalizedFood

    @Query("SELECT * FROM $ALL_FOOD_LIST where $ID in (select $FOOD_ID from $PERSONALISED_FOOD_LIST where $DATE = :date)")
    fun getFood(date:Date):ArrayList<Food>
}

Converter.kt

class Converter {

    companion object{
        @TypeConverter
        fun fromTimestamp(value: Long?): Date? {
            return if (value == null) null else Date(value)
        }

        @TypeConverter
        fun dateToTimestamp(date: Date): Long {
            return date.time
        }
    }
}

FoodDatabase.kt

@Database(entities = arrayOf(Food::class, PersonalizedFood::class), version = 1)
@TypeConverters(Converter::class)
abstract class FoodDatabase : RoomDatabase(){
    abstract fun foodDao():FoodDao

    companion object{
        private val databaseName = "diet"

        var dbInstance:FoodDao? = null
        fun getInstance(context:Context):FoodDao?{
            if(dbInstance == null)
                dbInstance = Room.inMemoryDatabaseBuilder(context, FoodDatabase::class.java).build().foodDao()
            return dbInstance;
        }
    }
}

And when i run following code to create database:

FoodDatabase.getInstance(baseContext)?.getFood(Calendar.getInstance().time)

It gives me following exception:

Caused by: java.lang.RuntimeException: cannot find implementation for com.chandilsachin.diettracker.database.FoodDatabase. FoodDatabase_Impl does not exist at android.arch.persistence.room.Room.getGeneratedImplementation(Room.java:90) at android.arch.persistence.room.RoomDatabase$Builder.build(RoomDatabase.java:340) at com.chandilsachin.diettracker.database.FoodDatabase$Companion.getInstance(FoodDatabase.kt:21) at com.chandilsachin.diettracker.MainActivity$SetUpFoodDatabase.doInBackground(MainActivity.kt:95) at com.chandilsachin.diettracker.MainActivity$SetUpFoodDatabase.doInBackground(MainActivity.kt:77) at android.os.AsyncTask$2.call(AsyncTask.java:295) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)  at java.lang.Thread.run(Thread.java:818)

Has anyone implemented room persistence in kotlin?

Edited This question was marked duplicate of this. Though problem statement is same but solution given does not solve my problem. Solution says i have to add replace annotationProcessor to kapt "android.arch.persistence.room:compiler:1.0.0-alpha1" dependency. I made those changes and it resulted in gradle error while project build.

Information:Gradle tasks [:app:assembleDebug] Warning:warning: Supported source version 'RELEASE_7' from annotation processor 'android.arch.persistence.room.RoomProcessor' less than -source '1.8' Warning:warning: The following options were not recognized by any processor: '[kapt.kotlin.generated]' /Users/BBI-M1025/Documents/BBI/Workspace_fun/Android/diet-tracker/app/src/main/java/com/chandilsachin/diettracker/database/Food.kt Error:(1, 1) Some error(s) occurred while processing annotations. Please see the error messages above. Error:Execution failed for task ':app:kaptDebugKotlin'.

Compilation error. See log for more details Information:BUILD FAILED in 10s Information:2 errors Information:2 warnings Information:See complete output in console

I am attaching my gradle file also:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.chandilsachin.diettracker"
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    compile 'com.android.support:appcompat-v7:25.0.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8'
    compile 'com.android.support:cardview-v7:25.0.1'
    compile 'com.android.support:recyclerview-v7:25.0.1'
    compile 'com.github.ne1c:rainbowmvp:1.2.1'
    compile "org.jetbrains.anko:anko-commons:0.10.0"

    /*annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha1"
    compile "android.arch.lifecycle:extensions:1.0.0-alpha1"
    annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha1"*/

    compile "android.arch.persistence.room:runtime:1.0.0-alpha1"
    annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha1"
    kapt "android.arch.persistence.room:compiler:1.0.0-alpha1"

    testCompile 'junit:junit:4.12'
}
repositories {
    mavenCentral()
}

Has anyone come across this issue?

Mohammad Kanan
  • 4,452
  • 10
  • 23
  • 47
Sachin Chandil
  • 17,133
  • 8
  • 47
  • 65
  • @Raghunandan, Problem statement of that question is same but applying those solution doesn't solve my problem. – Sachin Chandil May 24 '17 at 06:15
  • You can comment on the original post. Add a bounty too. But I only closed this cause I felt it's a duplicate – Raghunandan May 24 '17 at 06:18
  • @Raghunandan I have commented in original post. But to add bounty in this question you have to open this question as there is not link for bounty here. – Sachin Chandil May 24 '17 at 06:21
  • Add bounty to the other question. Or comment on the post. this post will eventually be closed by someone even if i din't close it before cause its a duplicate – Raghunandan May 24 '17 at 06:21
  • can't you open this question? – Sachin Chandil May 24 '17 at 06:24
  • i can but the point is its a duplicate hence i closed this. If you still think this is wrong pls go to https://meta.stackoverflow.com/ and point this question and some one should answer your query. – Raghunandan May 24 '17 at 06:26
  • as the error message hints for, just replace `kotlin-stdlib-jre7` with `kotlin-stdlib`. – Martin Zeitler Aug 19 '18 at 01:53

5 Answers5

24

After spinning my head around for a while with this problem, I came across to the solution.

It was really hard as there is no official tutorial, blog etc out there to help with this problem as of now.

I had to do several hit and trial for all the combination of gradle plugins and dependencies as i knew that something is wrong with gradle config only.

Lets come to the solution:

I had to remove apply plugin: 'kotlin-kapt' from build.gradle(:module) file and replace annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha1" to kapt "android.arch.persistence.room:compiler:1.0.0-alpha1".

This is the gradle configuration to successfully compile code.

But there more things to check. You have to initialise properties of your @Entity class unlike java given in Room Persistence lib doc. Though there are getter setter but it is not mentioned to create a constructor with initialisation. So I had to change my @Entity class with this:

@Entity(tableName = "all_food_list")
class Food (@ColumnInfo(name = "food_name") var foodName: String = "",
            @ColumnInfo(name = "food_desc") var foodDesc: String = "",
            @ColumnInfo(name = "protein") var protein: Double = 0.0,
            @ColumnInfo(name = "carbs") var carbs: Double = 0.0,
            @ColumnInfo(name = "fat") var fat: Double = 0.0,
            @ColumnInfo(name = "calories") var calories: Double = 0.0)
{
    @ColumnInfo(name = "id")
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
}

Now for TypeConverts, Unlike java, you need to create normal function not static functions(companion object):

class Converters{

        @TypeConverter
        fun fromTimestamp(value: String): Calendar {
            val arr = value.split("-")
            val cal = Calendar.getInstance()
            cal.set(arr[0].toInt(), arr[1].toInt(), arr[2].toInt())
            return cal
        }

        @TypeConverter
        fun dateToTimestamp(date: Calendar): String {
            return "${date.get(Calendar.DATE)}-${date.get(Calendar.MONTH)+1}-${date.get(Calendar.YEAR)}"
        }

}

I am adding build.gradle file also to make it more clear:

build.gradle(:project)

buildscript {
    ext.kotlin_version = '1.1.2-4'
    ext.gradle_version_stable = '2.3.2'
    ext.gradle_version_preview = '3.0.0-alpha1'
    ext.anko_version = '0.10.0'
    repositories {
        maven { url 'https://maven.google.com' }
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0-alpha1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        maven { url 'https://maven.google.com' }
        jcenter()
        maven { url "https://jitpack.io" }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

build.gradle(:module)

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.chandilsachin.diettracker"
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}


dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    ...
    ...
    // room persistence dependency 
    compile "android.arch.persistence.room:runtime:1.0.0-alpha1"
    kapt "android.arch.persistence.room:compiler:1.0.0-alpha1"

    testCompile 'junit:junit:4.12'
}
repositories {
    mavenCentral()
}

I think this is all, I did to make my code woking.

Hope this helps someone else also.

Community
  • 1
  • 1
Sachin Chandil
  • 17,133
  • 8
  • 47
  • 65
  • So I am under the suspicion that this only worked for you by coincidence. I am using Room along with DataBinding (which requires the apply kapt line) and it was working only when I built the project in a certain order. Could you try doing a full clean/rebuild of your porject? – RichyHBM May 28 '17 at 17:20
  • I did that n number of times but didn't work, Even when it worked i tried to play with all gradle settings but this is what seems to work. I am using Android Studio 3.0.0-canary version. May be you are using something different if not, then GOD will know whats the problem :) Kidding – Sachin Chandil May 28 '17 at 17:26
  • I am using AS 2, I think that Room doesnt yet support Java 8, and thus when you try to use it with kotlin it fails (I am guessing it requires J8?) atleast thats what the error looks to be – RichyHBM May 28 '17 at 17:28
  • Also, are you also using DataBinding? – RichyHBM May 28 '17 at 17:29
  • I am not using DataBinding. – Sachin Chandil May 29 '17 at 04:57
  • Thanks a ton, that actually helped me in fixing my kotlin room setup. I also had an error saying "Not sure how to convert a Cursor to this method's return type" the reason was: In my @Ðao interface I had used SELECT count(*) ... but had forgotten to add the return type (long). – AgentKnopf Jun 26 '17 at 19:04
  • remove apply plugin: 'kotlin-kapt' did the trick for me. Thanks – Yasitha Waduge Jun 29 '17 at 10:48
  • 1
    This is not working for me as I am using dagger2 & without `kapt` it fails to compile :( I am getting the different error than yours as I mentioned in this issue: https://stackoverflow.com/questions/47676905/kotlin-room-java-lang-illegalargumentexception-void-cannot-be-converted-to – Sandip Fichadiya Dec 07 '17 at 06:26
  • Using a kotlin `data class` like `data class Food(…` might simplify things a bit. – Andrew Siplas Jun 28 '18 at 07:05
  • 1
    removing `apply plugin: 'kotlin-kapt'` is not necessary I guess , if I removed it other libraries like glide gave issues – Manohar Jun 28 '18 at 13:19
2

Here my gradle files, i didn't need to add thoses plugins.

build.gradle(project):

buildscript {
    ext.kotlin_version = '1.1.2-4'
    ext.lifecycle_version = '1.0.0-alpha1'
    ext.room_version = '1.0.0-alpha1'

    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

    }
}

build.gradle (app)

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

//enable anotation processing with kotlin, disabled by default
kapt {
    generateStubs = true
}


android {
/**
...
**/
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    //kotlin
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

    //support
    compile 'com.android.support:appcompat-v7:25.3.1'

    //google architecture
    compile "android.arch.lifecycle:runtime:$lifecycle_version"
    compile "android.arch.lifecycle:extensions:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"
    kapt "android.arch.lifecycle:compiler:$lifecycle_version"

    //database
    compile "android.arch.persistence.room:runtime:$room_version"
    annotationProcessor "android.arch.persistence.room:compiler:$room_version"
    kapt "android.arch.persistence.room:compiler:$room_version"

}
repositories {
    mavenCentral()
}

Then run menu build-> make project to create impl class. Hope this helps

Lyofen
  • 631
  • 6
  • 15
0

I made an example similar using Java and used to have same problem and solution was add APT line apt "android.arch.persistence.room:compiler:1.0.0-alpha1"

without apply: apply plugin: 'kotlin-kapt'. Remove this line!!

You have to clear project and rebuild it before run again and try uninstall existing app on phone or Virtual device. Hope you help.

  • 1
    Java's annotation processor is apt and kotlin's is kapt. So if you are doing it for Java you have to use apt. I think your answer is not applicable here. – Sachin Chandil May 30 '17 at 05:58
  • I made an observation on your code, you must to delete the line in KAPT I know my code is APT because is JAVA, Just tell to you my story with the same problem and how I solved it. so.... it's for you that point, not my code. – Martín De Girolamo Jun 01 '17 at 11:58
0

I got the same issue when I try to migrate from Java to Kotlin:

RuntimeException: cannot find implementation for AppDatabase. AppDatabase_Impl does not exist

I tried all these answers in this question, none all them works, then I found an article: Kotlinlang Tutorials - Android Frameworks Using Annotation Processing:

In Kotlin you specify the dependencies in a similar to Java way using Kotlin Annotation processing tool (kapt) instead of annotationProcessor.

Then I modified the build.gradle(Module:app) by changing all annotationProcessor to kapt, it works:

--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,6 +2,8 @@ apply plugin: 'com.android.application'

 apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
 apply plugin: 'kotlin-android-extensions'

 android {
     // Butter Knife
     implementation "com.jakewharton:butterknife:$butterknife_version"
-    annotationProcessor "com.jakewharton:butterknife-compiler:$butterknife_version"
+    kapt "com.jakewharton:butterknife-compiler:$butterknife_version"

     // Room
     implementation "android.arch.persistence.room:runtime:$arch_lifecycle_version"
-    annotationProcessor "android.arch.persistence.room:compiler:$arch_lifecycle_version"
+    kapt "android.arch.persistence.room:compiler:$arch_lifecycle_version"
     // LifeCycle
     implementation "android.arch.lifecycle:runtime:$arch_lifecycle_version"
     implementation "android.arch.lifecycle:extensions:$arch_lifecycle_version"
     implementation "android.arch.lifecycle:common-java8:$arch_lifecycle_version"
-    annotationProcessor "android.arch.lifecycle:compiler:$arch_lifecycle_version"
+    kapt "android.arch.lifecycle:compiler:$arch_lifecycle_version"

     // Dagger
     implementation "com.google.dagger:dagger:$dagger_version"
     implementation "com.google.dagger:dagger-android-support:$dagger_version"
-    annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
-    annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
+    kapt "com.google.dagger:dagger-compiler:$dagger_version"
+    kapt "com.google.dagger:dagger-android-processor:$dagger_version"
}

If you use Butter Knife, you should also change to kapt, otherwise the views which are injected might be null.

Weiyi
  • 1,843
  • 2
  • 22
  • 34
0

in the Dao interface check that the annotations like @Query .. are imported from the room database class and not from somewhere else

Eyal Israel
  • 257
  • 4
  • 9