For some reason the RoboBlender does not generate the annotation database.
My build.gradle has the following dependencies:
dependencies {
provided 'org.roboguice:roboblender:3.0'
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'org.roboguice:roboguice:3.0'
}
This is not a final solution, but it can help you. I don't know why, but RoboGuice 3.0 and 3.0.1 throws this exception. What you have to do is disable annotations for databases in your MainActivity as follows:
static {
RoboGuice.setUseAnnotationDatabases(false);
}
I hope this help
Ok, so it seems that since I didn't have any injection in the main class MainActivity it didn't trigger the annotation processing of the inner AsyncTask. Therefore no annotation database was created.
Moreover, it seems that injection in anonymous inner classes is not supported. So the AsyncTask needs to be a proper class (it can still be inside the MainActivity).
I haven't figured out yet how to tell RoboGuice to inspect the inner classes even though the outer one does not have injections.
What does the rest of your project structure look like?
Specifically, have you read the RoboBlender wiki
Later versions of Android Studio will, by default, generate a project that falls into the Configuring RoboBlender for a large application using libraries-category.
Fix below does the following:
Rearrange dependencies in build.gradle
Supplies pointer to GuiceModule in project
Rudimentary module for your project
diff --git a/app/build.gradle b/app/build.gradle
index 1e69cec..8450fff 100644
--- a/app/build.gradle
+++ b/app/build.gradle
## -34,9 +34,9 ## android {
}
dependencies {
- provided 'org.roboguice:roboblender:3.0'
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'org.roboguice:roboguice:3.0'
+ provided 'org.roboguice:roboblender:3.0'
}
project.tasks.withType(JavaCompile) { task ->
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 017d11e..dba9e49 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
## -8,6 +8,7 ##
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<meta-data android:name="roboguice.annotations.packages" android:value="org.jush.roboguice3test"/>
+ <meta-data android:name="roboguice.modules" android:value="org.jush.roboguice3test.GuiceModule"/>
<activity
android:name="org.jush.roboguice3test.MainActivity"
android:label="#string/app_name" >
package org.jush.roboguice3test;
import android.app.Application;
import com.google.inject.AbstractModule;
public class GuiceModule extends AbstractModule {
private Application application;
public GuiceModule(Application application) {
this.application = application;
}
#Override
protected void configure() {
}
}
What did you have to do that it did trigger the annotation processing? My main activity has injections. The maina activity inherits from an abstract activity which has as well injections. That abstract activity inherits from RoboActivity.
When i set the roboguice.annotations.packages to roboguice the NoClassFound exception isn't thrown anymore, but i get a NullPointer Exception for the first inject-Object that I wanna use.
I use eclipse to start the app.
When I disable RoboBlender (RoboGuice.setUseAnnotationDatabases(false);) injection works.
The AnnotationDatabaseImpl is generated at compile time
An explanation is available here
Injected objects became null after upgrading to Roboguice 3
Related
I have a core branch where following dependencies is decalared
implementation "org.parceler:parceler-api:$rootProject.ext.parcelVersion"
annotationProcessor "org.parceler:parceler:$rootProject.ext.parcelVersion"
I am declaring a dependency in one of my library module as below
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':core')
}
Here is the class that I will be parceling in my activity
#Parcel(parcelsIndex = false)
public class MyClass {
#SerializedName("validation")
public String myField;
}
Exception is thrown at the third line.
MyClass myClass = new MyClass();
myClass.myField = "bjbskas";
Parcelable parcelable =Parcels.wrap(myClass);
Exception reads as shown below
Unable to find generated Parcelable class for com.example.mylibrary.MyClass,
verify that your class is configured properly and that the Parcelable class
com.example.mylibrary.MyClass$$Parcelable is generated by Parceler.
If I try putting the parceler library in my library module directly, it gives me another error called
Program type already present: org.parceler.Parceler$$Parcels$1
Changing the version of Parceler library from 1.0.4 to 1.1.10 solved the problem. I don't know what is the reason behind this but I guess it may be because of some transitive dependencies using version 1.1.10
I have a multi-module gradle project that looks like this:
Parent
|--server
|--application (android module)
+--common
The server tests have a dependency on the common module tests. For this, I added
testCompile files(project(':common').sourceSets.test.output.classesDi
compileTestJava.dependsOn tasks.getByPath(':common:testClasses')
and it worked great. Unfortunately, when I tried to do the same thing for the application module that also has a dependency on the common module tests, it wouldn't work. It fails with:
Build file 'application\build.gradle' line: 103
A problem occurred evaluating project ':application'.
Could not find property 'sourceSets' on project ':common'
After googling a bit I also tried
project.evaluationDependsOn(':common')
testCompile files(project(':common').sourceSets.test.output.classesDir)
But fails with another exception:
Project application: Only Jar-type local dependencies are supported. Cannot handle: common\build\classes\test
Any ideas on how to fix this?
There's a couple of approaches solving the problem of importing test classes in this article. https://softnoise.wordpress.com/2014/09/07/gradle-sub-project-test-dependencies-in-multi-project-builds/ The one I used is:
code in shared module:
task jarTest (type: Jar) {
from sourceSets.test.output
classifier = 'test'
}
configurations {
testOutput
}
artifacts {
testOutput jarTest
}
code in module depending on the shared module:
dependencies{
testCompile project(path: ':common', configuration: 'testOutput')
}
And there seems to be a plugin for it as well! https://plugins.gradle.org/plugin/com.github.hauner.jarTest/1.0
Following the approach from sakis, this should be the configuration you need to get the tests available from another project in the Android platform (done for debug variant).
Shared module:
task jarTests(type: Jar, dependsOn: "assembleDebugUnitTest") {
classifier = 'tests'
from "$buildDir/intermediates/classes/test/debug"
}
configurations {
unitTestArtifact
}
artifacts {
unitTestArtifact jarTests
}
Your module:
dependencies {
testCompile project(path: ":libName", configuration: "unitTestArtifact")
}
The solution mentioned by droidpl for Android + Kotlin looks like this:
task jarTests(type: Jar, dependsOn: "assembleDebugUnitTest") {
getArchiveClassifier().set('tests')
from "$buildDir/tmp/kotlin-classes/debugUnitTest"
}
configurations {
unitTestArtifact
}
artifacts {
unitTestArtifact jarTests
}
Gradle for project that is going to use dependencies:
testImplementation project(path: ':shared', configuration: 'unitTestArtifact')
I know it's kinda an old question but the solution mentioned in the following blog solves the problem very nicely and is not a sort of hack or a temporary workaround:
Shared test sources in Gradle multi-module project
It works something like this:
// in your module's build.gradle file that needs tests from another module
dependencies {
testCompile project(path: ':path.to.project', configuration: 'test')
}
Also you should note that in the very last paragraph he mentioned that you need to enable Create separate module per source set in IntelliJ settings. But it works fine without using that option too. Probably due to changes in the recent IntelliJ versions.
EDIT: IntelliJ recognizes this fine as of 2020.x versions.
I think you could use gradles java test fixtures. This will automatically create a testFixtures source set, in which you can write your test that you want to reuse.
Test fixtures are configured so that:
they can see the main source set classes
test sources can see the test fixtures classes
For example, if you have some class in common module:
public class CommonDto {
private final Long id;
private final String name;
// getters/setters and other methods ...
}
Then in the common module, you could write into src/testFixtures/java following utils:
public class Utils {
private static final CommonDto A = new CommonDto(1, "A");
private static final CommonDto B = new CommonDto(2, "B");
public static CommonDto a() { return A; }
public static CommonDto b() { return B; }
}
Then in you other modules you could add this to reuse Utils class
dependencies {
// other dependencies ...
testImplementation(testFixtures(project(":common")))
}
All of this is better explained in the documentation that I provided initially. There are some nuances that you need to take into account until you create this not to leak test classes into production.
I try to make Roboblender work with Roboguice but the compile time processing doesn't seem to do anything, the AnnotationDatabaseImpl class is not generated. (Project builds without error.)
I even created a sample project, please see below. What do I miss?
(I know the gradle task and the second metadata would only be needed for multi module project, but it didn't work without them either).
build.gradle:
project.tasks.withType(JavaCompile) { task ->
options.compilerArgs << "-AguiceAnnotationDatabasePackageName=gk.com.roboguice_compile"
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.0.0'
compile 'org.roboguice:roboguice:3.+'
provided 'org.roboguice:roboblender:3.+'
}
manifest:
<meta-data
android:name="roboguice.modules"
android:value="gk.com.roboguice_compile.RoboguiceBindings" />
<meta-data
android:name="roboguice.annotations.packages"
android:value="gk.com.roboguice_compile" />
activity:
#ContentView(R.layout.activity_main)
public class MainActivity extends RoboActivity {
#Inject
private PresentMaker presentMaker;
bindings:
public class RoboguiceBindings extends AbstractModule {
#Override
protected void configure() {
bind(PresentMaker.class).to(BirthdayPresentMaker.class);
}
}
The AnnotationDatabaseImpl was there but only under the build directory not among the source files.
My bad, probably every annotation processor work like this.
(Although strangely this project has the AnnotationDatabaseImpl generated in the project root..).
When I run the following code:
public class ActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
....
public void testCanCreateMockito() {
List mockedList = Mockito.mock(List.class);
}
}
I get the following exceptions:
java.lang.ExceptionInInitializerError
at org.mockito.internal.creation.cglib.ClassImposterizer.createProxyClass(ClassImposterizer.java:95)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:57)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:49)
at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59)
at org.mockito.Mockito.mock(Mockito.java:1285)
at org.mockito.Mockito.mock(Mockito.java:1163)
at com.acesounderglass.hungertracker.ActivityTest.testCanCreateMockito(ActivityTest.java:60)
at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
at android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:192)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1837)
Caused by: org.mockito.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:238)
at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:109)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:105)
at org.mockito.cglib.proxy.Enhancer.<clinit>(Enhancer.java:70)
... 23 more
Caused by: java.lang.reflect.InvocationTargetException
at org.mockito.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:385)
at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:220)
... 28 more
Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
at java.lang.ClassLoader.defineClass(ClassLoader.java:300)
... 32 more
This occurs with any class, List was just an easy example. My gradle dependencies are:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.0'
androidTestCompile "org.mockito:mockito-core:1.+"
androidTestCompile files('libs/dexmaker-mockito-1.0.jar')
androidTestCompile files('libs/dexmaker-1.0.jar')
}
I've upgraded gradle to 1.1, tried using the experimental unit test feature and not, nothing seems to make a difference. What's going on?
I received this error when I was missing the two dexmaker dependencies.
Adding these lines to the app/gradle.build file is working for me.
androidTestCompile 'org.mockito:mockito-core:1.10.19'
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
I am also using Android Studio and have found it to be a good idea to restart AS after altering the dependencies.
For me this eventually worked:
androidTestCompile "org.mockito:mockito-core:1.10.19"
androidTestCompile "com.crittercism.dexmaker:dexmaker:1.4"
androidTestCompile "com.crittercism.dexmaker:dexmaker-mockito:1.4"
androidTestCompile "com.crittercism.dexmaker:dexmaker-dx:1.4"
I was getting this because I was using proguard for my debug builds because of the 65K method limit (Yes, I need to cut down on the number of dependencies), and that was causing this error for me.
I added this in my (debug) proguard config to solve it:
### Keep Mockito
-keep class org.mockito.** { *; }
-keep interface org.mockito.** { *; }
-keep class com.google.dexmaker.** { *; }
-keep interface com.google.dexmaker.** { *; }
Not sure If I really need all four of those lines, but this did the trick.
LOOK HERE IF YOU DON'T BUILD YOUR APKS USING GRADLE!!!!
If you don't build your app using gradle (which, unfortunately, my team doesn't) then the above solutions may not work for you. Let me explain a little bit more about how dexmaker-mockito works before giving the solution to the issue.
Motivation
Mockito is a mocking framework for Java and comes packaged with cglib which creates Bytecode mocks, this is how Mockito/Junit outside of Instrumentation tests works. But if you're trying to run Mockito in Android instrumentation tests then Bytecode mocks are not sufficient and you need mocks that Mockito can load into the Dex classloader that ART/Dalvik can understand and that's where Dexmaker comes in. Dexmaker has a Mockito "plugin" that allows Mockito to dynamically switch to using it to create Dex mocks.
How does it know to switch?
The dexmaker-mockito jar has a top level folder named mockito-extensions/org.mockito.plugins.MockMaker containing a fully qualified name com.google.dexmaker.mockito.DexmakerMockMaker. When this jar is packaged with an APK, this top level folder can be included as a "class loader resource".
The class that Mockito uses to abstract the Mocking layer is called MockUtil and, statically, it determines which MockMaker subclass it should use by checking for these resources in the classloader that it is launched from via its PluginLoader. If the resource from dexmaker-mockito can be found, then com.google.dexmaker.mockito.DexmakerMockMaker is instantiated and used as the MockMaker but, if it's not, Mockito defaults to using CGLib which is incompatible within Android's DVM.
Problem
Depending on how you build your APK, you can potentially strip that class loader resource and cause Mockito to not dynamically switch to using Dexmaker.
Fix
First, include the jars that dexmaker requires: mockito 1.9.5+, junit, dexmaker-mockito 1.0+ and dexmaker 1.0+ and then simply reflectively switch which MockMaker Mockito will use manually. This is safe, given the fact that if this is running in the DVM, using the default CGLib MockMaker won't ever work since it produces Bytecode mocks.
static {
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = ClassLoader.getSystemClassLoader();
}
Enumeration<URL> resources = loader.getResources("mockito-extensions/org.mockito.plugins.MockMaker");
if (resources == null || !resources.hasMoreElements()) {
LOGGER.info("Replacing Mockito mockMaker because the classloader resources were not present.");
Field field = MockUtil.class.getDeclaredField("mockMaker");
Class<?> pluginClass = loader.loadClass("com.google.dexmaker.mockito.DexmakerMockMaker");
Object plugin = pluginClass.newInstance();
field.setAccessible(true);
field.set(null, plugin);
} else {
LOGGER.info("Mockito class loader resources present");
}
} catch (Throwable e) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
throw new AssertionError("Unable to replace mockMaker to be the DexmakerMockMaker", e);
} else {
e.printStackTrace();
throw new AssertionError("Unable to replace mockMaker to be the DexmakerMockMaker");
}
}
}
and be sure to add this as well because dexmaker will need to know where to output its Dex mocks.
System.setProperty("dexmaker.dexcache", "/sdcard/");
I stumbled upon the same error and could make it work by using
androidTestImplementation 'org.mockito:mockito-android:2.18.3'
in my build.gradle.
I previously made the mistake to only import 'org.mockito:mockito-core:2.18.3'.
I encountered the same error with EasyMock and eventually traced it to an absence of dexmaker. I solved it with the following dependency:
androidTestCompile "com.google.dexmaker:dexmaker:1.2"
That might work for mockito as well
User23's answer is close to the solution that worked for me.
According to the official documentation you have to do the following things to activate Mockito in your Android app:
Download dexmaker-1.4.jar and dexmaker-mockito-1.4.jar and put them into your libs folder.
Add the following dependencies into your build.gradle:
androidTestCompile "org.mockito:mockito-core:1.10.19"
androidTestCompile fileTree(dir: 'libs', include: ['dexmaker-1.4.jar'])
androidTestCompile fileTree(dir: 'libs', include: ['dexmaker-mockito-1.4.jar'])
Here is a test project: click
I have a test Gradle Android project with three modules: app, library_a, library_b. app depends on library_a, then library_a depends on library_b:
build.gradle (app)
dependencies {
...
compile (project(":library_a")){
transitive = false;
}
}
build.gradle (library_a)
dependencies {
...
compile (project(":library_b")){
transitive = false;
}
}
Note that I set transitive = false because I don't want classes from library_b to be accessed from app
Every module has just one class, code is pretty simple:
app:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
ClassA classA = new ClassA();
classA.doSomething();
}
}
library_a:
public class ClassA
{
public void doSomething(){
Log.i("Test", "Done A!");
ClassB classB = new ClassB();
classB.doSomething();
}
}
library_b:
public class ClassB
{
public void doSomething(){
Log.i("Test", "Done B!");
}
}
Well, here is the problem: I'm building my project with gradlew. Apk is compiling successfully, but when I run it I get NoClassDefFoundError.
I/Test﹕ Done A!
E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.NoClassDefFoundError: ru.pvolan.library_b.ClassB
at ru.pvolan.somelibrary.ClassA.doSomething(ClassA.java:12)
...
If I set transitive = true in both .gradle files, it runs ok, but, as I noted above, I don't want dependency to be transitive, as far as I don't want ClassB can be accessed from MainActivity - only ClassA.
What am I doing wrong?
This is a problem that Gradle has simplified in Gradle v3.4.
If you convert library A to use v3.4 there is a simple fix.
Gradle 3.4 changes the "compile" configuration to a set of configurations "api" and "implementation".
First you should upgrade gradle to 3.4 and use the java-library plugin in lieu of the java plugin.
You should use the "api" configuration on any jar that is explicitly used in the API method calls (return type, input parameters, etc).
For all other jars that you want to "hide" (like Library B) you should use the "implementation" configuration. As Library B is only used within the body of implementation methods there is no need to expose it to any other jars at compile time; however it still needs to be available at runtime so Library A can use it.
To implement this your Library A script should replace
apply plugin: 'java'
dependencies {
...
compile (project(":library_b")){
transitive = false;
}
}
with
apply plugin: 'java-library'
dependencies {
implementation project(":library_b")
}
This change will tell Gradle to include Library B as a runtime dependency of app, so that app cannot compile against it, but Library B still will be available at runtime for Library A to use. If for some reason app ends up needing Library B in the future, it would be forced to explicitly include Library B in it's dependency list to ensure it gets the desired version.
See this description from Gradle itself for more details and examples:
https://blog.gradle.org/incremental-compiler-avoidance
The problem is that library_b is a required dependency. You can't simply exclude it, since you need it to be on the classpath at runtime. You are effectively misrepresenting your actual dependencies in order to enforce a code convention and therefore losing any advantage of leveraging a dependency management system like Gradle. If you want to enforce class or package blacklist I'd suggest using a source analysis tool like PMD. Here's an an example of a rule to blacklist specific classes.
If that is not possible for some reason you can get your above example to "work" by simply adding library_b to the runtime classpath of app.
dependencies {
runtime project(':library_b')
}
Do you use multidex?
When I had a problem like this I used multidex and called class from different module. I could fix it only by turning off multidex and running proguard.
UPD
android {
compileSdkVersion 21
buildToolsVersion "21.1.0"
defaultConfig {
...
minSdkVersion 14
targetSdkVersion 21
...
// Enabling multidex support.
multiDexEnabled true
}
...
}
dependencies {
compile 'com.android.support:multidex:1.0.0'
}
more about multi dex https://developer.android.com/tools/building/multidex.html
and about proguard http://developer.android.com/tools/help/proguard.html