您现在的位置是:首页 >技术交流 >AS Gradle 8.0 配置 + Realm 使用网站首页技术交流

AS Gradle 8.0 配置 + Realm 使用

_昨日重现 2023-07-08 08:00:03
简介AS Gradle 8.0 配置 + Realm 使用

目录

Gradle 概述

AS Gradle 8.0 配置说明

1)项目 settings.gradle

2)项目 build.gradle

3)子模块 hawkysdk-realm

3.1 build.gradle

3.2 Realm 表

3.3 Realm Dao

3.4 RealmCallback DB操作回调

3.5 HawkyRealm DB配置初始化

4)子模块 app

4.1 build.gradle

4.2 RealmApplication

4.3 User 实体类 

4.4 MainActivity 测试类

4.5 activity_main 布局文件

小结


Gradle 概述

Gradle 是一个基于 Apache Ant 和 Apache Maven 概念的项目自动化构建开源工具。它使用一种基于 Groovy 的特定领域语言 (DSL) 来声明项目设置,也增加了基于 Kotlin 语言的 kotlin-based DSL。

查看版本路径:Gradle 版本可在 gradle-wrapper.properties 查看(一般检查本地是否有下载对应的版本,没有就会按照distributionUrl定义的路径下载对应的 Gradle 版本),也可在 Project Structure  查看 Gradle 版本及 Gradle 插件版本(classpath 'com.android.tools.build:gradle:8.0.0' 默认已内置,无需再配置)。

同 Gradle 插件的本质区别Gradle 是提供 DSL 语言脚本,供项目配置使用;Gradle 插件是支持脚本执行的环境。

AS Gradle 8.0 配置说明

最近由于项目需要,使用到了 Realm 数据库,而 Gradle 版本升级到了 8.0,Gradle 插件版本也升级到了 8.0.0。所以打算对本次升级结合 Realm 的具体使用 以及编译过程中遇到的坑做个总结。

1)项目 settings.gradle

说明:用于定义项目级代码库设置及所包含的模块。--原项目 build.gradle 中的部分配置拆分至新增的 settings.gradle 文件中。

//  New Version:buildscript --> pluginManagement 插件管理
pluginManagement {
    repositories {// 下载 Gradle 插件 的依赖仓库
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

// New Version: allprojects --> dependencyResolutionManagement 依赖项解析管理
dependencyResolutionManagement {
    /**
     * 设置存储库模式(原 allprojects 是没有这一项的):
     * PREFER_PROJECT:解析依赖库时, 优先使用本地仓库
     * FAIL_ON_PROJECT_REPOS:解析依赖库时, 强行使用远程仓库
     */
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

// 项目工程的名字,可不写
// rootProject.name = "Realm"

// include modules
include ':app'
include ':hawkybase'
include ':hawkysdk-realm'

// project paths
project(':hawkybase').projectDir = new File(settingsDir, '../../hawky-base-android/hawkybase')
project(':hawkysdk-realm').projectDir = new File(settingsDir, '../../hawky-sdk-android/hawkysdk-realm')

2)项目 build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.

// 这里仍然可以使用老的 buildscript,来配置需要的插件
buildscript {
    ext.kotlin_version = '1.8.0'

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "io.realm:realm-gradle-plugin:10.15.1"
    }

    repositories {
        mavenCentral()
    }
}

plugins {
    // 项目build配置应声明为false,告诉 Gradle 不要将插件应用到当前项目
    id 'com.android.application' version '8.0.0' apply false
    id 'com.android.library' version '8.0.0' apply false
    id 'org.jetbrains.kotlin.android' version "$kotlin_version" apply false
}

// 定义扩展参数,可全局配置使用
ext {
    compileSdkVersion = 33
    //...
}

3)子模块 hawkysdk-realm

3.1 build.gradle

// 必须放前面
plugins {
    // 相当于 apply plugin: 'com.android.library'
    id 'com.android.library'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-kapt'
    id 'realm-android'
}

android {
    namespace 'com.hawky.hawkysdk.realm'
    compileSdk rootProject.ext.compileSdkVersion

    defaultConfig {
        minSdk 24
        targetSdk 33

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles "consumer-rules.pro"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = '17'
    }
}

