下面是我使用过的网络框架,每种框架都有自己的优点缺点,要根据自身的应用场景选择。

测试使用一个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请求分为同步和异步,如果是同步的话,使用httpClient.newCall(request).execute(),如果是异步,则要使用httpClient.newCall(request).enqueue(callback),并传入回调地址。

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(); } }); }

输出如下,非常的方便

那些年我用过的Android网络请求库_java