Setting up Protobuf + Kotlin in Android Studio 2023 - android

I spend hours on just setting up Protobuf with Kotlin in Android Studio. The endgoal is just that my proto files are compiled in Kotlin and that I can use them in my project.
I have an example project here: https://github.com/Jasperav/ProtobufAndroid. It mimics my setup in the real application: an external dir containing the proto files and the android project. It contains all the code mentioned below. This is a combined effort of tutorials I found on the internet. It is probably terrible wrong. I tried https://github.com/google/protobuf-gradle-plugin, but it just looks so complicated for something simple I am doing:
Have a dir with protofiles somewhere on your filesystem
Create a new Android project on Kotlin
In the Project build.gradle, add id 'com.google.protobuf' version '0.9.2' apply false as plugin
In the Module build.gradle, add ->
This to the dependencies: implementation 'com.google.protobuf:protobuf-lite:3.21.12'
The sourceSets at the bottom inside the android bracket
The protobuf section at the bottom between the dependencies and android section.
sourceSets:
sourceSets {
main {
kotlin {
srcDirs += 'build/generated/source/proto/main/kotlin'
}
proto {
srcDir '/Users/me/androidkotlin/proto'
}
}
}
protobuf:
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.21.12'
}
plugins {
kotlinlite {
artifact = 'com.google.protobuf:protoc-gen-kotlin:3.21.12'
}
}
generateProtoTasks {
ofSourceSet("main").forEach { task ->
task.builtins {
getByName("kotlin") {
option("lite")
}
}
}
}
}
I get this error:
A problem occurred evaluating project ':app'.
> Could not find method proto() for arguments [build_cxwfo79b6zcc266x9rsqzou9f$_run_closure1$_closure8$_closure10$_closure12#203aac02] on source set main of type com.android.build.gradle.internal.api.DefaultAndroidSourceSet.

You are in a good way, but, there is some stuff missing:
The gradle code I'll share is written in Kotlin, just in case. If you can convert your grade files to Kotlin, nice, if not you have to convert them to groovy.
The first thing to check is if you have the proto folder in the right path, it should be in
root->app->src->main->proto
In the project gradle make sure to have the plugin applied
id("com.google.protobuf") version "0.8.15" apply false
In the app gradle, make sure to have the following.
import com.google.protobuf.gradle.*
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.protobuf")
}
The dependencies:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2")
implementation("com.google.protobuf:protobuf-kotlin:3.21.2")
implementation("io.grpc:grpc-stub:1.52.0")
implementation("io.grpc:grpc-protobuf:1.52.0")
implementation("io.grpc:grpc-okhttp:1.52.0")
implementation("com.google.protobuf:protobuf-java-util:3.21.7")
implementation("com.google.protobuf:protobuf-kotlin:3.21.2")
implementation("io.grpc:grpc-kotlin-stub:1.3.0")
And the protobuf task:
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:${rootProject.ext["protobufVersion"]}"
}
plugins {
id("java") {
artifact = "io.grpc:protoc-gen-grpc-java:${rootProject.ext["grpcVersion"]}"
}
id("grpc") {
artifact = "io.grpc:protoc-gen-grpc-java:${rootProject.ext["grpcVersion"]}"
}
id("grpckt") {
artifact = "io.grpc:protoc-gen-grpc-kotlin:${rootProject.ext["grpcKotlinVersion"]}:jdk8#jar"
}
}
generateProtoTasks {
all().forEach {
it.plugins {
id("java") {
option("lite")
}
id("grpc") {
option("lite")
}
id("grpckt") {
option("lite")
}
}
it.builtins {
id("kotlin") {
option("lite")
}
}
}
}
}
These are the versions I'm using:
ext["grpcVersion"] = "1.47.0"
ext["grpcKotlinVersion"] = "1.3.0" // CURRENT_GRPC_KOTLIN_VERSION
ext["protobufVersion"] = "3.21.2"
ext["coroutinesVersion"] = "1.6.2"
Having that your project should generate the code based on your proto files.
For further reference, I recently build this Android App based on Kotlin + gRPC: https://github.com/wilsoncastiblanco/notes-grpc

