Adds R8 use cases to the repo

down-call-native
Ajesh R 1 month ago
parent 689ef92e41
commit a282b3e72e

@ -3,7 +3,6 @@
# Obsfuscation must be disabled for the build variant that generates Baseline Profile, otherwise
# wrong symbols would be generated. The generated Baseline Profile will be properly applied when generated
# without obfuscation and your app is being obfuscated.
-dontobfuscate
# Please add these rules to your existing keep rules in order to suppress warnings.
# This is generated automatically by the Android Gradle plugin.

@ -70,6 +70,7 @@ android {
}
dependencies {
implementation(projects.app.nativelib)
implementation(projects.feature.interests)
implementation(projects.feature.foryou)
implementation(projects.feature.bookmarks)

@ -0,0 +1 @@
/build

@ -0,0 +1,13 @@
plugins {
id("java-library")
alias(libs.plugins.kotlin.jvm)
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11
}
}

@ -0,0 +1,4 @@
package com.example.lib
class MyClass {
}

@ -0,0 +1 @@
/build

@ -0,0 +1,54 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "com.example.nativelib"
compileSdk = 36
defaultConfig {
minSdk = 21
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
externalNativeBuild {
cmake {
cppFlags("")
}
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
externalNativeBuild {
cmake {
path("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.test.espresso.core)
}

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -0,0 +1,24 @@
package com.example.nativelib
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.nativelib.test", appContext.packageName)
}
}

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

@ -0,0 +1,38 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
project("nativelib")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
# is preferred for the same purpose.
#
# In order to load a library into your app from Java/Kotlin, you must call
# System.loadLibrary() and pass the name of the library defined here;
# for GameActivity/NativeActivity derived applications, the same library name must be
# used in the AndroidManifest.xml file.
add_library(${CMAKE_PROJECT_NAME} SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
nativelib.cpp
)
# Specifies libraries CMake should link to your target library. You
# can link libraries from various origins, such as libraries defined in this
# build script, prebuilt third-party libraries, or Android system libraries.
target_link_libraries(${CMAKE_PROJECT_NAME}
# List libraries link to the target library
android
log)

@ -0,0 +1,10 @@
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_nativelib_NativeLib_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}

@ -0,0 +1,18 @@
package com.example.nativelib
class NativeLib {
/**
* A native method that is implemented by the 'nativelib' native library,
* which is packaged with this application.
*/
external fun stringFromJNI(): String
companion object {
// Used to load the 'nativelib' library on application startup.
init {
System.loadLibrary("nativelib")
}
}
}

@ -0,0 +1,17 @@
package com.example.nativelib
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

@ -16,12 +16,15 @@
package com.google.samples.apps.nowinandroid
import android.os.Build.VERSION_CODES
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -50,7 +53,10 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import com.example.nativelib.NativeLib
import javax.inject.Inject
import kotlin.reflect.KVisibility
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@ -79,6 +85,8 @@ class MainActivity : ComponentActivity() {
val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
accessSecretMessage(LibraryClass())
// We keep this as a mutable state, so that we can track changes inside the composition.
// This allows us to react to dark/light mode changes.
var themeSettings by mutableStateOf(

@ -0,0 +1,42 @@
/*
* Copyright 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid
import android.util.Log
val TAG: String = "R8"
/**
* A class with private members to demonstrate reflection.
*/
class LibraryClass {
private val secretMessage: Message = Message("R8 will remove me")
}
data class Message(
val message: String,
val id: Int = 0
)
// In your app code:
fun accessSecretMessage(instance: LibraryClass) {
// Use Java reflection from Kotlin to access the private field
val secretField = instance::class.java.getDeclaredField("secretMessage")
secretField.isAccessible = true
val message = secretField.get(instance) as Message
Log.d(TAG, message.toString())
}

@ -60,5 +60,6 @@ plugins {
alias(libs.plugins.roborazzi) apply false
alias(libs.plugins.secrets) apply false
alias(libs.plugins.room) apply false
alias(libs.plugins.module.graph) apply true // Plugin applied to allow module graph generation
alias(libs.plugins.module.graph) apply true
alias(libs.plugins.kotlin.android) apply false // Plugin applied to allow module graph generation
}

@ -2,3 +2,11 @@
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite* {
<fields>;
}
-keepclassmembers,includedescriptorclasses class com.google.samples.apps.nowinandroid.LibraryClass {
private * secretMessage;
}
-printconfiguration r8/full-r8-config.txt # Prints the entire configuration for the app
-printusage r8/usage.txt # Prints where R8 removed code from the app
-printseeds r8/seeds.txt

@ -18,7 +18,7 @@ androidxEspresso = "3.6.1"
androidxHiltNavigationCompose = "1.2.0"
androidxLifecycle = "2.8.7"
androidxLintGradle = "1.0.0-alpha03"
androidxMacroBenchmark = "1.3.4"
androidxMacroBenchmark = "1.4.0-beta02"
androidxMetrics = "1.0.0-beta01"
androidxNavigation = "2.8.5"
androidxProfileinstaller = "1.4.1"
@ -59,6 +59,9 @@ room = "2.6.1"
secrets = "2.0.1"
truth = "1.4.4"
turbine = "1.2.0"
junit = "4.13.2"
junitVersion = "1.2.1"
material = "1.12.0"
[bundles]
androidx-compose-ui-test = ["androidx-compose-ui-test", "androidx-compose-ui-testManifest"]
@ -161,6 +164,9 @@ firebase-performance-gradlePlugin = { group = "com.google.firebase", name = "per
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
room-gradlePlugin = { group = "androidx.room", name = "room-gradle-plugin", version.ref = "room" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
@ -198,3 +204,4 @@ nowinandroid-android-room = { id = "nowinandroid.android.room" }
nowinandroid-android-test = { id = "nowinandroid.android.test" }
nowinandroid-hilt = { id = "nowinandroid.hilt" }
nowinandroid-jvm-library = { id = "nowinandroid.jvm.library" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

@ -83,3 +83,4 @@ check(JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {
https://developer.android.com/build/jdks#jdk-config-in-studio
""".trimIndent()
}
include(":app:nativelib")

@ -0,0 +1,36 @@
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.google.samples.apps.nowinandroid/com.google.samples.apps.nowinandroid.MainActivity}: java.util.NoSuchElementException: List is empty.
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4048)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4235)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:112)
at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:174)
at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:109)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:81)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2636)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loopOnce(Looper.java:232)
at android.os.Looper.loop(Looper.java:317)
at android.app.ActivityThread.main(ActivityThread.java:8705)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886)
Caused by: java.util.NoSuchElementException: List is empty.
at i4.k.m0(SourceFile:22)
at i4.k.l0(SourceFile:12)
at com.google.samples.apps.nowinandroid.MainActivity.onCreate(SourceFile:54)
at android.app.Activity.performCreate(Activity.java:9002)
at android.app.Activity.performCreate(Activity.java:8980)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1526)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4030)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4235) 
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:112) 
at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:174) 
at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:109) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:81) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2636) 
at android.os.Handler.dispatchMessage(Handler.java:107) 
at android.os.Looper.loopOnce(Looper.java:232) 
at android.os.Looper.loop(Looper.java:317) 
at android.app.ActivityThread.main(ActivityThread.java:8705) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886) 
Loading…
Cancel
Save