5

I am implementing Room persistence lib in kotlin for my database implementation. Same question is asked in Android Room Persistences library and Kotlin thread, applying those solution resulted in different gradle errors:

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

My previous question on same topic 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?

Sachin Chandil
  • 17,133
  • 8
  • 47
  • 65
  • 2
    Moderators, Please check problem statement before marking as duplicate. – Sachin Chandil May 24 '17 at 09:41
  • you can track some issues https://youtrack.jetbrains.com/issues/KT and you can also file some bugs if you think its relevant. – Raghunandan May 24 '17 at 10:19
  • could you check two things? 1. The Kotlin to JVM targhet version (File > Settings > Kotlin compiler) 2. The project JDK version File > Project structure > "JDK location" to see the JDK setted for your project; then go to the setted path, navigate into "bin", and type "java -version" – Nicola De Fiorenze May 24 '17 at 10:53
  • @NicolaDeFiorenze JVM target version is 1.6, and java version is "1.8.0_91". – Sachin Chandil May 24 '17 at 10:58
  • @chandil03 try to update the JDK, possibly to v1.8.0_121. I tried to set Kotlin to JVM target to 1.6 and 1.8 without being able to reproduce your error. The java version seems to be the only difference – Nicola De Fiorenze May 24 '17 at 12:20
  • @NicolaDeFiorenze problem wasn't with JDK or kotlin version. It was some some gradle config. – Sachin Chandil May 25 '17 at 07:11

2 Answers2

12

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
  • 1
    You can also keep your converters as `object` but put `@JvmStatic` on the methods. That way the generated methods will actually be static and the Room plugin will be happy, – BoD May 25 '17 at 14:32
  • Adding **default** values to properties solve my problem. It's awful solution. I even think that is a bug. – murt Sep 25 '17 at 08:12
  • @murt its not a bug, as entity classes is not required to have parameterised constructor, it is necessary to have default value. – Sachin Chandil Sep 25 '17 at 09:36
  • 1
    Ok but fields in Java may be not initialized, so why var properties have to? – murt Sep 25 '17 at 11:15
0

man just add the following in the build.gradle. Above dependencies :D

    kapt {
          generateStubs = true
    }
Oscar Gallon
  • 180
  • 2
  • 7