Related

How to setup protobuf in Android/IOS Kotlin Multiplatform project

First time using protobu and could not find an example of how to connect it to Kotlin Multiplatform. I created Multiplatform project Android/IOS with "shared" module where I need to use protobuf. Project structure:
Code in build.gradle shared.module lvl
build.gradle shared.module lvl
plugins {
kotlin("multiplatform")
id("com.android.library")
id("com.google.protobuf")
}
kotlin {
android()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting
val androidTest by getting
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
}
}
android {
namespace = "com.example.someproject"
compileSdk = 33
defaultConfig {
minSdk = 21
targetSdk = 33
}
protobuf {
protoc {
artifact = ("com.google.protobuf:protoc:3.21.9")
}
generateProtoTasks {
all().forEach { task ->
task.builtins {
java {
// option 'lite' //Error - Unresolved reference: option
}
kotlin {
// option 'lite' //Error - Unresolved reference: option
}
}
}
}
}
dependencies {
implementation("com.google.protobuf:protobuf-javalite:3.21.9")
}
}
My build.gradle project lvl:
build gradle project lvl
buildscript {
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.1'
}
}
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
id 'com.google.protobuf' version '0.9.1' apply false
}
I tried to add path to my .proto files with sourceSets, but I got errors:
android {
namespace = "com.example.someproject"
compileSdk = 33
defaultConfig {
minSdk = 21
targetSdk = 33
}
sourceSets {
main { //Error - Unresolved reference: main
proto { //Error - Unresolved reference: proto
srcDir 'src/main/protobuf' //Error - Unresolved reference: srcDir
}
java {
srcDirs 'build/generated/source/proto/main/java' //Error - Unresolved reference: srcDirs
srcDirs 'build/generated/source/proto/main/kotlin' //Error - Unresolved reference: srcDirs
}
}
}
protobuf {
protoc {
artifact = ("com.google.protobuf:protoc:3.21.9")
}
generateProtoTasks {
all().forEach { task ->
task.builtins {
java {
// option 'lite' //Error - Unresolved reference: option
}
kotlin {
// option 'lite' //Error - Unresolved reference: option
}
}
}
}
}
dependencies {
implementation("com.google.protobuf:protobuf-javalite:3.21.9")
}
}
When I try to build project without sourceSets, I didn't get generated java/kotlin files in build/generated/source - path
And my question is: "How setup protobuf in Android/IOS project Kotlin multiplatform?"
If you want to use ProtoBuf library in shared module in common code, then you have to use library that supports Kotlin Multiplatform. You can take a look at official kotlinx.serialization library, it supports ProtoBuf. And you can use it in commonMain module.
Also take a look at community library
Or learn more about platform-specific implementations and use expect/actual mechanism
Another option will be use your ProtoBuf library and plugin in androidApp only and for iOS part use another iOS specific ProtoBuf library.

Could not resolve git repository as dependency in android gradle