dependencies {

    implementation "androidx.core:core-ktx:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation project(path: ':hawkybase')
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

3.2 Realm 表

// 定义Person 表,主键 id
open class PersonModel : RealmObject() {
    @PrimaryKey
    var id: Long = 0
    var name: String? = null
    var email: String? = null
}

3.3 Realm Dao

package com.hawky.hawkysdk.realm.realmdao

import com.hawky.hawkysdk.realm.realmmodel.PersonModel
import io.realm.Realm
import io.realm.Sort
import io.realm.exceptions.RealmPrimaryKeyConstraintException

object PersonDao {

    /**
     * 保存数据
     * 注意:1)无论 copyToRealm 还是 insert 都必须设置主键,否则再次执行会报 “existing primary key value”
     *     2)Realm对象只能在它创建的线程上访问(“Realm access from incorrect thread”)
     */
    fun savePerson(person: PersonModel, callback: RealmCallback?) {
        val realm = Realm.getDefaultInstance()
        //========= 方式1:手动使用事务 ====================//
        /*realm.beginTransaction()
        // insert 速度快而且没有返回值,一般用在数据修改少、查询多的场景。
        realm.insert(person)
        realm.commitTransaction()*/

        //========== 方式2:executeTransaction 必须在子线程中使用================//
        /*realm.executeTransaction {
            it.insert(person)
        }*/

        //========== 方式3:异步使用事务 ===================//
        realm.executeTransactionAsync({
            // 子线程中执行任务(copyToRealm 带返回值)
            // 注意这里不能用 realm 只能用 it,否则报错
            it.copyToRealm(person)// 必须设置主键id 且id不能相同,否则报错
            //  it.copyToRealmOrUpdate(person)// id相同则更新,否则插入
        }, {
            // 当前线程
            callback?.onResult(true, null, person)
        }) {
            // 当前线程
            callback?.onResult(false, it.message, null)
        }
    }

    /**
     * 删除数据
     */
    fun deletePerson(id: Int, callback: RealmCallback?) {
        val realm = Realm.getDefaultInstance()
        //  realm.delete(PersonModel::class.java)// 全部删除
        realm.executeTransactionAsync({
            it.where(PersonModel::class.java).equalTo("id", id).findFirst()
                ?.deleteFromRealm() ?: throw Throwable(
                "User($id) doesn't exists",
                null
            )// 异常往外抛,统一在onResult线程中处理
        }, {
            callback?.onResult(true, null, null)
        }) {
            callback?.onResult(false, it.message, null)
        }
    }

    /**
     * 更新数据
     */
    fun updatePerson(person: PersonModel, callback: RealmCallback?) {
        val realm = Realm.getDefaultInstance()
        realm.executeTransactionAsync({
            it.copyToRealmOrUpdate(person)
        }, {
            callback?.onResult(true, null, null)
        }) {
            callback?.onResult(false, it.message, null)
        }
    }

    /**
     * 查询数据:无需开启事务
     * realm 查询速度贼快(纯查询操作不放子线程也可)
     */
    fun queryPerson(email: String): PersonModel? {
        val realm = Realm.getDefaultInstance()
        //  realm.where(PersonModel::class.java).findAll().forEach { DebugLog.d("$it") }
        // 查询指定邮箱且按id倒序的第一个PersonModel
        return realm.where(PersonModel::class.java).equalTo("email", email)
            .sort("id", Sort.DESCENDING).findFirst()
    }

    @Synchronized
    fun getNextId(): Long {
        return try {
            val realm = Realm.getDefaultInstance()
            val currentIdNum: Number? = realm.where(PersonModel::class.java).max("id")
            if (currentIdNum != null) currentIdNum.toLong() + 1 else 1L
        } catch (e: Exception) {
            val errorMsg = "Exception during getPrimaryKey(): " + e.message
            throw RealmPrimaryKeyConstraintException(errorMsg)
        }
    }

}

3.4 RealmCallback DB操作回调

package com.hawky.hawkysdk.realm.realmdao

import io.realm.RealmModel

interface RealmCallback {
    fun onResult(success: Boolean, error: String?, result: RealmModel?)
}

3.5 HawkyRealm DB配置初始化

package com.hawky.hawkysdk.realm

import android.content.Context
import io.realm.DynamicRealm
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.RealmMigration

object HawkyRealm {
    private const val REALM_DATABASE_VERSION: Long = 1
    private const val REALM_DATABASE_NAME = "hawky.realm"

    /**
     * 初始化 Realm 参数配置
     */
    fun init(context: Context) {
        Realm.init(context)

        val config = RealmConfiguration.Builder()
            .name(REALM_DATABASE_NAME) // 设置库名,默认为 "default.realm"
            .schemaVersion(REALM_DATABASE_VERSION) // 设置版本
            // 可指定初始化的数据库文件:
            // Realm会把assets路径下的xxx.realm文件拷贝到Context.getFilesDir()默认目录中,
            // 以替换默认创建的空数据库文件。
            // .assetFile("xxx.realm")
            .migration(userMigration) // 升级
            .encryptionKey(getRealmKey())// 加密
            .build()

        // 设置config 为realm 默认配置,
        // 之后可通过 Realm.getDefaultInstance() 来创建或从缓存中拿到 Realm 对象。
        Realm.setDefaultConfiguration(config)

        // 当然也可以不进行 setDefaultConfiguration操作,
        // 通过 Realm.getInstance(config) 也可获取 Realm 对象。
    }

    /**
     * DB 升级使用
     */
    private val userMigration =
        RealmMigration { realm: DynamicRealm, oldVersion: Long, newVersion: Long ->
            //...
        }

    /**
     * NOTE:Realm 密钥必须是64个字节,可自定义
     */
    private fun getRealmKey() = byteArrayOf(
        0x03, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, 0x02,
        0x03, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, 0x02,
        0x03, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, 0x02,
        0x03, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, 0x02,
        0x03, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, 0x02,
        0x03, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, 0x02,
        0x03, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, 0x02,
        0x03, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, 0x02
    )

}

4)子模块 app

