使用Apache DefaultHttpClient时出现“java.lang.RuntimeException:Stub!”错误。

5
我正在涉足Android开发。我有一个将与RESTful资源进行接口的项目,并且我正在尝试弄清楚如何通过HTTP进行基本GET参数。从我所读的所有内容来看,共识似乎更喜欢使用HTTPClient而不是HttpURLConnection。
我编写了一个包装类,其中一个方法负责实例化关键对象以使用HTTPClient发出请求:
public String get() {  
    String responseString = null;

    HttpClient client = new DefaultHttpClient();
    HttpGet get = new HttpGet();
    try {
        get.setURI(new URI(this.baseURL()));
    } catch (URISyntaxException e1) {
        e1.printStackTrace();
    }

    HttpResponse response;

    try {
        response = client.execute(get);
        responseString = readResponse(response.getEntity());

        if(response != null) {
            System.out.println(responseString);
        }
    } catch(ClientProtocolException e) {
        e.printStackTrace();
    } catch(IOException e) {
        e.printStackTrace();
    }

    return responseString;

以下代码行:HttpClient client = new DefaultHttpClient();会抛出以下异常:

java.lang.RuntimeException: Stub!
at org.apache.http.impl.client.AbstractHttpClient.<init>(AbstractHttpClient.java:5)
at org.apache.http.impl.client.DefaultHttpClient.<init>(DefaultHttpClient.java:7)
at org.rcindustries.appmap.RestClient.get(RestClient.java:54)
at org.rcindustries.appmap.test.functional.RestClientTest.shouldReturnSomeJSon(RestClientTest.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

我看到的HttpClient的所有示例都使用类似的结构来执行GET和POST请求。Apache Commons库与Android SDK捆绑在一起的标准库有显著的不同吗?

5个回答

8

如果有人感兴趣,我遇到了类似的问题 - 尽管不是HttpClient,而是DateUtils导致存根错误。

Stephen似乎是绝对正确的 - Android平台的一部分需要运行模拟器:http://simpleprogrammer.com/2010/07/27/the-best-way-to-unit-test-in-android/

以上链接的真正快速摘要:

实际上,当您调用它们时,所有方法都被存根以抛出带有消息“Stub!”的异常。 多可爱。

真正的android.jar实现位于模拟器或真实的Android设备上。

因此,您可以进行单元测试...

  • 在模拟器上 - 获取完整的Android平台,“真实”的Android环境等(但没有常见的Java单元测试工具的强大功能,例如JMock)

或者

  • 使用JVM - 更快,可以使用诸如JMock等单元测试助手,但是无法测试依赖于Android平台的任何内容。

似乎 android.jar SDK 库中的所有方法都是存根方法。 - HackNone

7
我认为这是Android告诉你不能在该平台上运行该单元测试的方式。涉及与Android平台交互的单元测试(例如此情况下的网络)需要在实际的Android设备或正常运行的Android模拟器上运行。
(它们不能在常规Eclipse环境中运行。早期,您需要使用Eclipse的Android插件。自2013年以来,您应该使用建立在Intellij之上的Android Studio。)
显然,你实际上正在运行针对Android SDK提供的存根类的单元测试。这永远不可能起作用。

那么有一些只有存根方法的服务/库吗?dev.android.com上有描述这个的内容吗?我是Android生态系统的新手,不确定在哪里寻找这个。 - manlycode
我通过“谷歌”异常信息并从堆栈跟踪中推断出来解决了这个问题。我自己不做Android开发。 - Stephen C
1
一般来说,在Java中仅暴露带有存根方法的接口是常见的做法吗?自从我大学时代使用它以来已经有一段时间了,而且我对Java中的TDD完全是新手。 - manlycode

4

当在Android SDK 23中使用Proguard和com.apache.http.legacy库时会出现此问题。

在我的Proguard配置中添加以下内容后,问题得到解决:

-keep class org.apache.http.** { *; }
-keep class org.apache.commons.codec.** { *; }
-keep class org.apache.commons.logging.** { *; }
-keep class android.net.compatibility.** { *; }
-keep class android.net.http.** { *; }
-keep class com.android.internal.http.multipart.** { *; }
-dontwarn org.apache.http.**
-dontwarn android.webkit.**

这使得Apache系统实现能够正确地覆盖编译到应用程序中的存根。

1

0

这个帖子有点老了,但是对于那些不知道的人来说,Robolectric 可以解决测试中的这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接