I am trying to add a git repository (https://github.com/FHNW-IP5-IP6/ComposeForms) as a dependency into my project with Gradle and tried the below-listed variants (1.-3.) from Is it possible to declare git repository as dependency in android gradle? but every time when I sync the project I get an Error saying: "Could not resolve com.github.FHNW-IP5-IP6:ComposeForms:master-SNAPSHOT".
I tried the following:
Jitpack (https://jitpack.io/#FHNW-IP5-IP6/ComposeForms/master-SNAPSHOT)
allprojects {
repositories {
...
maven("https://jitpack.io") // also tried uri https://www.jitpack.io
}
}
and in app build.gradle
kotlin {
sourceSets {
named("main") {
dependencies {
...
implementation("com.github.FHNW-IP5-IP6:ComposeForms:master-SNAPSHOT")
}
}
}
}
Git Submodule (named as compose-forms)
include(":compose-forms") inside settings.gradle
kotlin {
sourceSets {
named("main") {
dependencies {
...
implementation(project(":compose-forms"))
}
}
}
}
New feature in gradle
Inside settings.gradle
sourceControl {
gitRepository(uri("https://github.com/FHNW-IP5-IP6/ComposeForms.git")) {
producesModule("compose-forms")
}
}
and in app build.gradle
kotlin {
sourceSets {
named("main") {
dependencies {
...
implementation("compose-forms") {
version {
branch = "master"
}
}
}
}
}
}
I'm running out of options and really need the git repository as a dependency. I would prefer not to have any git submodules inside my project so I prefer numbers 1 and 3 to work. Thanks in Advance for any hint :)
Open Android Studio as Administrator then add maven { url 'https://jitpack.io' } to both, build.gradle(project) and settings.gradle(project) and the respective implementation [...] to build.gradle(:app). This worked for me as every other solution proposed failed.

Protobuf gRPC - google protobuf package does not exist

I am trying to implement gRPC and now I'm having all sorts of issues, but I just don't get what I'm doing wrong. I am following this doc:
https://github.com/grpc/grpc-java/blob/master/README.md
And now I keep getting such errors when I'm trying to build my project
error: package com.google.protobuf.GeneratedMessageV3 does not exist
com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
In my Android Studio external libraries I have protobuf-java-3.12.1 jar.
In my project gradle file I've added this to dependencies:
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.14'
And in my app gradle file:
apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'
In dependencies I added:
implementation 'io.grpc:grpc-okhttp:1.35.0'
implementation 'io.grpc:grpc-protobuf-lite:1.35.0'
implementation 'io.grpc:grpc-stub:1.35.0'
compileOnly 'org.apache.tomcat:annotations-api:6.0.53'
implementation 'com.google.protobuf:protobuf-javalite:3.12.1'
And outside of the android tag:
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.12.1"
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.35.0'
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}
Finally, my proto file:
syntax = "proto3";
import "google/protobuf/timestamp.proto";
option java_package = "com.xxx.xxx.proto.log";
option java_outer_classname = "MyClass";
message MyObject {
string name = 1;
string unit = 2;
oneof value {
bool bool_value = 3;
sint32 int32_value = 4;
uint32 u_int32_value = 5;
google.protobuf.Timestamp timestamp_value = 6;
}
}
When I run: protoc --version in terminal, this is the output:
libprotoc 3.12.1
Do I have to add something else or I missed something in my Gradle setup?
It looks like you are hoping to use protobuf lite (as is normal for Android projects):
implementation 'io.grpc:grpc-protobuf-lite:1.35.0'
...
implementation 'com.google.protobuf:protobuf-javalite:3.12.1'
However, currently you are generating full protobuf code. You need to tell the java and grpc-java code generators to generate for lite via options. As seen in the grpc-java Android helloworld example:
protobuf {
...
generateProtoTasks {
all().each { task ->
task.builtins {
java { option 'lite' }
}
task.plugins {
grpc { option 'lite' }
}
}
}
}

Kotlin Multiplatform Configuration issue

I continue getting Gradle configuration error in my KMP + Jetpack Compose project
A problem occurred configuring project ':shared'.
Configuration with name 'testApi' not found.
My setup is:
Android Studio Arctic Fox 2020.3.1 Canary 3
Project level setup
dependencies {
classpath("com.android.tools.build:gradle:7.0.0-alpha03")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20")
}
'shared module'
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
kotlin("multiplatform")
id("com.android.library")
}
kotlin {
android()
ios {
binaries {
framework {
baseName = "shared"
}
}
}
sourceSets {
val commonMain by getting
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val androidMain by getting {
dependencies {
implementation("com.google.android.material:material:1.2.1")
}
}
val androidTest by getting {
dependencies {
implementation(kotlin("test-junit"))
implementation("junit:junit:4.13.1")
}
}
val iosMain by getting
val iosTest by getting
}
}
android {
compileSdkVersion(30)
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdkVersion(21)
targetSdkVersion(30)
}
}
val packForXcode by tasks.creating(Sync::class) {
group = "build"
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator"
val targetName = "ios" + if (sdkName.startsWith("iphoneos")) "Arm64" else "X64"
val framework = kotlin.targets.getByName<KotlinNativeTarget>(targetName).binaries.getFramework(mode)
inputs.property("mode", mode)
dependsOn(framework.linkTask)
val targetDir = File(buildDir, "xcode-frameworks")
from({ framework.outputDirectory })
into(targetDir)
}
tasks.getByName("build").dependsOn(packForXcode)
Note:
By removing the configuration part by part, I seem to figure out w
that the problem seems to be around the android configuration itself,
so if I remove android() part from
kotlin {
android()
....
and just go with simple jvm() it goes well
You can use the below code as a workaround in your shared module Gradle file
android {
configurations {
create("androidTestApi")
create("androidTestDebugApi")
create("androidTestReleaseApi")
create("testApi")
create("testDebugApi")
create("testReleaseApi")
}
}
NOTE: This has to be put before the kotlin {} block
Fixed issue in Kotlin 1.5 M1 (pending)
The problem is in Canary or AGP 7.0.0:
IDE: Canary 11
distributionUrl: 6.8.2
7.0.0-alpha11
Workaround 1:
IMPORTANT: Make sure the file is groovy or dsl
PRECONDITION: These configurations have to be done in all modules / sub-modules of the project that are KMM and the android {} block has to be before the kotlin {} block
For Kotlin DSL:
build.gradle.kts (:kmm_shared)
android {
configurations {
create("androidTestApi")
create("androidTestDebugApi")
create("androidTestReleaseApi")
create("testApi")
create("testDebugApi")
create("testReleaseApi")
}
}
kotlin { }
For Groovy:
build.gradle (:kmm_shared)
android {
configurations {
androidTestApi {}
androidTestDebugApi {}
androidTestReleaseApi {}
testApi {}
testDebugApi {}
testReleaseApi {}
}
}
kotlin { }
Also, you should to use AGP 7.0 because previous versions of gradle generate problems.
build.gradle.kts (:project) && build.gradle.kts (:buildSrc)
dependencies {
implementation("com.android.tools.build:gradle:7.0.0-alpha11")
}
Workaround 2 (Deprecated)
Temporarily use a maximum of the beta versions:
IDE: Beta 6
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
classpath 'com.android.tools.build:gradle:4.2.0-beta06'
Good Luck
issue/KT-43944
commit in Mobius

How to reuse parts of Android configuration in Kotlin Gradle DSL for multiple modules?

I have a multi-module Android project & Kotlin Gradle DSL. There is some configuration which has to be repeated in every module and I would like to reuse the code. I would like to reuse for example this code:
android {
sourceSets {
getByName("main").java.srcDirs("src/main/kotlin")
getByName("test").java.srcDirs("src/test/kotlin")
}
}
There are two methods documented in Kotlin DSL samples:
apply(from = "foo.gradle.kts")
and extension functions in buildSrc like this:
fun Project.kotlinProject() {
dependencies {
"compile"(kotlin("stdlib"))
}
}
However both these methods work only for top-level configuration, I can't access Android plugin's stuff. I'm getting errors like Unresolved reference: BaseExtension
At the end I was inspired by SUPERCILEX's code:
allprojects {
val parent = (group as String).split(".").getOrNull(1)
when {
name == "app" -> {
apply(plugin = "com.android.application")
configureAndroidModule()
}
parent == "common-android" -> {
apply(plugin = "com.android.library")
configureAndroidModule()
}
}
}
fun Project.configureAndroidModule() {
configure<BaseExtension> {
sourceSets {
getByName("main").java.srcDirs("src/main/kotlin")
getByName("test").java.srcDirs("src/test/kotlin")
}
}
}​
How about using subprojects block? I have a multi-module Android project and this is how I reuse code in my build scripts.
subprojects {
apply plugin: 'com.android.library'
android {
sourceSets {
getByName("main").java.srcDirs("src/main/kotlin")
getByName("test").java.srcDirs("src/test/kotlin")
}
}
}
Unresolved reference: BaseExtension
As for the above error message, if you want to use android block, you should declare your modules as android application or library by applying plugins like above build script.
If you want the settings to be repeated in only some of the modules, you can use configure block like this:
configure(subprojects - project(':${module_name}')) {
dependencies {
implementation 'com.x.y.z:abc:1.0.0'
}
}
The above block will define the dependency on all modules except the module with the given name.

Categories

Resources