4.1 build.gradle

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-kapt'
    id 'realm-android'
}

android {
    // namespace:注意这个不是打包的包名,而是一些自动生成的类会引用到(import package)
    namespace 'com.hawky.realm'
    compileSdk rootProject.ext.compileSdkVersion

    // 配置签名
    signingConfigs {
        release {
            storeFile file('../keystore/hawky.jks')
            storePassword "hawky2023!"
            keyAlias "hawky"
            keyPassword "hawky2023!"
        }
    }

    defaultConfig {
        applicationId "com.hawky.realm"
        minSdk 24
        targetSdk 33
        multiDexEnabled true
        flavorDimensions "brand"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    // 配置编译类型
    buildTypes {
        release {
            minifyEnabled true// release版本使用混淆
            buildConfigField "boolean", "IS_DEBUGENV", 'false'
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }

        debug {
            minifyEnabled false// debug版本不使用混淆
            buildConfigField "boolean", "IS_DEBUGENV", 'true'
        }

        // 对打包的 apk 重命名
        applicationVariants.all {
            variant ->
                variant.outputs.each {
                    output ->
                        def formattedDate = new Date().format('yyyyMMdd-HHmm')
                        def newName = output.outputFile.name
                        newName = newName.replace("-release", "-release-" + formattedDate)
                        newName = newName.replace("-debug", "-debug-" + formattedDate)
                        output.outputFileName = newName
                }
        }
    }

    // 多渠道打包(applicationIdSuffix 改包名)
    // 若直接 build 打包,则会默认使用最后一个Flavor配置,apk生成路径:build/outputs/apk/huawei/
    // 若使用 签名 打包(如选择huawei),则apk生成路径 /app/huawei
    productFlavors {
        xiaomi {
            dimension "brand"
            applicationIdSuffix ".xiaomi"
            versionCode 1
            versionName "1.0"
            buildConfigField 'String', 'brand_id', '"xiaomi"'
        }

        huawei {
            dimension "brand"
            applicationIdSuffix ".huawei"
            versionCode 1
            versionName "1.0"
            buildConfigField "String", "brand_id", '"huawei"'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }

    /*lintOptions {
        abortOnError false
        checkReleaseBuilds true
    }*/

    kotlinOptions {
        jvmTarget = '17'
    }

    // 开启 dataBinding
    dataBinding {
        enabled = true
    }
}

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

    implementation "androidx.core:core-ktx:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    implementation 'com.android.support:multidex:1.0.3'
    implementation project(path: ':hawkybase')
    implementation project(path: ':hawkysdk-realm')
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

4.2 RealmApplication

class RealmApplication : MultiDexApplication() {
    companion object {
        lateinit var instance: RealmApplication
            private set
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
        // init RealmDB
        HawkyRealm.init(this)
    }
}

4.3 User 实体类 

class User : BaseObservable() {
    var id: Long = 0

    @Bindable
    var name: String? = null
        set(value) {
            field = value
            notifyPropertyChanged(BR._all)
        }

    @Bindable
    var email: String? = null
        set(value) {
            field = value
            notifyPropertyChanged(BR._all)
        }
}

4.4 MainActivity 测试类

package com.hawky.realm.ui

import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.hawky.hawkybase.DebugLog
import com.hawky.hawkysdk.realm.realmdao.PersonDao
import com.hawky.hawkysdk.realm.realmdao.RealmCallback
import com.hawky.hawkysdk.realm.realmmodel.PersonModel
import com.hawky.realm.R
import com.hawky.realm.RealmApplication
import com.hawky.realm.databinding.ActivityMainBinding
import com.hawky.realm.model.User
import io.realm.RealmModel

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    //private lateinit var realm: Realm

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        initView()
        initData()
    }

    private fun initView() {
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    }

    private fun initData() {
        // realm = Realm.getDefaultInstance()
        binding.event = HandleClickEvent()
    }

    // 处理点击事件
    inner class HandleClickEvent {
        fun addUser(view: View) {
            val person = PersonModel()
            person.id = PersonDao.getNextId()
            person.name = "张三"
            person.email = "123@qq.com"
            PersonDao.savePerson(person, object : RealmCallback {
                override fun onResult(success: Boolean, error: String?, result: RealmModel?) {
                    if (success) {
                        Toast.makeText(RealmApplication.instance, "ADD OK", Toast.LENGTH_SHORT)
                            .show()
                    } else {
                        Toast.makeText(RealmApplication.instance, error, Toast.LENGTH_SHORT).show()
                    }
                }
            })
        }

        fun deleteUser(view: View) {
            PersonDao.deletePerson(0, object : RealmCallback {
                override fun onResult(success: Boolean, error: String?, result: RealmModel?) {
                    if (success) {
                        Toast.makeText(RealmApplication.instance, "DEL OK", Toast.LENGTH_SHORT)
                            .show()
                    } else {
                        Toast.makeText(RealmApplication.instance, error, Toast.LENGTH_SHORT).show()
                    }
                }
            })
        }

        fun updateUser(view: View) {
            val person = PersonModel()
            person.id = 1
            person.name = "张三"
            person.email = "123456@qq.com"
            PersonDao.updatePerson(person, object : RealmCallback {
                override fun onResult(success: Boolean, error: String?, result: RealmModel?) {
                    if (success) {
                        Toast.makeText(RealmApplication.instance, "UPDATE OK", Toast.LENGTH_SHORT)
                            .show()
                        person.run {
                            val user = User()
                            user.id = id
                            user.name = name
                            user.email = email
                            binding.user = user
                        }
                    } else {
                        Toast.makeText(RealmApplication.instance, error, Toast.LENGTH_SHORT).show()
                    }
                }
            })
        }

        fun queryUser(view: View) {
            val person = PersonDao.queryPerson("123@qq.com")
            DebugLog.d("person:${person}")
            person?.run {
                val user = User()
                user.id = id
                user.name = name
                user.email = email
                binding.user = user
            }
        }

    }

    override fun onDestroy() {
        super.onDestroy()
        // 同一个Realm实例操作完成后,记得close()
        // realm.close()
        binding.unbind()
    }

}

4.5 activity_main 布局文件

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="com.hawky.realm.model.User" />

        <variable
            name="event"
            type="com.hawky.realm.ui.MainActivity.HandleClickEvent" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <Button
            android:id="@+id/btn_add"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:onClick="@{event::addUser}"
            android:text="添加数据" />

        <Button
            android:id="@+id/btn_delete"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:onClick="@{event::deleteUser}"
            android:text="删除数据" />

        <Button
            android:id="@+id/btn_modify"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:onClick="@{event::updateUser}"
            android:text="修改数据" />

        <Button
            android:id="@+id/btn_query"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:onClick="@{event::queryUser}"
            android:text="查询数据" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:background="#BBBBBB"
            android:gravity="center"
            android:text="@{@string/nameFormat(user.id,user.name,user.email)}" />

    </LinearLayout>
</layout>

 字符引用:<string name="nameFormat">%s---%s---%s</string>

小结

通过本文,相信大家对 Gradle 8.0 的配置说明 及 Realm 数据库实例操作有了一定了解。后面计划:1. 添加Realm 数据迁移实例内容。2.以task生成aar的形式,加快编译速度。

最后,代码已上传至GitHub:hawky-kotlin-realm

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。