引言:
前些天,我在研究一个app项目结构的时候,被一个问题困扰,我发现它的网络请求部分没有单开线程,我找了半天它的异步网络请求操作没有找到,直到今天,谜团终于打开。
解释:
OKhttp用于网络请求,一般常见的就是GET与POST。
一个简单的get请求基本步骤如下:
1.创建一个okhttpclient对象:
OkHttpClient client = new OkHttpClient(); //创建一个窗口对象
2.创建一个请求对象:
Request request = new Request.Builder() //创建一个请求
.url("https://www.baidu.com")
.get() //表明为get请求
.build();
其实request里面还可以添加许多参数,比如请求头,请求方法,请求行,还有很多参数。
3.创建一个call对象:
Call call=client.newCall(request); //创建一个Call
4.发送请求,这里就有区分了,有同步的请求,异步的请求。call.execute()表示为同步请求,call.enqueue(new Callback(){…})为异步请求。他们的区别正是我的困扰所在,我开篇提到的问题在这里回答了,对于同步get请求,我们往往需要单开线程,因为网络请求比较耗时。对于异步get请求,我们则不需要单开线程,因为它本身运行在其他线程中。
下面我给出完整请求代码以及线程打印截图:
(1)同步:
private void sendSyncRequest(){
//发送同步get请求,由于网络请求耗时,需单开线程
new Thread(new Runnable() {
@Override
public void run() {
try {
Log.w(TAG,""+Thread.currentThread());
OkHttpClient client = new OkHttpClient(); //创建一个窗口对象
Request request = new Request.Builder() //创建一个请求
.url("https://www.baidu.com")
.get() //表明为get请求
.build();
Call call=client.newCall(request); //创建一个Call
Response response=call.execute(); //execute为同步GET请求,可直接获取Response对象
if(response.isSuccessful()){
String responseData=response.body().string();
Log.w(TAG+" success",""+Thread.currentThread());
showRequestSuccess(textView,responseData);
}else{
Log.w(TAG+" fail",""+Thread.currentThread());
showRequestFail(textView);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
).start();
}
(2)异步:
private void sendAsyncRequest(){
//发送异步get请求,本身就不在主线程运行
Log.w(TAG,""+Thread.currentThread());
OkHttpClient client = new OkHttpClient(); //创建一个窗口对象
Request request = new Request.Builder() //创建一个请求
.url("https://www.baidu.com")
.get() //表明为get请求
.build();
Call call=client.newCall(request); //创建一个Call
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
showRequestFail(textView);
Log.w(TAG+" fail",""+Thread.currentThread());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String responseData=response.body().string();
showRequestSuccess(textView,responseData);
Log.w(TAG+" success",""+Thread.currentThread());
}
});
}
我这里的showRequestSuccess和showRequestFail方法使用了Handler与主线程通信,详情参照我的另一篇文章:
5.post请求与get基本类似,只不过创建请求时可以post上去一些参数:
Request request1 = new Request.Builder() //创建一个请求
.url("https://www.baidu.com")
.post(RequestBody.create(...)) //表明为post请求
.build();
完整代码:
package com.example.test2.fragments;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.example.test2.R;
import com.example.test2.entities.MyHandler;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class Fragment3 extends Fragment implements View.OnClickListener {
private Button send,clear;
private TextView textView;
private MyHandler myHandler;
public static final int REQUEST_SUCCESS=1;
public static final int REQUEST_FAIL=2;
private String mFailString;
private static final String TAG="Fragment3";
/*线程池的使用*/
private ThreadPoolExecutor threadPoolExecutor=
new ThreadPoolExecutor(10,20,1, TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.lay3,container,false);
send=(Button)view.findViewById(R.id.send_request);
clear=(Button)view.findViewById(R.id.clear_data);
textView=(TextView)view.findViewById(R.id.receive_data);
mFailString=getString(R.string.request_fail_string);
myHandler=new MyHandler();
myHandler.onBindingMyData(textView,mFailString);
send.setOnClickListener(this);
clear.setOnClickListener(this);
return view;
}
private void sendAsyncRequest(){
//发送异步get请求,本身就不在主线程运行
Log.w(TAG,""+Thread.currentThread());
OkHttpClient client = new OkHttpClient(); //创建一个窗口对象
Request request = new Request.Builder() //创建一个请求
.url("https://www.baidu" +
".com")
.get() //表明为get请求
.build();
Call call=client.newCall(request); //创建一个Call
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
showRequestFail(textView);
Log.w(TAG+" fail",""+Thread.currentThread());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String responseData=response.body().string();
showRequestSuccess(textView,responseData);
Log.w(TAG+" success",""+Thread.currentThread());
}
});
}
private void sendSyncRequest(){
//发送同步请求,使用线程池来开启线程
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
try {
Log.w(TAG,""+Thread.currentThread());
OkHttpClient client = new OkHttpClient(); //创建一个窗口对象
Request request = new Request.Builder() //创建一个请求
.url("https://www.baidu.com")
.get() //表明为get请求
.build();
Call call=client.newCall(request); //创建一个Call
Response response=call.execute(); //execute为同步GET请求,可直接获取Response对象
if(response.isSuccessful()){
String responseData=response.body().string();
Log.w(TAG+" success",""+Thread.currentThread());
showRequestSuccess(textView,responseData);
}else{
Log.w(TAG+" fail",""+Thread.currentThread());
showRequestFail(textView);
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
//发送同步get请求,由于网络请求耗时,需单开线程
/*new Thread(new Runnable() {
@Override
public void run() {
try {
Log.w(TAG,""+Thread.currentThread());
OkHttpClient client = new OkHttpClient(); //创建一个窗口对象
Request request = new Request.Builder() //创建一个请求
.url("https://www.baidu.com")
.get() //表明为get请求
.build();
Call call=client.newCall(request); //创建一个Call
Response response=call.execute(); //execute为同步GET请求,可直接获取Response对象
if(response.isSuccessful()){
String responseData=response.body().string();
Log.w(TAG+" success",""+Thread.currentThread());
showRequestSuccess(textView,responseData);
}else{
Log.w(TAG+" fail",""+Thread.currentThread());
showRequestFail(textView);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
).start();*/
}
private void showRequestSuccess(TextView textView, final String response){
Message message=Message.obtain();
message.obj=response;
message.what=REQUEST_SUCCESS;
Log.w("fragment3",message.what+" ^_^");
myHandler.sendMessage(message);
}
private void showRequestFail(TextView textView){
Message message=Message.obtain();
message.obj=null;
message.what=REQUEST_FAIL;
Log.w("fragment3",message.what+" ^_^");
myHandler.sendMessage(message);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.send_request:
sendSyncRequest();
break;
case R.id.clear_data:
Toast.makeText(getActivity(),"Text was cleared",Toast.LENGTH_SHORT).show();
textView.setText("");
break;
default:
break;
}
}
}
注意:
1.对Response的操作也要放在子线程中。
2.response.body()后的.string()方法不能连续调用两次,别问我怎么知道的,你可以去试试:
错误示范:
getExcution(response.body().string()); //获取登录时需要的表单项之一
Log.w("main",response.body().string()); //.string方法不能连续调两次
然后运行结果如下:
更多:
更详细相关用法推荐(简书大佬写的):OkHttp3简单使用教程(一):请求和响应