【问题标题】:NullPointerException when using Spring RestTemplate in Android在 Android 中使用 Spring RestTemplate 时出现 NullPointerException
【发布时间】:2012-09-05 11:41:09
【问题描述】:

我正在使用托管在 Windows Azure 上的 Spring 3 MVC 服务器,并使用它为用户提供通过 Web 浏览器和 Android 设备进行的交互。所有代码都是用 Java 编写的。

当我通过浏览器与服务器交互时,一切正常,当使用 Android 与它交互时,如果服务器作为 localhost 运行,它就可以正常工作。但是,当我使用 Android 客户端与 Azure 托管服务器交互时,我随机(但总是最终)得到一个 NullPointerException,因此有时代码运行良好但最终会出现。我得到NullPointer 更改的确切位置,但它总是在使用RestTemplate 的HTTP 调用期间。

另一个奇怪的事情是,在调试中运行 Android 应用程序时,代码将运行到代码中的某个任意点(仍然在 RestTemplate 调用时崩溃),但 NullPointerException 的堆栈跟踪将指向之前打的电话。

我调用的一般代码类似于以下内容:

 private class ChangeDirectory extends AsyncTask< String, Void, Boolean >
{
    @Override
    protected Boolean doInBackground(String... directoryName) {

        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add( new StringHttpMessageConverter() );

        String url = "http://" + Constants.DNS +"/ui/android/cloud/change_directory";

        restTemplate.put(url, 
                         directoryName[0]);
        return true;
    }
}

堆栈跟踪看起来像:

09-05 11:32:05.081: E/AndroidRuntime(535): Caused by: java.lang.NullPointerException
09-05 11:32:05.081: E/AndroidRuntime(535):  at org.springframework.http.client.SimpleClientHttpResponse.getStatusCode(SimpleClientHttpResponse.java:62)
09-05 11:32:05.081: E/AndroidRuntime(535):  at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:46)
09-05 11:32:05.081: E/AndroidRuntime(535):  at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:476)
09-05 11:32:05.081: E/AndroidRuntime(535):  at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:438)
09-05 11:32:05.081: E/AndroidRuntime(535):  at org.springframework.web.client.RestTemplate.put(RestTemplate.java:364)
09-05 11:32:05.081: E/AndroidRuntime(535):  at com.artmaps.im.activity.HomeActivity$ChangeDirectory.doInBackground(HomeActivity.java:192)
09-05 11:32:05.081: E/AndroidRuntime(535):  at com.artmaps.im.activity.HomeActivity$ChangeDirectory.doInBackground(HomeActivity.java:1)
09-05 11:32:05.081: E/AndroidRuntime(535):  at android.os.AsyncTask$2.call(AsyncTask.java:264)
09-05 11:32:05.081: E/AndroidRuntime(535):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
09-05 11:32:05.081: E/AndroidRuntime(535):  ... 5 more

指向restTemplate.put(url, directoryName[0]);调用

我尝试在服务器端放入语句,当这些NullPointers 被抛出时,服务器似乎没有收到请求,因为没有到达语句。

对于NullPointerException 的原因的任何帮助将不胜感激,因为我对此感到很困惑,尤其是有时它有效,有时却无效

编辑: 根据https://jira.springsource.org/browse/ANDROID-102

这有点帮助,但仍然不能解释为什么它有时会起作用,并且只会在与基于 Azure 的服务器交互时失败

