I got a question about the usage of GWT-RequestFactory in Android. As a starting point I used the code from the “Create a AppEngine connected Android-Project”-Wizard (infos: http://code.google.com/intl/de-DE/eclipse/docs/appengine_connected_android.html) and it worked great.
But now in my case I want do extend this application to use a local ContentProvider with SQLite and a SyncService with SyncAdapter do synchronize the data from the ContentProvider to the AppEngine using the RequestFactory. Now my problem is the following: I can call
MyRequestFactory requestFactory = Util.getRequestFactory(mContext, MyRequestFactory.class);
in any Activity I want and will receive an instance of MyRequestFactory. (Note: Util is a class created by the Wizard.) But if I try to make the same call from my SyncAdapter, I will get a java.lang.RuntimeException: The RequestFactory ValidationTool must be run for the com.hotool.client.MyRequestFactory RequestFactory type”.
Maybe for your information: The Util.getRequestFacory method looks like this:
/**
* Creates and returns an initialized {@link RequestFactory} of the given
* type.
*/
public static <T extends RequestFactory> T getRequestFactory(
Context context, Class<T> factoryClass) {
T requestFactory = RequestFactorySource.create(factoryClass);
SharedPreferences prefs = getSharedPreferences(context);
String authCookie = prefs.getString(Util.AUTH_COOKIE, null);
String uriString = Util.getBaseUrl(context) + RF_METHOD;
URI uri;
try {
uri = new URI(uriString);
} catch (URISyntaxException e) {
Log.w(TAG, "Bad URI: " + uriString, e);
return null;
}
requestFactory.initialize(new SimpleEventBus(),
new AndroidRequestTransport(uri, authCookie));
return requestFactory;
}
The error occurs in RequestFactorySource which lies in the requestfactory-client.jar I think, that this may be a Class-Loader problem, but tried to figure it out with no success.
I tried to use the ValidationTool but first of all it didn't help and secondly I discovered, that the classes the ValidationTool will generate are already in place (probably thanks to annotation-processing as mentioned here: http://code.google.com/p/google-web-toolkit/wiki/RequestFactoryInterfaceValidation)
Does anybody have an idea what could cause this?
Thanks a lot and best regards.
Markus Neuenschwander
You are right Mark, this is an class-loader issue.
It happens in the requestfactory-client.jar, here the relevant source:
class InProcessRequestFactory extends AbstractRequestFactory {
//...
public InProcessRequestFactory(Class<? extends RequestFactory> requestFactoryInterface) {
this.requestFactoryInterface = requestFactoryInterface;
deobfuscator =
Deobfuscator.Builder.load(requestFactoryInterface,
Thread.currentThread().getContextClassLoader()).build();
}
//...
}
and
public class Deobfuscator {
//...
public static class Builder {
public static Builder load(Class<?> clazz, ClassLoader resolveClassesWith) {
Throwable ex;
try {
Class<?> found;
try {
// Used by the server
found = Class.forName(clazz.getName() + GENERATED_SUFFIX, false, resolveClassesWith);
} catch (ClassNotFoundException ignored) {
// Used by JRE-only clients
found = Class.forName(clazz.getName() + GENERATED_SUFFIX_LITE, false, resolveClassesWith);
}
Class<? extends Builder> builderClass = found.asSubclass(Builder.class);
Builder builder = builderClass.newInstance();
return builder;
} catch (ClassNotFoundException e) {
throw new RuntimeException("The RequestFactory ValidationTool must be run for the "
+ clazz.getCanonicalName() + " RequestFactory type");
} catch (InstantiationException e) {
ex = e;
} catch (IllegalAccessException e) {
ex = e;
}
throw new RuntimeException(ex);
}
//...
}
The problem is that Thread.currentThread().getContextClassLoader() seems to return a null value when called from the sync adapter on Android, because the Sync Adapter thread is created by the System, not by your application.
I solved this by calling manually calling setContextClassLoader in the onPerformSync method before creating a requestfactory instance:
@Override
public void onPerformSync(final Account account, Bundle extras,
String authority, final ContentProviderClient provider,
final SyncResult syncResult) {
Thread.currentThread().setContextClassLoader(mContext.getClassLoader());
// ...
MyRequestFactory requestFactory = Util.getRequestFactory(mContext,
MyRequestFactory.class, acsidToken);
// ...
}
I hope this makes sense. Andreas
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With