下面是我使用过的网络框架,每种框架都有自己的优点缺点,要更具自身的应用场景选择。
测试使用一个ip地址查询接口进行测试:http://ip.tianqiapi.com?ip=xxx.xxx.xxx.xxx。
在Android中请求网络时要注意几点问题:
1.权限 :<uses-permission android:name="android.permission.INTERNET"/>
。
2.不能在主线程请求网络,将报错。
3.不能在子线程操作UI,将报错。
4.为了保证数据安全,在Android P 不能使用http,要更改为https,但是可以通过在AndroidManifest.xml文件中application标签增加 android:usesCleartextTraffic="true"来避免强制https,当然还有其他办法,不止一种,但一种足矣。(还是推荐自己的服务升级为https)。
一、HttpURLConnection
要说第一还是当属HttpURLConnection,在java阶段学习网络编程时从他开始。
首先是构建URL,通过URL打开HttpURLConnection,在使用IO流读取数据,这是最基本的操作了。可以更具setRequestMethod(“GET”)来修改请求类型。其他类型的操作也十分麻烦。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread() {
@Override
public void run() {
super.run();
doHttpURLConnection("http://ip.tianqiapi.com?ip=39.156.66.18");
}
}.start();
}
private void doHttpURLConnection(String u){
try {
URL url =new URL(u);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
InputStreamReader inputStreamReader =new InputStreamReader(urlConnection.getInputStream());
BufferedReader bufferedReader =new BufferedReader(inputStreamReader);
String temp="";
StringBuffer stringBuffer =new StringBuffer();
while ((temp=bufferedReader.readLine())!=null){
stringBuffer.append(temp);
}
Log.i(TAG, "doHttpURLConnection: "+stringBuffer);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
二、Okhttp
接下来是Okhttp,非常火的一个网络库,由Square公司提供。有很多优点,如利用响应缓存来避免重复的网络请求、拥有拦截器等,地址:https://github.com/square/okhttp。
首先是引入他,最新的是4.3.1。
implementation("com.squareup.okhttp3:okhttp:4.3.1")
API非常好用,首先是构建OkHttpClient,全局只需要初始化一次即可,可以通过内部构建对象Builder设置参数,如读取超时时间,接着通过Request.Builder()构建请求对象,如果是Get请求,则直接使用.get(),如果是Post请求,则使用.post(RequestBody),里面还要传入RequestBody对象,可以通过RequestBody的静态方法create创建,也可以使用他的子类,如FormBody。
Okhttp请求分为同步和异步,如果是同步的话,使用OhttpClitent.execute(),如果是异步,则要使用enqueue(),并传入回调地址。
Callback是回调接口,包括两个方法,失败回调onFailure和成功回调onResponse,通过response.body()可以拿到响应对象ResponseBody,最简单通过ResponseBody.string方法转换成string,不是toString()!!!,如果响应头中没有Content Type,则使用UTF8编码,否则使用指定编码,但是要注意的是,string方法只能调用一次,调用之后Okhttp会释放资源,所以第二次调用会报错。如果响应对象过大,可能引起OutOfMemoryError
private void dpOkHttp(String u){
OkHttpClient httpClient =new OkHttpClient();
FormBody body =new FormBody.Builder().build();
Request request =new Request.Builder()
.url(u)
.post(body)
.build();
String string = null;
//同步
try {
string = httpClient.newCall(request).execute().body().string();
Log.i(TAG, "dpOkHttp: "+string);
} catch (IOException e) {
e.printStackTrace();
}
//异步
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
Log.i(TAG, "onResponse: "+response.body().string());
}
});
}
三、okhttputils
这是张鸿洋大神封装的Okhttp库,以前写demo的时候经常用,使用也很简单。具体详细使用可以查看https://github.com/hongyangAndroid/okhttputils。注意版本要对应。
implementation 'com.zhy:okhttputils:2.6.2'
implementation("com.squareup.okhttp3:okhttp:3.3.1")
private void doOkhttpUtil(String u){
OkHttpClient httpClient =new OkHttpClient.Builder()
.build();
OkHttpUtils.initClient(httpClient);
OkHttpUtils.get()
.url(u)
.build()
.execute(new StringCallback() {
@Override
public void onError(Call call, Exception e, int id) {
e.printStackTrace();
}
@Override
public void onResponse(String response, int id) {
Log.i(TAG, "onResponse: "+response);
}
});
}
四、Volley
在Goole I/O 2013上发布的网络通信库,优点是自动调度网络请求、支持请求优先级、支持取消请求,可以取消单个请求或多个,等,场合在数据不大但通信频繁的情况下,jar包体积非常小,并且Volley回调时候是在主线程,可以直接操作UI,okhttputils也是在主线程,但是Okhttp不是,需要通过Handler处理。缺点也有,如文件下载和图片加载一般。
这个可以到https://developer.android.google.cn/training/volley详细查看。
首先是构建RequestQueue请求列队,全局也只初始化一次就好,接着构建Request请求对象,有StringRequest、ImageRequest、ClearCacheRequest、JsonRequest这四个子类。可以给Request设置一个tag,并通过RequestQueue.cancelAll(tag)可以进行取消。`
implementation 'com.android.volley:volley:1.1.1'
private void doVolley(String u) {
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest stringRequest = new StringRequest(Request.Method.GET,u, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.i(TAG, Thread.currentThread().getName()+" onResponse: " + response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
stringRequest.setTag("tag");
Request<String> request = requestQueue.add(stringRequest);
requestQueue.cancelAll("tag");
}
五、Retrofit
首先这是Square公司的,也是基于Okhttp扩展,最终网络请求由Okhttp完成,而Retrofit负责请求接口的封装。我现在一直在用这个。
他的地址https://square.github.io/retrofit/或https://github.com/square/retrofit
Retrofit全程操作依靠注解和接口来完成,写法不同于上面四种,非常有风格。
implementation("com.squareup.okhttp3:okhttp:3.3.1")
implementation 'com.squareup.retrofit2:retrofit:2.7.1'
首先创建一个接口,返回值在Call< T>中,@GET("/")表示请求路径,@Query是参数,也就是?后面的数据
public interface Apis {
@GET("/")
Call<ResponseBody> getIpAddress(@Query("ip")String ip);
}
接着就可以用以下方式请求了,其中Retrofit和Apis也不用每次请求都创建,create使用动态代理返回一个代理Apis的对象。构建Retrofit时候要通过baseUrl指明主机地址,请求时会拼接上@Get、@Post等中的值。
private void doRetrofit(String u) {
OkHttpClient okHttpClient = new OkHttpClient();
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl("http://ip.tianqiapi.com/")
.build();
Apis apis = retrofit.create(Apis.class);
apis.getIpAddress("39.156.66.18").enqueue(new retrofit2.Callback<ResponseBody>() {
@Override
public void onResponse(retrofit2.Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
try {
Log.i(TAG, "onResponse: " + response.body().string());
} catch (IOException e) {
}
}
@Override
public void onFailure(retrofit2.Call<ResponseBody> call, Throwable t) {
}
});
}
还要很多注解:
请求方式注解:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、HTTP
标记注解:FormUrlEncoded、Multipart、Streaming
参数类注解:Headers、Header、Body、Field、FieldMap、Part、PartMap、Query、QueryMap、Path
配合这些注解可以完成文件上传,Post提交json等请求。
更实用的是转换器,通过他可以把json字符串转换成对象。如果没有转换器,返回值必须一律是ResponseBody。
引入依赖,内部使用com.google.gson处理。
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
IpAddress。
public class IpAddress {
private String ip;
private String country;
private String province;
private String city;
private String isp;
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getIsp() {
return isp;
}
public void setIsp(String isp) {
this.isp = isp;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
@Override
public String toString() {
return "IpAddress{" +
"ip='" + ip + '\'' +
", country='" + country + '\'' +
", province='" + province + '\'' +
", city='" + city + '\'' +
", isp='" + isp + '\'' +
'}';
}
}
修改返回值。
public interface Apis {
@GET("/")
Call<IpAddress> getIpAddress(@Query("ip")String ip);
}
private void doRetrofit(String u) {
OkHttpClient okHttpClient = new OkHttpClient();
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(u)
.addConverterFactory(GsonConverterFactory.create())
.build();
Apis apis = retrofit.create(Apis.class);
apis.getIpAddress("39.156.66.18").enqueue(new retrofit2.Callback<IpAddress>() {
@Override
public void onResponse(retrofit2.Call<IpAddress> call, retrofit2.Response<IpAddress> response) {
Log.i(TAG, "onResponse: " + response.body().toString());
}
@Override
public void onFailure(retrofit2.Call<IpAddress> call, Throwable t) {
t.printStackTrace();
}
});
}
输出如下,非常的方便