Note that there are some explanatory texts on larger screens.

plurals
  1. POAndroid Robolectric Library class support. How to have library class R references load from application project
    text
    copied!<p>I'm trying to implement support for Library projects in Robolectric (android unit testing framework). I have the framework loading all resources for the library projects and have tested this to work just fine. The process is pretty simple, from the RobolectricConfig I read in the project.properties and look for android.library.reference.x values in a loop, and recurse through each project. </p> <p>The tricky part has to do with resolving R references at runtime when doing so from the Library Project codebase. Lets say for example that we have an application with 1 library project like so:</p> <p><strong>com.example.app</strong> which depends on library <strong>com.example.lib</strong></p> <p>Both projects have resources in them. Under the com.example.app project we have:</p> <p><strong>gen/com.example.app.R</strong></p> <p><strong>gen/com.example.lib.R</strong></p> <p>com.example.app.R is a superset of com.example.lib.R as it contains all of com.example.lib.R's definitions and then those that have been added because of its own resources. I originally thought they were identical but I was wrong, they are in fact different. However for R's inner classes ((R.string, R.color, R.attr, etc) the name/values map the same as they corresponding values in com.example.app.R in the app project. The library project's R classes values do not map the same though as the one in the application project.</p> <p>As a simplification, lets say they look like this:</p> <pre><code>package com.example.app; public final class R { public static final class string { public static final int a = 0x7f050001; public static final int a = 0x7f050002; public static final int c = 0x7f050003; } } //this file is in the application project package com.example.lib; public final class R { public static final class string { public static final int c = 0x7f050003; } } </code></pre> <p>Then, under the library project there is <strong>gen/com.example.lib.R</strong>:</p> <pre><code>//this file is in the library project package com.example.lib; public final class R { public static final class string { public static final int c = 0x7f040003; } } </code></pre> <p>So what happened is that the library has <strong>c</strong> defined in an string.xml and the project does not. The value for R.string.c in the library's values are not the same as the application's R values (c = 0x7f050003 in app and c = 0x7f040003 in library). I understand that the library doesn't know about the application thus it's own R class can't possibly generate values that are the same, and thus the application class must have its values re-mapped. What I want to know is how could I possibly, at runtime, use the com.example.lib.R values that are defined in the application project as opposed to those defined in the library's com.example.lib.R class?</p> <p>What fails is that when the Robolectric Test runner is running my test, when it gets into the library project codebase I do a lookup for string <strong>c</strong>, as so:</p> <pre><code> resources.getString(R.string.c); </code></pre> <p>So what I get when I do the lookup is 0x7f040003 instead of 0x7f050003. It seems most values map exactly 0x10000 higher but that does not always work out so I can't rely on that.</p> <p>What I don't understand is how come if we have 2 classes with the same package name and the one from the application comes first in the classpath (which I verified at runtime by printing out System.getProperty("java.class.path") at runtime), why does the library project still use its own <strong>com.example.lib.R</strong> definitions rather than the equivalent version in the application project? They both have the exact same canonical name.</p> <p>I imagine something in the class loader says that this class (lets call its MyLibraryActivity) only knows about the com.example.lib.R class that it depends on, and loads that one. Perhaps the security manager or something makes that decision? I have no idea really. But I would hope that somehow I could change this behavior so that library project resource lookups from within the library project could resolve to the version in the application project (since after all, it is in the class path). Maybe there is a way I can force load this class ahead of time so the system doesn't try to reload it? </p> <p>I understand that there is no dependency between the library project and the application project and I definitely don't want to add that dependancy, however I'd like the values at runtime to come in from the application's R class instead of the library R class.</p> <p>Anyone have any idea why this happens and also if there is a way for me to force the application R class to be the one that the application project references?</p> <p>-- Update --</p> <p>After some thought, I believe the problem is that the java compiler is inlining these values since they are final static variables and thus there would be no way to fix the problem with class loading changes.</p> <p>Another idea I had at one point was to load all of the library project's R classes, and map the variables to the application projects variables in the R class. Whenever getValue was called I would somehow (at runtime) analyze the stack to determine who the caller was and which R class was associated with that caller. From there I could determine what the associated ID was from the application project and the lookup would work as expected.</p> <p>Anyone have an idea if this is possible? I know how to get the stack trace at runtime and could put in some logic to figure out who the caller was, but figuring out which R class is associated with that caller seems complicated, slow, and probably error prone.</p> <p>-- Update Again -- What about doing something like using <a href="http://www.csg.is.titech.ac.jp/~chiba/javassist/" rel="nofollow">http://www.csg.is.titech.ac.jp/~chiba/javassist/</a> to modify the class file at runtime, substituting the values where appropriate! This would be amazing but probably pretty difficult.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload