What is wrong with my configuration or code ?
I have this error highlighted
Cannot resolve method 'plant(timber.log.Timber.DebugTree)'
for the code
import timber.log.Timber;
public class AppClass extends Application {
#Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); }
}
}
but it builds and it executes. Still I think it means something, no ?
Configuration infos:
Android Studio Bumblebee | 2021.1.1
classpath 'com.android.tools.build:gradle:7.1.0'
Gradle: com.jakewharton.timber:timber:5.0.1#aar
ext.kotlin_version = '1.6.10'
sourceCompatibility JavaVersion.VERSION_1_8
Until issue fixed (as #n8yn8 noted in question comment) I solved it with downgrade to version 4.7.1:
implementation 'com.jakewharton.timber:timber:4.7.1'
In app level build.gradle file, set the following jakewharton timber version:
implementation 'com.jakewharton.timber:timber:4.7.1'
Then in your application class onCreate() Method:
For Kotlin:
if (BuildConfig.DEBUG) {
Timber.plant(DebugTree())
} else {
Timber.plant(ReleaseTree())
}
For Java:
if (BuildConfig.DEBUG) {
Timber.plant(new DebugTree());
} else {
Timber.plant(new ReleaseTree());
}
Inner ReleaseTree() class Kotlin:
inner class ReleaseTree : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
if (priority == Log.VERBOSE || priority == Log.DEBUG) {
return
}
// log your crash to your favourite
// Sending crash report to Firebase CrashAnalytics
// FirebaseCrash.report(message);
// FirebaseCrash.report(new Exception(message));
}
}
Inner ReleaseTree() class Java:
class ReleaseTree extends Timber.Tree {
#Override
protected void log(int priority, String tag, String message, Throwable t) {
if (priority == Log.VERBOSE || priority == Log.DEBUG) {
return;
}
// log your crash to your favourite
// Sending crash report to Firebase CrashAnalytics
// FirebaseCrash.report(message);
// FirebaseCrash.report(new Exception(message));
}
}
For the workaround solution without downgrade dependency version and also no need to apply with another dependency by keep applying the one from JakeWharton, we can try to config Timber in Kotlin instead of Java class since the warning message only appear on Java class.
By doing so, you can try two options bellow:
Convert your custom application class from Java to Kotlin
Create another class in Kotlin and create new method to config Timber with sample bellow:
TimberUtils.kt
import timber.log.Timber
object TimberUtils {
#JvmStatic
fun configTimber() {
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
}
}
YourCustomJavaClass.java
#Override
public void onCreate() {
super.onCreate();
TimberUtils.configTimber();
}
Hope it can resolve your problem.
For those using sentry-timber
Just use
implementation "io.sentry:sentry-android:$sentry_version"
implementation "io.sentry:sentry-android-timber:$sentry_version"
Remove this dependency
implementation "com.jakewharton.timber:timber:$timber_version"
For me, this fix resolves the issue
Related
After updated koin and gradle, the following error prompt
Caused by: org.koin.core.error.DefinitionOverrideException: Already existing definition for [Singleton:'java.lang.String'] at java.lang.String::_root_
I don't know where is the cause of this error.
Here my Application files:
Class MyApplication --> With 2 modules, I'm importing startKoin from: import org.koin.core.context.GlobalContext.startKoin
class MyApplication : Application(), OnMapsSdkInitializedCallback {
override fun onCreate() {
super.onCreate()
startKoin{
if(BuildConfig.DEBUG){
androidLogger(Level.DEBUG)
Timber.plant(Timber.DebugTree())
}
androidContext(this#MyApplication)
modules(applicationModule, viewModelModule)
}
MapsInitializer.initialize(applicationContext, Renderer.LATEST, this)
}
override fun onMapsSdkInitialized(renderer: Renderer) {
when (renderer) {
Renderer.LATEST -> Timber.d("The latest version of the google maps renderer is used.")
Renderer.LEGACY -> Timber.d("The legacy version of the google maps renderer is used.")
}
}
}
MODULE 1 -- applicationModule
// declare a module
val applicationModule = module {
single { BuildConfig.SOME_STRING1 }
single { BuildConfig.SOME_STRING2 }
single { BuildConfig.SOME_STRING3 }
single { BuildConfig.SOME_STRING4 }
single {
Environment(get(named("SOME_STRING1")),
get(named("SOME_STRING2")),
get(named("SOME_STRING3")),
get(named("SOME_STRING4")), get())
} bind GrpcConfiguration::class
//endregion
//region Channel
single {
GrpcChannelBuilder.Companion.prepare(get(), get())
}
//endregion
//region Repository
single { SomeDataRepository(get()) } bind SomeRepository::class
//endregion
//region XXXXProvider
single { XXXXProvider(get()) }
//endregion
//region REST API
single (named("RETROFIT")){
Retrofit.Builder().client(get())
.baseUrl("xxxxxxxxxx")
.addConverterFactory(GsonConverterFactory.create())
.build()
} bind Retrofit::class
//region REST API
single {
val l = HttpLoggingInterceptor()
l.level = HttpLoggingInterceptor.Level.BODY
OkHttpClient.Builder().addInterceptor(l).build()
}
single {
get<Retrofit>(named("RETROFIT")).create(XXXXService::class.java)
} bind XXXXService::class
//endregion
//region DB
single {
MyAppDatabase.getDatabase(get()).someDao()
} bind SomeDao::class
//endregion
//region Util
single {
DarkModeUtil()
} bind DarkModeUtil::class
//endregion
}
Module 2 -- viewModelModule (only for viewmodels)
// declare a module
val viewModelModule: Module = module {
viewModel { HomeViewModel(get(), get(), get()) }
}
build.gradle(:app) KOIN dependencies:
def koin_version = "3.1.6" (I've tried with 3.2.2 and 3.2.1 and got the same error)
// Koin main features for Android
implementation "io.insert-koin:koin-android:$koin_version"
// No more koin-android-viewmodel, koin-android-scope, koin-android-fragment
// Java Compatibility
implementation "io.insert-koin:koin-android-compat:$koin_version"
// Jetpack WorkManager
implementation "io.insert-koin:koin-androidx-workmanager:$koin_version"
// Navigation Graph
implementation "io.insert-koin:koin-androidx-navigation:$koin_version"
ANDROID GRADLE PLUGIN VERSION: 7.2.2
GRADLE VERSION: 7.5
KOTLIN VERSION: 1.7.10
Caused by: org.koin.core.error.DefinitionOverrideException:
Already existing definition for [Singleton:'java.lang.String'] at java.lang.String::_root_
The error indicates that there are many String type definitions without a qualifier.
These lines below are causing the problem.
single { BuildConfig.SOME_STRING1 }
single { BuildConfig.SOME_STRING2 }
If you would like to declare a definition with the same type, you can declare them by giving a name.
single(named("SOME_STRING1")) { BuildConfig.SOME_STRING1 }
single(named("SOME_STRING2")) { BuildConfig.SOME_STRING2 }
single(named("SOME_STRING3")) { BuildConfig.SOME_STRING3 }
single(named("SOME_STRING4")) { BuildConfig.SOME_STRING4 }
You already use named definitions to create an Environment object.
single {
Environment(get(named("SOME_STRING1")),
get(named("SOME_STRING2")), ...
Hope this helps.
I'm following the dev guide here:
https://developer.android.com/guide/components/activities/testing
and have a test class like:
#RunWith(AndroidJUnit4::class)
class MyTestSuite {
#get:Rule var activityScenarioRule = activityScenarioRule<MyActivity>()
#Test fun testEvent() {
val scenario = activityScenarioRule.scenario
}
}
the method activityScenarioRule<T>() is not defined. What dependency do I need? Also, what is the best way to determine which dependencies to add when reading these docs?
The activityScenarioRule<T>() method is part of the androidx.test.ext:junit-ktx:1.1.0 dependency.
Usually, this would be listed under the List of AndroidX Test dependencies, but it appears it isn't up to date with the junit-ktx or core-ktx modules as of yet, despite it being explicitly mentioned as part of the Version 1.1.0-beta01 release notes
If you are not using the ktx dependency, e.g. androidx.test.ext:junit:1.1.2 you can do it like this:
#get:Rule
var activityScenarioRule = ActivityScenarioRule(MyActivity::class.java)
I had a similar problem.
Changing dependency from testImplementation 'androidx.test.ext:junit:1.1.3'
to androidTestImplementation 'androidx.test.ext:junit:1.1.3' worked for me.
My example of usage ActivityScenarioRule<> for Java:
public class AdMobContainerImplTest {
private static final String TAG = AdMobContainerImplTest.class.getSimpleName();
#Rule
public ActivityScenarioRule<ENDetailsActivity> mActivityRule = new ActivityScenarioRule<>(
ENDetailsActivity.class);
#Test
public void testAdVisibility() {
mActivityRule.getScenario().onActivity(activity -> {
AdView ad = activity.findViewById(R.id.ad_banner);
ad.setAdListener(new AdListener() {
#Override
public void onAdLoaded() {
super.onAdLoaded();
Log.i(TAG, "ad loaded");
Assert.assertNotNull(ad);
onView(withId(R.id.ad_banner)).check(matches(isDisplayed()));
}
});
});
}
}
We're trying to use the org.robolectric:robolectric:3.0 dependency from our own internal Nexus repository. The issue is that Robolectric tries to load some dependencies at runtime from a public repository (as mentioned here), and ignores any repository overrides in the build.gradle.
Since we don't have access to that public location from our intranet, my tests timeout after trying to load that dependency:
[WARNING] Unable to get resource
'org.robolectric:android-all:jar:5.0.0_r2-robolectric-1' from
repository sonatype (https://oss.sonatype.org/content/groups/public/):
Error transferring file: Operation timed out
The bottom section of the Robolectric configuration documentation recommends adding this to your Gradle configuration to override the URL:
android {
testOptions {
unitTests.all {
systemProperty 'robolectric.dependency.repo.url', 'https://local-mirror/repo'
systemProperty 'robolectric.dependency.repo.id', 'local'
}
}
}
Unfortunately, I've tested that and I never see that system property being set. I've printed it out from inside my custom Robolectric runner (which extends RobolectricGradleTestRunner) and that system property remains set to null.
System.out.println("robolectric.dependency.repo.url: " + System.getProperty("robolectric.dependency.repo.url"));
I also tried to do something similar to this comment (but that method doesn't exist to override in RobolectricGradleTestRunner), and I also tried setting the system properties directly in my custom Robolectric runner, and that didn't seem to help.
#Config(constants = BuildConfig.class)
public class CustomRobolectricRunner extends RobolectricGradleTestRunner {
private static final String BUILD_OUTPUT = "build/intermediates";
public CustomRobolectricRunner(Class<?> testClass) throws InitializationError {
super(testClass);
System.setProperty("robolectric.dependency.repo.url", "https://nexus.myinternaldomain.com/content");
System.setProperty("robolectric.dependency.repo.id", "internal");
System.out.println("robolectric.dependency.repo.url: " + System.getProperty("robolectric.dependency.repo.url"));
}
The Robolectric source code does seem to confirm that these system properties exist.
While not a fix for using the properties directly, another way to get this to work is by overriding getJarResolver() in a RobolectricTestRunner subclass and pointing it at your artifact host:
public final class MyTestRunner extends RobolectricTestRunner {
public MyTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
#Override protected DependencyResolver getJarResolver() {
return new CustomDependencyResolver();
}
static final class CustomDependencyResolver implements DependencyResolver {
private final Project project = new Project();
#Override public URL[] getLocalArtifactUrls(DependencyJar... dependencies) {
DependenciesTask dependenciesTask = new DependenciesTask();
RemoteRepository repository = new RemoteRepository();
repository.setUrl("https://my-nexus.example.com/content/groups/public");
repository.setId("my-nexus");
dependenciesTask.addConfiguredRemoteRepository(repository);
dependenciesTask.setProject(project);
for (DependencyJar dependencyJar : dependencies) {
Dependency dependency = new Dependency();
dependency.setArtifactId(dependencyJar.getArtifactId());
dependency.setGroupId(dependencyJar.getGroupId());
dependency.setType(dependencyJar.getType());
dependency.setVersion(dependencyJar.getVersion());
if (dependencyJar.getClassifier() != null) {
dependency.setClassifier(dependencyJar.getClassifier());
}
dependenciesTask.addDependency(dependency);
}
dependenciesTask.execute();
#SuppressWarnings("unchecked")
Hashtable<String, String> artifacts = project.getProperties();
URL[] urls = new URL[dependencies.length];
for (int i = 0; i < urls.length; i++) {
try {
urls[i] = Util.url(artifacts.get(key(dependencies[i])));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
return urls;
}
#Override public URL getLocalArtifactUrl(DependencyJar dependency) {
URL[] urls = getLocalArtifactUrls(dependency);
if (urls.length > 0) {
return urls[0];
}
return null;
}
private String key(DependencyJar dependency) {
String key =
dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getType();
if (dependency.getClassifier() != null) {
key += ":" + dependency.getClassifier();
}
return key;
}
}
}
It should be noted that this relies on two internal classes of Robolectric so care should be taken when upgrading versions.
You can set properties mavenRepositoryId and mavenRepositoryUrl of RoboSettings which are used by MavenDependencyResolver.
Example:
public class CustomRobolectricRunner extends RobolectricGradleTestRunner {
static {
RoboSettings.setMavenRepositoryId("my-nexus");
RoboSettings.setMavenRepositoryUrl("https://my-nexus.example.com/content/groups/public");
}
...
}
As per the linked Github issue, one fix is to configure a settings.xml in your ~\.m2 folder:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<mirrors>
<mirror>
<id>jcenter</id>
<name>JCenter Remote</name>
<mirrorOf>*</mirrorOf>
<url>https://www.example.com/artifactory/jcenter-remote/</url>
</mirror>
</mirrors>
</settings>
<mirrorOf>*</mirrorOf> seems necessary to force Maven to redirect all repository requests to the one remote. See here for more details about mirror settings in Maven.
I found that using a remote of Sonatype is not sufficient, you should use a remote of JCenter or Maven Central in order to obtain all of the transitive dependencies.
As of time of this writing, those previous answers are now obsolete. If you refer to the latest robolectric documentation you need to override the robolectric.dependency.repo.url property like so:
android {
testOptions {
unitTests.all {
systemProperty 'robolectric.dependency.repo.url', 'https://local-mirror/repo'
systemProperty 'robolectric.dependency.repo.id', 'local'
}
}
}
I am creating an Android app using Google App Engine. In order to use GCM (Google Cloud Messaging), I have created a GCM module in Android Studio. This module provides a sample code that registers devices in the Datastore.
All was working well yesterday, and although nothing changed, I have this error when I try to register my device :
java.lang.NoSuchMethodError: com.google.appengine.api.datastore.Cursor: method <init>()V not found
I don't know what exactly means the notation <init>()V, but I found the Cursor class to be generated by the Google plugin of Android Studio, here :
This is the decompiled code inside Cursor.class :
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.google.appengine.api.datastore;
import java.io.Serializable;
public final class Cursor implements Serializable {
private String webString;
public Cursor(String webString) {
this.webString = webString;
}
public String toWebSafeString() {
return this.webString;
}
public static Cursor fromWebSafeString(String encodedCursor) {
if(encodedCursor == null) {
throw new NullPointerException("encodedCursor must not be null");
} else {
return new Cursor(encodedCursor);
}
}
public boolean equals(Object o) {
if(this == o) {
return true;
} else if(o != null && this.getClass() == o.getClass()) {
Cursor cursor = (Cursor)o;
return this.webString.equals(cursor.webString);
} else {
return false;
}
}
public int hashCode() {
return this.webString.hashCode();
}
public String toString() {
return this.webString;
}
}
Finally, this is my build.gradle :
// If you would like more information on the gradle-appengine-plugin please refer to the github page
// https://github.com/GoogleCloudPlatform/gradle-appengine-plugin
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.google.appengine:gradle-appengine-plugin:1.9.18'
}
}
repositories {
jcenter();
}
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'appengine'
sourceCompatibility = 1.7
targetCompatibility = 1.7
dependencies {
appengineSdk 'com.google.appengine:appengine-java-sdk:1.9.18'
compile 'com.google.appengine:appengine-endpoints:1.9.18'
compile 'com.google.appengine:appengine-endpoints-deps:1.9.18'
compile 'javax.servlet:servlet-api:2.5'
compile 'com.googlecode.objectify:objectify:4.0b3'
compile 'com.ganyo:gcm-server:1.0.2'
}
appengine {
downloadSdk = true
appcfg {
oauth2 = true
}
endpoints {
getClientLibsOnBuild = true
getDiscoveryDocsOnBuild = true
}
}
Because I changed nothing in the concerned code, I really can't understand what happened and I found nothing useful on the web.
Thank you in advance for your help.
Edit : StackTrace from the backend log
It's looking for the empty constructor : method init()v not found
It appears this could be because Cursor.java is pulled from your <module>/build/classes/main (or <module>/build/exploded-app/WEB-INF/classes/main), when really it should just be pulled in from a library appengine-api-1.0-sdk-<version>.jar.
Have you added the source for a Cursor.java into your project src folder somehow? The App Engine build creates a runnable build at <module>/build/exploded-app and the cursor class is usually sourced from <module>/build/exploded-app/WEB-INF/lib/appengine-api-1.0-sdk-<version>.jar
Is there an annotation or some other convenient way to ignore junit tests for specific Android SDK versions? Is there something similar to the Lint annotation TargetApi(x)? Or do I manually have to check whether to run the test using the Build.VERSION?
I don't think there is something ready but it pretty easy to create a custom annotation for this.
Create your custom annotation
#Target( ElementType.METHOD )
#Retention( RetentionPolicy.RUNTIME)
public #interface TargetApi {
int value();
}
Ovverride the test runner (that will check the value and eventually ignore/fire the test)
public class ConditionalTestRunner extends BlockJUnit4ClassRunner {
public ConditionalTestRunner(Class klass) throws InitializationError {
super(klass);
}
#Override
public void runChild(FrameworkMethod method, RunNotifier notifier) {
TargetApi condition = method.getAnnotation(TargetApi.class);
if(condition.value() > 10) {
notifier.fireTestIgnored(describeChild(method));
} else {
super.runChild(method, notifier);
}
}
}
and mark your tests
#RunWith(ConditionalTestRunner.class)
public class TestClass {
#Test
#TargetApi(6)
public void testMethodThatRunsConditionally() {
System.out.print("Test me!");
}
}
Just tested, it works for me. :)
Credits to: Conditionally ignoring JUnit tests
An alternative is to use JUnit's assume functionality:
#Test
fun shouldWorkOnNewerDevices() {
assumeTrue(
"Can only run on API Level 23 or newer because of reasons",
Build.VERSION.SDK_INT >= 23
)
}
If applied, this effectively marks the test method as skipped.
This is not so nice like the annotation solution, but you also don't need a custom JUnit test runner.
I've been searching for an answer to this question, and haven't found a better way than to check the version. I was able to conditionally suppress the execution of test logic by putting a check in the following Android TestCase methods. However, this doesn't actually prevent the individual tests from executing. Overriding the runTest() method like this will cause tests to "pass" on API levels you know will not work. Depending on your test logic, you may want to override tearDown() too. Maybe someone will offer a better solution.
#Override
protected void setUp() throws Exception {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
if (Log.isLoggable(TAG, Log.INFO)) {
Log.i(TAG, "This feature is only supported on Android 2.3 and above");
}
} else {
super.setUp();
}
}
#Override
protected void runTest() throws Throwable {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
assertTrue(true);
} else {
super.runTest();
}
}
I think #mark.w's answer is the path of least resistance:
You might wanna take a look at SdkSuppress annotation. It has two methods -- maxSdkVersion and minSdkVersion which you could use depending on your need.
for example:
#Test
#SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
public void testMethodThatRunsConditionally() {
System.out.print("Test me!");
}
I have upgraded answer of #Enrichman for Kotlin modern version. So, this is test runner class:
class ConditionalSDKTestRunner(klass: Class<*>?) : BlockJUnit4ClassRunner(klass) {
override fun runChild(method: FrameworkMethod, notifier: RunNotifier) {
// Annotation class could not have a base class of interface, so we can not group them.
val testOnlyForTargetSDKCode = method.getAnnotation(TestOnlyForTargetSDK::class.java)?.sdkLevel?.code
val testForTargetSDKAndBelowCode = method.getAnnotation(TestForTargetSDKAndBelow::class.java)?.sdkLevel?.code
val testForTargetSDKAndAboveCode = method.getAnnotation(TestForTargetSDKAndAbove::class.java)?.sdkLevel?.code
when {
// If annotation exists, but target SDK is not equal of emulator SDK -> skip this test.
testOnlyForTargetSDKCode != null && testOnlyForTargetSDKCode != Build.VERSION.SDK_INT ->
notifier.fireTestIgnored(describeChild(method))
// If annotation exists, but test SDK is lower than emulator SDK -> skip this test.
testForTargetSDKAndBelowCode != null && testForTargetSDKAndBelowCode < Build.VERSION.SDK_INT ->
notifier.fireTestIgnored(describeChild(method))
// If annotation exists, but test SDK is higher than emulator SDK -> skip this test.
testForTargetSDKAndAboveCode != null && testForTargetSDKAndAboveCode > Build.VERSION.SDK_INT ->
notifier.fireTestIgnored(describeChild(method))
// For other cases test should be started.
else -> super.runChild(method, notifier)
}
}
}
Enum with exist SDKs in your project:
enum class SdkLevel(val code: Int) {
SDK_24(24),
SDK_25(25),
SDK_26(26),
SDK_27(27),
SDK_28(28),
SDK_29(29),
SDK_30(30),
SDK_31(31),
SDK_32(32)
}
and annotations below:
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
annotation class TestForTargetSDKAndAbove(val sdkLevel: SdkLevel)
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
annotation class TestForTargetSDKAndBelow(val sdkLevel: SdkLevel)
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
annotation class TestOnlyForTargetSDK(val sdkLevel: SdkLevel)
To use it just add ConditionalSDKTestRunner to your base android unit test class:
#RunWith(ConditionalSDKTestRunner::class)
abstract class BaseAndroidUnitTest
and necessary annotation for test to make it actual only for special sdk:
#Test
#TestForTargetSDKAndAbove(SdkLevel.SDK_31)
fun getConnectionInfo_positive_SDK31() {
#Test
#TestForTargetSDKAndBelow(SdkLevel.SDK_30)
fun getConnectionInfo_negative1_SDK30() {
#Test
#TestOnlyForTargetSDK(SdkLevel.SDK_29)
fun getConnectionInfo_negative1_SDK29() {
That's all. Thanks!