Android 6.0 (MarshMallow) 上的 HttpURLConnnection 请求失败
Posted
技术标签:
【中文标题】Android 6.0 (MarshMallow) 上的 HttpURLConnnection 请求失败【英文标题】:HttpURLConnnection request failures on Android 6.0 (MarshMallow) 【发布时间】:2016-01-22 04:34:07 【问题描述】:我在我的应用程序项目中使用 Google 的 Volley 库,目标是最低 api 级别 14。因此,Volley 库使用 HttpURLConnection 作为 NetworkClient。
因此,应该不会有任何与移除 Apache HTTPClient 相关的问题。但是,我已经完成了 6.0 Sdk 所需的配置,即 compileSdkVersion 23、build-tool-version 23.0.1 和 build:gradle:1.3.1',甚至尝试添加 useLibrary 'org.apache.http.legacy'。在我的应用程序中为 Volley 库项目更新了相同的内容。
最近,我尝试在 android 6.0 (MarshMallow) 上运行我的应用程序,我的项目编译并运行。但是那些需要身份验证标头的请求在 MarshMallow 上失败了:
BasicNetwork.performRequest:意外响应代码 401 com.android.volley.AuthFailureError
但是在所有低于 23 的 Api 级别上运行相同。
我已经检查了很多次标头。奇怪的是,那些不需要身份验证的请求给出的响应是 200 OK。
现在我完全不知道是什么破坏了请求,有没有人知道新版本中 HttpURLConnection 请求仅针对 Api 级别 23 失败的变化?还有其他人在使用 Volley 并面临类似问题吗?
这是我的 CustomRequest 类
public class CustomRequest extends Request<Void>
int id, cmd;
Map<String, String> params;
BaseModel model;
public CustomRequest(int method, int cmd, String url, Map<String, String> params, int id, BaseModel model)
super(method, url, null);
this.id = id;
this.cmd = cmd;
this.params = params;
this.model = model;
if (method == Method.GET)
setUrl(buildUrlForGetRequest(url));
Log.v("Volley", "Making request to: " + getUrl());
private String buildUrlForGetRequest(String url)
if (params == null || params.size() == 0) return url;
StringBuilder newUrl = new StringBuilder(url);
Set<Entry<String, String>> paramPairs = params.entrySet();
Iterator<Entry<String, String>> iter = paramPairs.iterator();
newUrl.append("?");
while (iter.hasNext())
Entry<String, String> param = iter.next();
newUrl
.append(param.getKey())
.append("=")
.append(param.getValue());
if (iter.hasNext()) newUrl.append("&");
return newUrl.toString();
@Override
public Map<String, String> getHeaders() throws AuthFailureError
Map<String, String> headers = new HashMap<String, String>();
headers.put("X-Api-Version", Contract.API_VERSION);
headers.put("X-Client", "android");
String accessToken = APP.getInstance().getToken();
if (!accessToken.equals(""))
headers.put("Authorization", "Bearer " + accessToken);
return headers;
@Override
protected Response<Void> parseNetworkResponse(NetworkResponse response)
Exception ex;
try
String jsonString = new String(
response.data, HttpHeaderParser.parseCharset(response.headers));
JsonNode json = new ObjectMapper().readTree(jsonString);
if (model != null) model.parse(id, json);
EventBus.getDefault().post(new EventResponse(cmd, true, null));
return Response.success(null, HttpHeaderParser.parseCacheHeaders(response)); //Doesn't return anything. BaseModel.parse() does all the storage work.
catch (NoMoreDataException e)
ex = e;
EventBus.getDefault().post(new NoMoreDataModel(cmd, e));
EventBus.getDefault().post(new EventResponse(cmd, false, null));
return Response.success(null, HttpHeaderParser.parseCacheHeaders(response));
catch (Exception e)
ex = e;
Log.e("CustomRequest", Log.getStackTraceString(e));
String message = APP.getInstance().getString(R.string.failedRequest);
if (!TextUtils.isEmpty(e.getMessage()))
message = e.getMessage();
EventBus.getDefault().post(new ErrorEventModel(cmd, message, e));
return Response.error(new ParseError(ex));
@Override
protected Map<String, String> getParams() throws AuthFailureError
return params;
@Override
protected void deliverResponse(Void response)
Log.v("Volley", "Delivering result: " + getUrl());
@Override
public void deliverError(VolleyError error)
Log.e("CustomRequest", "Delivering error: Request=" + getUrl() + " | Error=" + error.toString());
String message = APP.getInstance().getString(R.string.failedRequest);
EventBus.getDefault().post(new ErrorEventModel(cmd, message, error));
我发现 Api 23 和其他版本之间的唯一区别是 HostNameVerifier。 对于 Api 级别 23:com.android.okhttp.internal.tls.OkHostnameVerifier 对于 API 级别
【问题讨论】:
如果可用,请发布您的代码和 logcat 信息。 对不起,我的声誉不允许我发布更多代码。Logcat 仅显示上述代码 sn-p 错误。请看我的发现,这可能是问题的原因。 我认为您可以尝试硬编码访问令牌(您可以使用 Postman 获取新令牌,然后粘贴到您的代码中)。我的带有不记名令牌的 volley 项目在 API23 模拟器中工作。确保您有一个有效/未过期的令牌 :) 尝试过硬编码。没有运气:(毕竟这些请求都在 23 以下的 api 上工作,并且相同的代码库和令牌被正确读取。 您可以控制服务器端应用程序吗?如果是,请尝试调试它。是 Asp.Net Web API 吗? 【参考方案1】:在检查了我的应用程序的服务器端后,找到了问题背后的原因。 对于 Android 6.0(MarshMallow),标题变为空,而其他版本则不是这样。
所以对我有用的修复:
创建并添加了一个新的自定义标头 X-Proxy-No-Redirect => 1 并与其他标头一起传递。
背后的理论:
我们发送请求到一个 API 服务器,然后 API 服务器根据 oAuth 令牌将请求重定向到相应的站点 重定向时 为了实现重定向,有两种方法可以做到这一点
1 - 我们只是将响应发送回调用者,说明重定向到某个页面。然后调用者(网络库)负责重定向
2 - API 服务器将自己发出重定向请求并获取响应,然后传递给调用者
之前我们使用的是方法 1。
在 Android 6.0 上 - 网络库 (Volley) 在发出重定向请求时似乎没有设置所有标头。 一旦设置了这个新的标头,方法二就会生效。
P.S此修复适用于我的应用程序项目,可能不适用于其他人。仅提供错误和对我有帮助的地方。
【讨论】:
以上是关于Android 6.0 (MarshMallow) 上的 HttpURLConnnection 请求失败的主要内容,如果未能解决你的问题,请参考以下文章
Android 6.0 (MarshMallow) 上的 HttpURLConnnection 请求失败
如何在 Android 6.0 Marshmallow 中访问相机?
如何以编程方式打开 Android 6.0 (Marshmallow) 上特定应用的权限屏幕?
Android 6.0 Marshmallow BLE 连接问题