【问题标题】:Getting null pointers from OkHttp3 API call on background thread从后台线程上的 OkHttp3 API 调用获取空指针
【发布时间】:2018-07-21 12:49:36
【问题描述】:

我正在开发一个使用网络服务的移动应用程序。我正在使用 MVP 模式。

有时,我在调试时得到响应,有时我得到一个空指针,表明在我尝试访问调用结果之前后台线程尚未完成对调用的处理。

我该如何解决这个问题?调用 API 的代码运行良好,但似乎时间问题在这里。

登录交互类代码

public class LoginInteractorImpl implements LoginInteractor {

    private static final String TAG = LoginInteractorImpl.class.getName();
    private Handler mHandler;
    private HttpResponseResult httpResponseResult = null;
    private Gson mGson;
    private OkHttpRequestUtil okHttpRequestUtil = null;

    @Override
    public void login(final String username, final String password, final OnLoginFinishedListener listener, final LoginView mLoginView) {

        mGson = new Gson();
        mHandler = new Handler(Looper.getMainLooper());

        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (TextUtils.isEmpty(username)) {
                    listener.onUsernameError();
                    return;
                }
                if (TextUtils.isEmpty(password)) {
                    listener.onPasswordError();
                    return;
                }


            }
        });



        String postBody = mGson.toJson(new LoginRequestDTO(username, password));

        okHttpRequestUtil = new OkHttpRequestUtil();
        String url = Configuration.BASE_URL.concat(Configuration.FEED_LOGIN_URL);
        MediaType jsonMediaType = Configuration.JSON_MEDIA_TYPE;


        httpResponseResult = okHttpRequestUtil.DoPost(null, postBody, (Activity) mLoginView, listener, url, jsonMediaType);

        try {
            TransformJsonResponseToPojo(httpResponseResult, (Activity)mLoginView);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

OkHttpRequestUtil 类的 DoPost 方法。

public class OkHttpRequestUtil {
    public static final String TAG = OkHttpRequestUtil.class.getName();

    OkHttpClient mClient = null;
    HttpResponseResult httpResponseResult = null;
    private Handler mHandler;

    public HttpResponseResult DoPost(final Map<String, String> headers, String postBody, Context context, final LoginInteractor.OnLoginFinishedListener listener, String url, MediaType jsonMediaType) {

        HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
        Request request = null;
        String processedUrl = urlBuilder.build().toString();
        RequestBody body = RequestBody.create(jsonMediaType, postBody);

        mHandler = new Handler(Looper.getMainLooper());


        if (headers != null) {
            Headers headerBuild = Headers.of(headers);

            request = new Request.Builder()
                    .headers(headerBuild)
                    .url(processedUrl)
                    .post(body)
                    .build();

        }
        else
        {
            request = new Request.Builder()
                    .url(processedUrl)
                    .post(body)
                    .build();
        }


        try {
            mClient = new OkHttpClient.Builder()
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS)
                    .sslSocketFactory(CustomTrust.getPinnedCertSslSocketFactory(context), (X509TrustManager) CustomTrust.getTrustManagerFactory(context).getTrustManagers()[0])
                    .build();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        }

        mClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                call.cancel();
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, final Response response) throws IOException {
                // ... check for failure using `isSuccessful` before proceeding
                if (!response.isSuccessful()) {
                    Log.i(TAG, response.toString());
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            listener.onPasswordError();
                            try {
                                throw new IOException("Unexpected code " + response);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    });



                }
                // Populate the HttpResponseResult

                httpResponseResult = new HttpResponseResult();
                httpResponseResult.setCode(String.valueOf(response.code()));
                httpResponseResult.setMessage(response.message());
                httpResponseResult.setBodyString(response.body().string());


                Headers okHttpResponseHeaders = response.headers();
                Map<String, String> responseHeadersTemp = new HashMap<String, String>();

                for(int i = 0; i < okHttpResponseHeaders.size(); i++)
                {
                    responseHeadersTemp.put(okHttpResponseHeaders.name(i), okHttpResponseHeaders.value(i));
                }

                httpResponseResult.setHeaders(responseHeadersTemp);

                listener.onSuccess();

            }
        });

        return httpResponseResult;
    }
}

【问题讨论】:

  • 为什么要让异步方法等待结果?正确使用回调和侦听器,则不应为空
  • 无论如何,如果你真的想要 okhttp 和 gson 数据,我建议你尝试使用 Retrofit
  • @cricket_007,我采用这种方法是因为我想尽可能多地解耦应用程序组件。我还想创建一个 okhttp3 包装器来为 API 调用执行 POST 和 GET。
  • 这很好,但是在任何异步代码中使用 return 这个词通常是不正确的
  • @cricket_007,这种方法非常适用于 json 数据。在我看来,使用 OkHttp3 比使用 Retrofit 更直观。请以仍然使我的关注点分开和解耦的方式分享任何用于执行上述异步操作的示例代码。谢谢。

标签: java android nullpointerexception okhttp3


【解决方案1】:

基本上,您的代码就像您的 return 语句发生在此队列回调完成之前一样

mClient.newCall(request).enqueue

使 HTTP 响应为空,是的。

解决方案是让DoPost 不返回任何内容(请不要将您的方法名称大写)。将方法类型设为void

通过参数将您的结果从onResponse 传回listener.onSuccess(responseData);

而且您可以立即解析响应,无需将带有标头和所有内容的整个 HTTP 响应复制回来

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-24
    • 2011-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-23
    • 1970-01-01
    相关资源
    最近更新 更多