如何在 Android SyncAdapter 中使用 GWT-RequestFactory(总是出现 ValidationTool-Error)

发布于 2024-12-14 09:29:06 字数 2133 浏览 0 评论 0原文

我有一个关于 Android 中 GWT-RequestFactory 用法的问题。作为起点,我使用了“创建 AppEngine 连接的 Android 项目”向导中的代码(信息:http://code.google.com/intl/de-DE/eclipse/docs/appengine_connected_android.html),效果很好。

但现在就我而言,我想扩展此应用程序以使用带有 SQLite 的本地 ContentProvider 和带有 SyncAdapter 的 SyncService 使用 RequestFactory 将数据从 ContentProvider 同步到 AppEngine。 现在我的问题如下:我可以调用

MyRequestFactory requestFactory = Util.getRequestFactory(mContext, MyRequestFactory.class);

任何我想要的 Activity,并将收到 MyRequestFactory 的实例。 (注意:Util 是由向导创建的类。)但是如果我尝试从 SyncAdapter 进行相同的调用,我将得到一个 java.lang.RuntimeException:必须为 com.hotool.client.MyRequestFactory RequestFactory 类型运行 RequestFactory ValidationTool”。

也许供您参考: Util.getRequestFacory 方法如下所示:

/**
* 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;
}

错误发生在 RequestFactorySource 中,位于 requestfactory-client.jar 中 我认为这可能是一个类加载器问题,但试图解决它但没有成功。

我尝试使用 ValidationTool 但首先它没有帮助,其次我发现 ValidationTool 将生成的类已经就位(可能要感谢此处提到的注释处理: http://code.google.com/p/google-web-toolkit/wiki/RequestFactoryInterfaceValidation

有人知道什么可能会导致这种情况吗?

非常感谢并致以诚挚的问候。

马库斯·纽恩施万德

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦巷 2024-12-21 09:29:07

你是对的,马克,这是一个类加载器问题。

它发生在 requestfactory-client.jar 中,这里是相关源:

class InProcessRequestFactory extends AbstractRequestFactory {

    //...

    public InProcessRequestFactory(Class<? extends RequestFactory> requestFactoryInterface) {
        this.requestFactoryInterface = requestFactoryInterface;
        deobfuscator =
            Deobfuscator.Builder.load(requestFactoryInterface,
                Thread.currentThread().getContextClassLoader()).build();
    }

    //...

}

并且

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);
    }

    //...

}

问题是 Thread.currentThread().getContextClassLoader() 在从 Android 上的同步适配器调用时似乎返回空值,因为同步适配器线程是由系统创建,而不是由您的应用程序创建。

我通过在创建 requestfactory 实例之前在 onPerformSync 方法中手动调用 setContextClassLoader 解决了这个问题:

@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);

    // ...
}

我希望这是有道理的。
安德烈亚斯

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文