【问题讨论】:

    标签: android spring azure nullpointerexception resttemplate


    【解决方案1】:

    我想我会发布我发现的内容,以防其他人陷入困境。事实证明,这与我在问题编辑中发布的错误有关。

    我通过删除 Spring RestTemplate 调用并改用 DefaultHttpClient 来解决这个问题。

    例如改变这个:

    RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add( new StringHttpMessageConverter() );
    
        String url = "http://" + Constants.DNS +"/ui/android/cloud/change_directory";
    
        restTemplate.put(url, 
                         directoryName[0]);
        return true;
    

    到这里:

    String url = "http://" + Constants.DNS +"/ui/android/cloud/change_directory";
            try {
    
                HttpClient client = new DefaultHttpClient();
                HttpPut putRequest = new HttpPut(url);
                putRequest.setEntity(new StringEntity(directoryName[0]));
                client.execute(putRequest);
    
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    

    【讨论】:

    • 一个很好的解决方法,但是不能为此使用spring android。维护了吗,没有人为这个bug做任何事情...jira.springsource.org/browse/ANDROID-102
    • 感谢您指出错误报告。看来它现在已被修复(从版本 1.0.1 开始)。
    • 这个bug在1.0.1版本中仍然存在,但是您可以通过以下方式解决这个问题:restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());这样,您将使用没有此错误的“旧”HttpComponents。
    【解决方案2】:

    如果您收到此错误,则可能是您在使用 Gzip 压缩时遇到问题:这是一种解决方法,在 ICS 和 JB 上进行了测试,但应该适用于任何地方:

    灵感来自http://android-developers.blogspot.fr/2011/09/androids-http-clients.html

        RestTemplate restTemplate = new RestTemplate() {
            @Override
            protected ClientHttpRequest createRequest( URI url, HttpMethod method ) throws IOException {
                ClientHttpRequest request = super.createRequest( url, method );
                HttpHeaders headers = request.getHeaders();
                headers.setAcceptEncoding( ContentCodingType.GZIP );
    
                return request;
            }
        };
    
        if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO ) {
            System.setProperty( "http.keepAlive", "false" );
        }
    
        try {
            long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
            File httpCacheDir = new File( getCacheDir(), "http" );
            Class.forName( "android.net.http.HttpResponseCache" ) //
                    .getMethod( "install", File.class, long.class )//
                    .invoke( null, httpCacheDir, httpCacheSize );
        } catch ( Exception httpResponseCacheNotAvailable ) {
            Ln.v( "Http cache disabled" );
        }
    

    其中 Ln 是 RoboGuice 日志库。

    【讨论】:

      【解决方案3】:

      我在实现自定义 ClientHttpRequestFactory 扩展 SimpleClientHttpRequestFactory 时遇到了同样的问题。我也会得到随机的 NullPointerExceptions。

      解决方案是从HttpComponentsClientHttpRequestFactory 扩展而不是SimpleClientHttpRequestFactory 并在postProcessHttpRequest(HttpUriRequest httpRequest) 方法中执行必要的逻辑。

      这是一个自定义HttpComponentsClientHttpRequestFactory 访问和设置标题中的用户代理字段的快速示例:

      public class CustomHTTPRequestFactory extends HttpComponentsClientHttpRequestFactory {
      
          public CustomHTTPRequestFactory() {
              super();
          }
      
          @Override
          protected void postProcessHttpRequest(HttpUriRequest httpRequest) {
              httpRequest.setHeader("User-Agent", "my-user-agent-string");
              super.postProcessHttpRequest(httpRequest);
          }
      
      }
      

      【讨论】:

        【解决方案4】:

        我解决了升级到 1.0.1 Spring Framework 并使用带有 Locale 的 String.format 作为 URL 和 ClientHttpFactory

        private class ChangeDirectory extends AsyncTask< String, Void, Boolean >
        {
            @Override
            protected Boolean doInBackground(String... directoryName) {
        
                RestTemplate restTemplate = new RestTemplate(true); // default converters
                restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
                String url = String.format(Locale.getDefault(),"http://%s/ui/android/cloud/change_directory", Constants.DNS);
        
                restTemplate.put(url, directoryName[0]);
                return true;
            }
        }
        

        我希望这对你有用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-11-11
          • 2016-11-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-01-11
          • 2013-06-29
          • 2015-05-16
          相关资源
          最近更新 更多