您现在的位置是:首页 >技术交流 >AS Gradle 8.0 配置 + Realm 使用网站首页技术交流
AS Gradle 8.0 配置 + Realm 使用
目录
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。