在Android4.0以后,在主线程中的HTTP请求,运行时都会报错 ANRs (“Application Not Responding”),”应用没有响应“。
所以当进行网络请求的时候 还是需要开辟一个子线程,然后等到数据返回成功后再刷新UI。
private void sendRequestWithOkHttp() {
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
// 指定访问的服务器地址是电脑本机
.url("http://10.0.2.2/get_data.json")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
parseJSONWithJSONObject(responseData);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
1 JSONArray对象 构造函数中放入获得的response.body 类型为string
(而这个response是client通过newCall发送request 来execute获得的 具体可以看上面
2.jsonArray为数组 定义JSONObject 其方法为jsonArray.getJSONObject(i)
3 jsonObject.getString(“id”)
循环遍历这个JSONArray ,从中取出的每一个元素都是一个JSONObject 对象,每个JSONObject 对象中又会包含id 、name 和version 这些数据。接下来只需要调用getString() 方法将这些数据取出,并打印出来即可。
下面是具体代码
private void parseJSONWithJSONObject(String jsonData) {
try {
JSONArray jsonArray = new JSONArray(jsonData);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
String id = jsonObject.getString("id");
String name = jsonObject.getString("name");
String version = jsonObject.getString("version");
Log.d("MainActivity", "id is " + id);
Log.d("MainActivity", "name is " + name);
Log.d("MainActivity", "version is " + version);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
前方代码一致和上个一致。后面就简单起来了:
private void parseJSONWithGSON(String jsonData) {
Gson gson = new Gson();
List appList = gson.fromJson(jsonData, new TypeToken>()
{}.getType());
for (App app : appList) {
Log.d("MainActivity", "id is " + app.getId());// 其他相同
}
}
这样就可以 获取并且解析JSON数据并且log出解析的具体了
通常情况下我们都应该将这些通用的网络操作提取到一个公共的类里,并提供一个静态方法
public static String sendHttpRequest(String address) {
在获取到服务器响应的数据后,我们就可以对它进行解析和处理了。但是需要注意,网络请求通常都是属于耗时操作,而sendHttpRequest() 方法的内部并没有开启线程,这样就有可能导致在调用sendHttpRequest() 方法的时候使得主线程被阻塞住。
以及,若直接在方法内开一个县城,所有的耗时逻辑都是在子线程里进行的,sendHttpRequest() 方法会在服务器还没来得及响应的时候就执行结束了,当然也就无法返回响应的数据了。 我们就需要Java的回调机制
public interface HttpCallbackListener {
void onFinish(String response);
void onError(Exception e);
}
但是 我们可以使用okhttp的 它给我们封装好了 很多东西
public static void sendOkHttpRequest(String address, **okhttp3.Callback callback**) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(address)
.build();
client.newCall(request).enqueue(callback);
}
okhttp3.Callback 参数是OkHttp库中自带的一个回调接口,类似于我们刚才自己编写的HttpCallbackListener
OkHttp在enqueue() 方法的内部帮我们开好了子线程,然后会在子线程中去执行HTTP请求,并将最终的请求结果回调到okhttp3.Callback 当中。在调用sendOkHttpRequest() 方法的时候就可以这样写:
HttpUtil.sendOkHttpRequest("http://www.baidu.com", new okhttp3.Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
// 得到服务器返回的具体内容
String responseData = response.body().string();
}
@Override
public void onFailure(Call call, IOException e) {
// 在这里对异常情况进行处理
}
});
注意写法 这里 是调用的时候那个括号直接括进来搞的qwq
好
注意理解:
但是需要注意,网络请求通常都是属于耗时操作,而sendHttpRequest() 方法的内部并没有开启线程,这样就有可能导致在调用sendHttpRequest() 方法的时候使得主线程被阻塞住。
你可能会说,很简单嘛,在sendHttpRequest() 方法内部开启一个线程不就解决这个问题了吗?其实没有你想象中的那么容易,因为如果我们在sendHttpRequest() 方法中开启了一个线程来发起HTTP请求,那么服务器响应的数据是无法进行返回的,所有的耗时逻辑都是在子线程里进行的,sendHttpRequest() 方法会在服务器还没来得及响应的时候就执行结束了,当然也就无法返回响应的数据了。
最终的回调接口都还是在子线程中运行的,因此我们不可以在这里执行任何的UI操作,除非借助runOnUiThread() 方法来进行线程转换
或许我错了 写博客真正的意图是写和整理 就像最初开始做题的时候的感觉 而不是边写边巴拉巴拉 这样写了只是记录而已 自己回去看也看不懂 真的有意义吗