一. HttpURLConnection

二. HttpClient  

三.Volley

四.OkHttp

五. Retrofit

-------------------------------------------------------------

一. HttpURLConnection

1. get请求方式

 1   public static void requestByGet() throws Exception {
 2         String path = "https://reg.163.com/logins.jsp?id=helloworld&pwd=android";
 3         // 新建一个URL对象
 4         URL url = new URL(path);
 5         // 打开一个HttpURLConnection连接
 6         HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
 7         // 设置连接超时时间
 8         urlConn.setConnectTimeout(5 * 1000);
 9         // 开始连接
10         urlConn.connect();
11         // 判断请求是否成功
12         if (urlConn.getResponseCode() == HTTP_200) {
13             // 获取返回的数据
14             byte[] data = readStream(urlConn.getInputStream());
15             Log.i(TAG_GET, "Get方式请求成功,返回数据如下:");
16             Log.i(TAG_GET, new String(data, "UTF-8"));
17         } else {
18             Log.i(TAG_GET, "Get方式请求失败");
19         }
20         // 关闭连接
21         urlConn.disconnect();
22     }  

2.  post请求方式

 1 public static void requestByPost() throws Throwable {
 2     String path = "https://reg.163.com/logins.jsp";
 3     // 请求的参数转换为byte数组
 4     String params = "id=" + URLEncoder.encode("helloworld", "UTF-8")
 5             + "&pwd=" + URLEncoder.encode("android", "UTF-8");
 6     byte[] postData = params.getBytes();
 7     // 新建一个URL对象
 8     URL url = new URL(path);
 9     // 打开一个HttpURLConnection连接
10     HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
11     // 设置连接超时时间
12     urlConn.setConnectTimeout(5 * 1000);
13     // Post请求必须设置允许输出
14     urlConn.setDoOutput(true);
15     // Post请求不能使用缓存
16     urlConn.setUseCaches(false);
17     // 设置为Post请求
18     urlConn.setRequestMethod("POST");
19     urlConn.setInstanceFollowRedirects(true);
20     // 配置请求Content-Type
21     urlConn.setRequestProperty("Content-Type",
22             "application/x-www-form-urlencode");
23     // 开始连接
24     urlConn.connect();
25     // 发送请求参数
26     DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream());
27     dos.write(postData);
28     dos.flush();
29     dos.close();
30     // 判断请求是否成功
31     if (urlConn.getResponseCode() == HTTP_200) {
32         // 获取返回的数据
33         byte[] data = readStream(urlConn.getInputStream());
34         Log.i(TAG_POST, "Post请求方式成功,返回数据如下:");
35         Log.i(TAG_POST, new String(data, "UTF-8"));
36     } else {
37         Log.i(TAG_POST, "Post方式请求失败");
38     }
39 }

二. HttpClient

1. get请求方式

 1 public static void requestByHttpGet() throws Exception {
 2     String path = "https://reg.163.com/logins.jsp?id=helloworld&pwd=android";
 3     // 新建HttpGet对象
 4     HttpGet httpGet = new HttpGet(path);
 5     // 获取HttpClient对象
 6     HttpClient httpClient = new DefaultHttpClient();
 7     // 获取HttpResponse实例
 8     HttpResponse httpResp = httpClient.execute(httpGet);
 9     // 判断是够请求成功
10     if (httpResp.getStatusLine().getStatusCode() == HTTP_200) {
11         // 获取返回的数据
12         String result = EntityUtils.toString(httpResp.getEntity(), "UTF-8");
13         Log.i(TAG_HTTPGET, "HttpGet方式请求成功,返回数据如下:");
14         Log.i(TAG_HTTPGET, result);
15     } else {
16         Log.i(TAG_HTTPGET, "HttpGet方式请求失败");
17     }
18 }  

2. post请求方式

 1 public static void requestByHttpPost() throws Exception {
 2     String path = "https://reg.163.com/logins.jsp";
 3     // 新建HttpPost对象
 4     HttpPost httpPost = new HttpPost(path);
 5     // Post参数
 6     List<NameValuePair> params = new ArrayList<NameValuePair>();
 7     params.add(new BasicNameValuePair("id", "helloworld"));
 8     params.add(new BasicNameValuePair("pwd", "android"));
 9     // 设置字符集
10     HttpEntity entity = new UrlEncodedFormEntity(params, HTTP.UTF_8);
11     // 设置参数实体
12     httpPost.setEntity(entity);
13     // 获取HttpClient对象
14     HttpClient httpClient = new DefaultHttpClient();
15     // 获取HttpResponse实例
16     HttpResponse httpResp = httpClient.execute(httpPost);
17     // 判断是够请求成功
18     if (httpResp.getStatusLine().getStatusCode() == HTTP_200) {
19         // 获取返回的数据
20         String result = EntityUtils.toString(httpResp.getEntity(), "UTF-8");
21         Log.i(TAG_HTTPGET, "HttpPost方式请求成功,返回数据如下:");
22         Log.i(TAG_HTTPGET, result);
23     } else {
24         Log.i(TAG_HTTPGET, "HttpPost方式请求失败");
25     }
26 }

三 . Volley

Volley是Google在2003年的I/O大会上推出的通信框架,结合了AsyncHttpClient和Universal- Image-Loader的优点——简化了http的使用 + 异步加载图片的神奇能力。Android中的Http实现主要有HttpUrlConnection和HttpClient两种,关于二者的选择 Google在Blog中表示推荐在姜饼小人(API level = 9)及以上的版本中使用Java的HttpUrlConnection而在之前的版本使用Apache的HttpClient,这在Volley这个框架 中也有明确的体现。

获取Volley
git clone https://android.googlesource.com/platform/frameworks/volley

把它编译成jar文件就可以加入libs了
1. 简单的请求(以StringRequest为例)
Http的通信最主要的部分应该就是发出请求和接收响应 了,所以Volley的比较核心的一个类就是RequestQueue,一个请求队列。它负责管理工作线程,读写缓存,和解析、分发响应(具体操作还是由 具体的类实现),即将发出的Http请求都会首先聚集在这里等待工作线程来实现请求。RequestQueue可以被看成一艘载满Http请求的航空母 舰,而工作线程就是弹射器喽。
所以按照航母起飞飞机的步骤,我们可以猜到利用Volley进行Http通信的简单步骤:
1.获取RequestQueue(得到一艘航母,可以是自己造的,也可以是委托别人造的,下面会提到)
2.实例化一个Request(得到一架飞机,你也知道飞机又很多类型啦)
3.将Request加入RequestQueue,等待工作线程将其发送出去(把飞机从机库升上起飞甲板,等待弹射器把它扔出去)
       起飞侦察机-发出GET请求
按照上面的步骤,第一步就是建立一个请求队列,最简单的方法就是用Volley.newRequestQueue(),这是一个特别方便的 静态方法,替我们默认实现了所有需要的东西(网络、缓存等,这些在Volley中都有默认实现),它会返回一个已经开始运行的 RequestQueue(相当于别人帮忙造了艘航母)。之后我们需要的只是设置好请求的响应监听接口,把请求加入到这个队列中就可以等着响应数据来敲门 了。下面是Google文档中的示例代码:
 1   //初始化一个请求队列
 2   RequestQueue queue = Volley.newRequestQueue(this);  3 String url ="http://www.google.com";  4  5 //根据给定的URL新建一个请求  6 StringRequest stringRequest = new StringRequest(Request.Method.GET, url,  7 new Response.Listener() {  8  @Override  9 public void onResponse(String response) { 10 //在这里操作UI组件是安全的,因为响应返回时这个函数会被post到UI线程来执行 11 // 在这里尽情蹂躏响应的String。 12  } 13 }, new Response.ErrorListener() { 14  @Override 15 public void onErrorResponse(VolleyError error) { 16 // 出错了怎么办?凉拌!并且在这里拌。 17  } 18 }); 19 // 把这个请求加入请求队列 20 queue.add(stringRequest);

StringRequest是Request的具体实现之一,代表解析后的响应数据是一个字符串,相似的还有JsonRequest(包括 JsonObjectRequest和JsonArrayRequest两个可以使用的子类)、ImageRequest来满足基本的使用,用法大同小 异。主要是构造参数不一样,分别如下:

  1.public StringRequest(int method, String url, Listener<String> listener,ErrorListener errorListener);      参数说明:从左到右分别是请求方法(都封装在Request中的Method接口内),请求URL,响应监听接口实例,错误监听接口实例。

2.public JsonObjectRequest(int method, String url, JSONObject jsonRequest,Listener<JSONObject> listener, ErrorListener errorListener);
public JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener,ErrorListener errorListener);
参数说明:如果是GET请求的话,jsonRequest传入null就可以了,否则在未指明请求方法的情况下(也就是第二个构造函数)会默认为POST请求。其他同上。
3.public JsonArrayRequest(String url, Listener<JSONArray> listener, ErrorListener errorListener);
参数说明:同上。
4.public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,Config decodeConfig, Response.ErrorListener errorListener);
参数说明:decodeConfig是图片的颜色属性,下面的几个值都可以使用。
Bitmap.Config中的颜色属性(枚举类型)
ALPHA_8  
ARGB_4444 由于质量低,已经被弃用,推荐用ARGB_8888
ARGB_8888 每个像素用4byte存储
RGB_565 每个像素用2byte存储,红色占5位,绿色占6位,蓝色占5位
       起飞战斗机-发出POST请求
基本方式和上面一样,但是怎么装导弹,啊不,是怎么提交的数据呢?
Volley会在Request的请求方法是POST(还有PUT和PATCH)的情况下调用Request类(就是XXXRequest的父 类)的getParam()函数来获取参数,提前剧透,如果使用的是HttpUrlConnection的话,调用getParam()是在 HurlStatck中的addBodyIfExists()函数实现的,感兴趣的话可以去看一下哈。所以,POST请求像下面这样就可以了。
 1 //初始化一个请求队列
 2 RequestQueue queue = Volley.newRequestQueue(this);  3 String url ="http://www.google.com";  4  5 //根据给定的URL新建一个请求  6 StringRequest stringRequest = new StringRequest(Request.Method.POST, url,  7 new Response.Listener() {  8  @Override  9 public void onResponse(String response) { 10 // 在这里处理请求得到的String类型的响应 11  } 12 }, new Response.ErrorListener() { 13  @Override 14 public void onErrorResponse(VolleyError error) { 15 // 在这里进行出错之后的处理 16  } 17 }) { 18 @Override 19 protected Map<String, String> getParams() throws AuthFailureError { 20 21 Map<String, String> map = new HashMap<String, String>(); 22 map.put("params1", "value1"); 23 map.put("params2", "value2"); 24 return map 25  }; 26 // 把这个请求加入请求队列 27 queue.add(stringRequest);

后悔药-取消请求
Request中有一个cancel()方法,调用这个就可以取消当前请求了,但是取消到哪一个层次就不一定了,但是Volley可以保证 响应处理函数(就是onResponse()和onErroeResponse())不会被调用。还有一个一起取消多个请求,就是在发出请求前调用 Request的setTag()方法为每个请求加一个标签,这个方法的参数是Object,所以我们可以使用任何类型作为标签。这样就可以调用 ReqiestQueue的cancelAll()函数取消一群标签了。比较常用的方法就是,将发出这个请求的Activity或者Fragment作为 标签,并在onStop()中调用cancelAll()。
2. 使用ImageLoader加载图片
  ImageLoader 是一个可以实现图片异步加载的类,但已经不是继承与Request了。ImageLoader虽然是头神兽,但必须在主线程召唤它,否则会抛出错误 IllegalStateException,可能是因为ImageLoader在图片返回时要直接操作ImageView,在主线程里操作UI组件才是 安全的,so~
用ImageLoader加载图片分三步
1.创建ImageLoader
2.获取一个ImageListener对象
3.调用ImageLoader的get()方法获取图片
ImageLoader的构造函数长成这样:public ImageLoader(RequestQueue queue, ImageCache imageCache);
所以实例化一个ImageLoader需要一个RequestQueue(之前建立的就行),还有一个ImageCache,这是一个 ImageLoader内部定义的接口,用来实现L1缓存——内存缓存(Volley在RequestQueue中已经实现了L2缓存——文件缓存)。 ImageLoader中并没有对传入的ImageCache在使用前判空的代码,传null进去会出错的。如果实在不想弄内存缓存,实现一个什么都不做 的ImageCache就好了。下面是代码:
 1 ImageLoader imageLoader = new ImageLoader(mRequestQueue, new ImageCache() {
 2  @Override  3 public void putBitmap(String url, Bitmap bitmap) {  4  }  5  6  @Override  7 public Bitmap getBitmap(String url) {  8 return null;  9  } 10 }); 11 12 //default_image是正在加载图片时占位用的 13 //error_image是加载不成功时显示的图片 14 ImageListener listener = ImageLoader.getImageListener(imageView, R.drawable.default_image, R.drawable.error_image); 15imageLoader.get("your image url", listener); 

除了ImageLoader之外Volley中还有一个Image加载的神器——NetworkImageView,使用步骤如下:
1.在布局文件中加入控件,并在Java代码中获取实例
2.设置default_image,error_image,图片URL,一个ImageLoader对象
代码如下:
1 networkImageView = (NetworkImageView) findViewById(R.id.network_image_view);
2 networkImageView.setDefaultImageResId(R.drawable.default_image);
3 networkImageView.setErrorImageResId(R.drawable.error_image); 4 networkImageView.setImageUrl("your image url", imageLoader); 

3.ImageRequest加载图片

 1 RequestQueue newRequestQueue = Volley.newRequestQueue(MainActivity.this);
 2             ImageRequest imageRequest = new ImageRequest(
 3                     "http://img0.imgtn.bdimg.com/it/u=2470748499,1329594201&fm=26&gp=0.jpg",
 4                     new Response.Listener<Bitmap>() {
 5                         @Override
 6                         public void onResponse(Bitmap response){
 7                             mBitmap = response;
 8                             mImageView.setImageBitmap(mBitmap);
 9                             // load the data from the web
10                             dataFragment.setData(mBitmap);
11                         }
12                     }, 0, 0, Bitmap.Config.RGB_565, null);
13
14             newRequestQueue.add(imageRequest);

4. Google推荐的用法
上面就是Volley的基本用法了,但是如果一个App需要频繁的网络通信的话,建立多个RequestQueue是件很奇怪的事儿(谁会 因为临时有飞机要在海上起飞就去新建一艘航母呢,这得多有钱啊),所以Google推荐我们只实例化一个RequestQueue来应付频繁的Http通 信,当然,要保证队列的寿命和App一样长。如何实现呢?Google又说了,不推荐在App的Application.onCretae()方法中实例 化一个RequestQueue(不过确实是个简单的方法哈),最好是建立一个单例模式的类,并把所有我们需要用到的Volley的瓶瓶罐罐都放进去,这 样显得更模块化。下面就是示例代码。这段代码中最重要的就是RequestQueue要用Application的Context实例化,要不然就会随着 Activity的生命周期不停重建。其实,像AsyncHttpClient中的纯静态使用方法也不错(详情见:http://loopj.com /android-async-http/)
PS:下面还实现了一个简单的ImageCache
 1 private static MySingleton mInstance;
 2     private RequestQueue mRequestQueue;  3 private ImageLoader mImageLoader;  4 private static Context mCtx;  5  6 private MySingleton(Context context) {  7 mCtx = context;  8 mRequestQueue = getRequestQueue();  9 10 mImageLoader = new ImageLoader(mRequestQueue, 11 new ImageLoader.ImageCache() { 12 private final LruCache<String, Bitmap> 13 cache = new LruCache<String, Bitmap>(20); 14 15  @Override 16 public Bitmap getBitmap(String url) { 17 return cache.get(url); 18  } 19 20  @Override 21 public void putBitmap(String url, Bitmap bitmap) { 22  cache.put(url, bitmap); 23  } 24  }); 25  } 26 27 public static synchronized MySingleton getInstance(Context context) { 28 if (mInstance == null) { 29 mInstance = new MySingleton(context); 30  } 31 return mInstance; 32  } 33 34 public RequestQueue getRequestQueue() { 35 if (mRequestQueue == null) { 36 // getApplicationContext()是关键, 它会避免 37 // Activity或者BroadcastReceiver带来的缺点. 38 mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext()); 39  } 40 return mRequestQueue; 41  } 42 43 public <T> void addToRequestQueue(Request<T> req) { 44  getRequestQueue().add(req); 45  } 46 47 public ImageLoader getImageLoader() { 48 return mImageLoader; 49  } 50 }

四.OkHttp

HTTP是现代应用网络的方式。这是我们如何交换数据和媒体。有效地进行HTTP使您的东西加载更快,并节省带宽。
OkHttp是默认情况下高效的HTTP客户端
    HTTP / 2支持允许同一主机的所有请求共享套接字。
    连接池减少请求延迟(如果HTTP / 2不可用)。
    透明GZIP缩小下载大小。
    响应缓存可以避免重复请求的网络。

当网络麻烦时,OkHttp坚持不懈:它将从常见的连接问题中静默地恢复。如果您的服务有多个IP地址,如果第一个连接失败,OkHttp将尝试替代地址。这对于IPv4 + IPv6以及在冗余数据中心中托管的服务是必需的。 OkHttp启动与现代TLS功能(SNI,ALPN)的新连接,如果握手失败,则返回TLS 1.0。
使用OkHttp很容易它的请求/响应API设计有流畅的构建器和不变性。它支持同步阻塞调用和具有回调的异步调用。
OkHttp支持Android 2.3及以上版本。对于Java,最低要求是1.7。

1. 下载URL并将其内容作为字符串打印

 1 package okhttp3.guide;
 2
 3 import java.io.IOException;
 4 import okhttp3.OkHttpClient;
 5 import okhttp3.Request;
 6 import okhttp3.Response;
 7
 8 public class GetExample {
 9   OkHttpClient client = new OkHttpClient();
10
11   String run(String url) throws IOException {
12     Request request = new Request.Builder()
13         .url(url)
14         .build();
15
16     try (Response response = client.newCall(request).execute()) {
17       return response.body().string();
18     }
19   }
20
21   public static void main(String[] args) throws IOException {
22     GetExample example = new GetExample();
23     String response = example.run("https://raw.github.com/square/okhttp/master/README.md");
24     System.out.println(response);
25   }
26 }

2. 将数据发送到服务

 1 package okhttp3.guide;
 2
 3 import java.io.IOException;
 4 import okhttp3.MediaType;
 5 import okhttp3.OkHttpClient;
 6 import okhttp3.Request;
 7 import okhttp3.RequestBody;
 8 import okhttp3.Response;
 9
10 public class PostExample {
11   public static final MediaType JSON
12       = MediaType.parse("application/json; charset=utf-8");
13
14   OkHttpClient client = new OkHttpClient();
15
16   String post(String url, String json) throws IOException {
17     RequestBody body = RequestBody.create(JSON, json);
18     Request request = new Request.Builder()
19         .url(url)
20         .post(body)
21         .build();
22     try (Response response = client.newCall(request).execute()) {
23       return response.body().string();
24     }
25   }
26
27   String bowlingJson(String player1, String player2) {
28     return "{'winCondition':'HIGH_SCORE',"
29         + "'name':'Bowling',"
30         + "'round':4,"
31         + "'lastSaved':1367702411696,"
32         + "'dateStarted':1367702378785,"
33         + "'players':["
34         + "{'name':'" + player1 + "','history':[10,8,6,7,8],'color':-13388315,'total':39},"
35         + "{'name':'" + player2 + "','history':[6,10,5,10,10],'color':-48060,'total':41}"
36         + "]}";
37   }
38
39   public static void main(String[] args) throws IOException {
40     PostExample example = new PostExample();
41     String json = example.bowlingJson("Jesse", "Jake");
42     String response = example.post("http://www.roundsapp.com/post", json);
43     System.out.println(response);
44   }
45 }

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

五. Retrofit

Retrofit是Square公司开发的一款针对Android网络请求的框架,Retrofit2底层基于OkHttp实现的,OkHttp现在已经得到Google官方认可,大量的app都采用OkHttp做网络请求,其源码详见OkHttp Github。

首先先来看一个完整Get请求是如何实现:

1. 创建业务请求接口

1 public interface BlueService {
2    @GET("book/search")
3    Call<BookSearchResponse> getSearchBooks(@Query("q") String name,
4         @Query("tag") String tag, @Query("start") int start,
5         @Query("count") int count);
6 }

这里需要稍作说明,@GET注解就表示get请求,@Query表示请求参数,将会以key=value的方式拼接在url后面。

2.  需要创建一个Retrofit的示例,并完成相应的配置

1 Retrofit retrofit = new Retrofit.Builder()
2    .baseUrl("https://api.douban.com/v2/")
3    .addConverterFactory(GsonConverterFactory.create())
4    .build();
5
6 BlueService service = retrofit.create(BlueService.class);

这里的baseUrl就是网络请求URL相对固定的地址,一般包括请求协议(如Http)、域名或IP地址、端口号等,当然还会有很多其他的配置,下文会详细介绍。还有addConverterFactory方法表示需要用什么转换器来解析返回值,GsonConverterFactory.create()表示调用Gson库来解析json返回值,具体的下文还会做详细介绍。

3.  调用请求方法,并得到Call实例

1  Call<BookSearchResponse> call = mBlueService.getSearchBooks("小王子", "", 0, 3);

Call其实在Retrofit中就是行使网络请求并处理返回值的类,调用的时候会把需要拼接的参数传递进去,此处最后得到的url完整地址为
        https://api.douban.com/v2/book/search?q=%E5%B0%8F%E7%8E%8B%E5%AD%90&tag=&start=0&count=3

4.   使用Call实例完成同步或异步请求

同步请求

1  BookSearchResponse response = call.execute().body();

这里需要注意的是网络请求一定要在子线程中完成,不能直接在UI线程执行,不然会crash

异步请求

 1   call.enqueue(new Callback<BookSearchResponse>() {
 2         @Override
 3         public void onResponse(Call<BookSearchResponse> call,        Response<BookSearchResponse> response) {
 4         asyncText.setText("异步请求结果: " + response.body().books.get(0).altTitle);
 5         }
 6         @Override
 7         public void onFailure(Call<BookSearchResponse> call, Throwable t) {
 8
 9         }
10         });
11        

----------------------------------------------------------------------------------------------------------------

然后看看是如何使用的。

首先需要在build.gradle文件中引入需要的第三包,配置如下:

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'

引入完第三包接下来就可以使用Retrofit来进行网络请求了。接下来会对不同的请求方式做进一步的说明。

Get方法

1. @Query

Get方法请求参数都会以key=value的方式拼接在url后面,Retrofit提供了两种方式设置请求参数。第一种就是像上文提到的直接在interface中添加@Query注解,还有一种方式是通过Interceptor实现,直接看如何通过Interceptor实现请求参数的添加。

 1 public class CustomInterceptor implements Interceptor {
 2     @Override
 3     public Response intercept(Chain chain) throws IOException {
 4         Request request = chain.request();
 5         HttpUrl httpUrl = request.url().newBuilder()
 6                 .addQueryParameter("token", "tokenValue")
 7                 .build();
 8         request = request.newBuilder().url(httpUrl).build();
 9         return chain.proceed(request);
10     }
11 }

addQueryParameter就是添加请求参数的具体代码,这种方式比较适用于所有的请求都需要添加的参数,一般现在的网络请求都会添加token作为用户标识,那么这种方式就比较适合。
创建完成自定义的Interceptor后,还需要在Retrofit创建client处完成添加
addInterceptor(new CustomInterceptor())

2. @QueryMap

如果Query参数比较多,那么可以通过@QueryMap方式将所有的参数集成在一个Map统一传递,还以上文中的get请求方法为例

1 public interface BlueService {
2     @GET("book/search")
3     Call<BookSearchResponse> getSearchBooks(@QueryMap Map<String, String> options);
4 }

调用的时候将所有的参数集合在统一的map中即可

1 Map<String, String> options = new HashMap<>();
2 map.put("q", "小王子");
3 map.put("tag", null);
4 map.put("start", "0");
5 map.put("count", "3");
6 Call<BookSearchResponse> call = mBlueService.getSearchBooks(options);

3. Query集合

假如你需要添加相同Key值,但是value却有多个的情况,一种方式是添加多个@Query参数,还有一种简便的方式是将所有的value放置在列表中,然后在同一个@Query下完成添加,实例代码如下:

1 public interface BlueService {
2     @GET("book/search")
3     Call<BookSearchResponse> getSearchBooks(@Query("q") List<String> name);
4 }

最后得到的url地址为
https://api.douban.com/v2/book/search?q=leadership&q=beyond%20feelings

4. Query非必填

如果请求参数为非必填,也就是说即使不传该参数,服务端也可以正常解析,那么如何实现呢?其实也很简单,请求方法定义处还是需要完整的Query注解,某次请求如果不需要传该参数的话,只需填充null即可。

针对文章开头提到的get的请求,加入按以下方式调用

1 Call<BookSearchResponse> call = mBlueService.getSearchBooks("小王子", null, 0, 3);

那么得到的url地址为
https://api.douban.com/v2/book/search?q=%E5%B0%8F%E7%8E%8B%E5%AD%90&start=0&count=3

5. @Path

如果请求的相对地址也是需要调用方传递,那么可以使用@Path注解,示例代码如下:

1 @GET("book/{id}")
2 Call<BookResponse> getBook(@Path("id") String id);

业务方想要在地址后面拼接书籍id,那么通过Path注解可以在具体的调用场景中动态传递,具体的调用方式如下:

1  Call<BookResponse> call = mBlueService.getBook("1003078");

此时的url地址为
https://api.douban.com/v2/book/1003078
@Path可以用于任何请求方式,包括Post,Put,Delete等等

Post请求

1. @field

Post请求需要把请求参数放置在请求体中,而非拼接在url后面,先来看一个简单的例子

1  @FormUrlEncoded
2  @POST("book/reviews")
3  Call<String> addReviews(@Field("book") String bookId, @Field("title") String title,
4  @Field("content") String content, @Field("rating") String rating);

这里有几点需要说明的
    @FormUrlEncoded将会自动将请求参数的类型调整为application/x-www-form-urlencoded,假如content传递的参数为Good Luck,那么最后得到的请求体就是

1 content=Good+Luck

FormUrlEncoded不能用于Get请求
    @Field注解将每一个请求参数都存放至请求体中,还可以添加encoded参数,该参数为boolean型,具体的用法为

1 @Field(value = "book", encoded = true) String book

encoded参数为true的话,key-value-pair将会被编码,即将中文和特殊字符进行编码转换

2. @FieldMap

上述Post请求有4个请求参数,假如说有更多的请求参数,那么通过一个一个的参数传递就显得很麻烦而且容易出错,这个时候就可以用FieldMap

1  @FormUrlEncoded
2  @POST("book/reviews")
3  Call<String> addReviews(@FieldMap Map<String, String> fields);

3. @Body

如果Post请求参数有多个,那么统一封装到类中应该会更好,这样维护起来会非常方便

 1 @FormUrlEncoded
 2 @POST("book/reviews")
 3 Call<String> addReviews(@Body Reviews reviews);
 4
 5 public class Reviews {
 6     public String book;
 7     public String title;
 8     public String content;
 9     public String rating;
10 }

上传

上传因为需要用到Multipart,所以需要单独拿出来介绍,先看一个具体上传的例子

首先还是需要新建一个interface用于定义上传方法

 1 public interface FileUploadService {
 2     // 上传单个文件
 3     @Multipart
 4     @POST("upload")
 5     Call<ResponseBody> uploadFile(
 6             @Part("description") RequestBody description,
 7             @Part MultipartBody.Part file);
 8
 9     // 上传多个文件
10     @Multipart
11     @POST("upload")
12     Call<ResponseBody> uploadMultipleFiles(
13             @Part("description") RequestBody description,
14             @Part MultipartBody.Part file1,
15             @Part MultipartBody.Part file2);
16 }

接下来我们还需要在Activity和Fragment中实现两个工具方法,代码如下:

 1 public static final String MULTIPART_FORM_DATA = "multipart/form-data";
 2
 3 @NonNull
 4 private RequestBody createPartFromString(String descriptionString) {
 5     return RequestBody.create(
 6             MediaType.parse(MULTIPART_FORM_DATA), descriptionString);
 7 }
 8
 9 @NonNull
10 private MultipartBody.Part prepareFilePart(String partName, Uri fileUri) {
11     File file = FileUtils.getFile(this, fileUri);
12
13     // 为file建立RequestBody实例
14     RequestBody requestFile =
15         RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), file);
16
17     // MultipartBody.Part借助文件名完成最终的上传
18     return MultipartBody.Part.createFormData(partName, file.getName(), requestFile);
19 }

好了,接下来就是最终的上传文件代码了

 1 Uri file1Uri = ... // 从文件选择器或者摄像头中获取
 2 Uri file2Uri = ...
 3
 4 // 创建上传的service实例
 5 FileUploadService service =
 6         ServiceGenerator.createService(FileUploadService.class);
 7
 8 // 创建文件的part (photo, video, ...)
 9 MultipartBody.Part body1 = prepareFilePart("video", file1Uri);
10 MultipartBody.Part body2 = prepareFilePart("thumbnail", file2Uri);
11
12 // 添加其他的part
13 RequestBody description = createPartFromString("hello, this is description speaking");
14
15 // 最后执行异步请求操作
16 Call<ResponseBody> call = service.uploadMultipleFiles(description, body1, body2);
17 call.enqueue(new Callback<ResponseBody>() {
18     @Override
19     public void onResponse(Call<ResponseBody> call,
20             Response<ResponseBody> response) {
21         Log.v("Upload", "success");
22     }
23     @Override
24     public void onFailure(Call<ResponseBody> call, Throwable t) {
25         Log.e("Upload error:", t.getMessage());
26     }
27 });

其他必须知道的事项

1. 添加自定义的header

Retrofit提供了两个方式定义Http请求头参数:静态方法和动态方法,静态方法不能随不同的请求进行变化,头部信息在初始化的时候就固定了。而动态方法则必须为每个请求都要单独设置。

静态方法

1 public interface BlueService {
2     @Headers("Cache-Control: max-age=640000")
3       @GET("book/search")
4       Call<BookSearchResponse> getSearchBooks(@Query("q") String name,
5             @Query("tag") String tag, @Query("start") int start,
6             @Query("count") int count);
7     }

当然你想添加多个header参数也是可以的,写法也很简单

 1   public interface BlueService {
 2     @Headers({
 3           "Accept: application/vnd.yourapi.v1.full+json",
 4           "User-Agent: Your-App-Name"
 5       })
 6       @GET("book/search")
 7       Call<BookSearchResponse> getSearchBooks(@Query("q") String name,
 8             @Query("tag") String tag, @Query("start") int start,
 9             @Query("count") int count);
10     }

此外也可以通过Interceptor来定义静态请求头

 1   public class RequestInterceptor implements Interceptor {
 2       @Override
 3       public Response intercept(Chain chain) throws IOException {
 4           Request original = chain.request();
 5           Request request = original.newBuilder()
 6               .header("User-Agent", "Your-App-Name")
 7               .header("Accept", "application/vnd.yourapi.v1.full+json")
 8               .method(original.method(), original.body())
 9               .build();
10           return chain.proceed(request);
11       }
12     }

添加header参数Request提供了两个方法,一个是header(key, value),另一个是.addHeader(key, value),两者的区别是,header()如果有重名的将会覆盖,而addHeader()允许相同key值的header存在

然后在OkHttp创建Client实例时,添加RequestInterceptor即可

1  private static OkHttpClient getNewClient(){
2     return new OkHttpClient.Builder()
3       .addInterceptor(new RequestInterceptor())
4       .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
5       .build();
6     }

动态方法

1    public interface BlueService {
2       @GET("book/search")
3       Call<BookSearchResponse> getSearchBooks(
4       @Header("Content-Range") String contentRange,
5       @Query("q") String name, @Query("tag") String tag,
6       @Query("start") int start, @Query("count") int count);
7     }

2. 网络请求日志

调试网络请求的时候经常需要关注一下请求参数和返回值,以便判断和定位问题出在哪里,Retrofit官方提供了一个很方便查看日志的Interceptor,你可以控制你需要的打印信息类型,使用方法也很简单。

首先需要在build.gradle文件中引入logging-interceptor

compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'

同上文提到的CustomInterceptor和RequestInterceptor一样,添加到OkHttpClient创建处即可,完整的示例代码如下:

1 private static OkHttpClient getNewClient(){
2     HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
3     logging.setLevel(HttpLoggingInterceptor.Level.BODY);
4     return new OkHttpClient.Builder()
5            .addInterceptor(new CustomInterceptor())
6            .addInterceptor(logging)
7            .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
8            .build();
9 }

HttpLoggingInterceptor提供了4中控制打印信息类型的等级,分别是NONE,BASIC,HEADERS,BODY,接下来分别来说一下相应的打印信息类型。

NONE

没有任何日志信息

Basic

打印请求类型,URL,请求体大小,返回值状态以及返回值的大小

D/HttpLoggingInterceptor$Logger: --> POST /upload HTTP/1.1 (277-byte body)  
    D/HttpLoggingInterceptor$Logger: <-- HTTP/1.1 200 OK (543ms, -1-byte body)

Headers

打印返回请求和返回值的头部信息,请求类型,URL以及返回值状态码

<-- 200 OK https://api.douban.com/v2/book/search?q=%E5%B0%8F%E7%8E%8B%E5%AD%90&start=0&count=3&token=tokenValue (3787ms)
    D/OkHttp: Date: Sat, 06 Aug 2016 14:26:03 GMT
    D/OkHttp: Content-Type: application/json; charset=utf-8
    D/OkHttp: Transfer-Encoding: chunked
    D/OkHttp: Connection: keep-alive
    D/OkHttp: Keep-Alive: timeout=30
    D/OkHttp: Vary: Accept-Encoding
    D/OkHttp: Expires: Sun, 1 Jan 2006 01:00:00 GMT
    D/OkHttp: Pragma: no-cache
    D/OkHttp: Cache-Control: must-revalidate, no-cache, private
    D/OkHttp: Set-Cookie: bid=D6UtQR5N9I4; Expires=Sun, 06-Aug-17 14:26:03 GMT; Domain=.douban.com; Path=/
    D/OkHttp: X-DOUBAN-NEWBID: D6UtQR5N9I4
    D/OkHttp: X-DAE-Node: dis17
    D/OkHttp: X-DAE-App: book
    D/OkHttp: Server: dae
    D/OkHttp: <-- END HTTP

Body

打印请求和返回值的头部和body信息

<-- 200 OK https://api.douban.com/v2/book/search?q=%E5%B0%8F%E7%8E%8B%E5%AD%90&tag=&start=0&count=3&token=tokenValue (3583ms)
    D/OkHttp: Connection: keep-alive
    D/OkHttp: Date: Sat, 06 Aug 2016 14:29:11 GMT
    D/OkHttp: Keep-Alive: timeout=30
    D/OkHttp: Content-Type: application/json; charset=utf-8
    D/OkHttp: Vary: Accept-Encoding
    D/OkHttp: Expires: Sun, 1 Jan 2006 01:00:00 GMT
    D/OkHttp: Transfer-Encoding: chunked
    D/OkHttp: Pragma: no-cache
    D/OkHttp: Connection: keep-alive
    D/OkHttp: Cache-Control: must-revalidate, no-cache, private
    D/OkHttp: Keep-Alive: timeout=30
    D/OkHttp: Set-Cookie: bid=ESnahto1_Os; Expires=Sun, 06-Aug-17 14:29:11 GMT; Domain=.douban.com; Path=/
    D/OkHttp: Vary: Accept-Encoding
    D/OkHttp: X-DOUBAN-NEWBID: ESnahto1_Os
    D/OkHttp: Expires: Sun, 1 Jan 2006 01:00:00 GMT
    D/OkHttp: X-DAE-Node: dis5
    D/OkHttp: Pragma: no-cache
    D/OkHttp: X-DAE-App: book
    D/OkHttp: Cache-Control: must-revalidate, no-cache, private
    D/OkHttp: Server: dae
    D/OkHttp: Set-Cookie: bid=5qefVyUZ3KU; Expires=Sun, 06-Aug-17 14:29:11 GMT; Domain=.douban.com; Path=/
    D/OkHttp: X-DOUBAN-NEWBID: 5qefVyUZ3KU
    D/OkHttp: X-DAE-Node: dis17
    D/OkHttp: X-DAE-App: book
    D/OkHttp: Server: dae
    D/OkHttp: {"count":3,"start":0,"total":778,"books":[{"rating":{"max":10,"numRaters":202900,"average":"9.0","min":0},"subtitle":"","author":["[法] 圣埃克苏佩里"],"pubdate":"2003-8","tags":[{"count":49322,"name":"小王子","title":"小王子"},{"count":41381,"name":"童话","title":"童话"},{"count":19773,"name":"圣埃克苏佩里","title":"圣埃克苏佩里"}
    D/OkHttp: <-- END HTTP (13758-byte body)

3. 为某个请求设置完整的URL

​ 假如说你的某一个请求不是以base_url开头该怎么办呢?别着急,办法很简单,看下面这个例子你就懂了

 1 public interface BlueService {
 2     @GET
 3     public Call<ResponseBody> profilePicture(@Url String url);
 4 }
 5
 6 Retrofit retrofit = Retrofit.Builder()
 7     .baseUrl("https://your.api.url/");
 8     .build();
 9
10 BlueService service = retrofit.create(BlueService.class);
11 service.profilePicture("https://s3.amazon.com/profile-picture/path");

​ 直接用@Url注解的方式传递完整的url地址即可。

4. 取消请求

Call提供了cancel方法可以取消请求,前提是该请求还没有执行

 1 String fileUrl = "http://futurestud.io/test.mp4";
 2 Call<ResponseBody> call =
 3     downloadService.downloadFileWithDynamicUrlSync(fileUrl);
 4 call.enqueue(new Callback<ResponseBody>() {
 5     @Override
 6     public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
 7         Log.d(TAG, "request success");
 8     }
 9
10     @Override
11     public void onFailure(Call<ResponseBody> call, Throwable t) {
12         if (call.isCanceled()) {
13             Log.e(TAG, "request was cancelled");
14         } else {
15             Log.e(TAG, "other larger issue, i.e. no network connection?");
16         }
17     }
18 });
19     }
20
21 // 触发某个动作,例如用户点击了取消请求的按钮
22 call.cancel();
23 }

转载于:https://www.cnblogs.com/neo-java/p/6841343.html

Android -- 网络请求相关推荐

  1. okhttp的应用详解与源码解析--android网络请求框架发展史

    乘5G之势,借物联网之风,Android未来亦可期,Android优势在于开放,手机.平板.车载设备.智能家居等都是Android的舞台,Google不倒,Android不灭,本专栏的同步视频教程已经 ...

  2. Android网络请求框架之Retrofit(二)

    前面一篇文章介绍了Retrofit的基本用法,没有看过的童鞋可以移步:Android网络请求框架之Retrofit(一),现在我们来继续介绍Retrofit配合RxJava.RxAndroid的用法. ...

  3. android网络请求 post

    2019独角兽企业重金招聘Python工程师标准>>> 最近刚了解网络请求post android-async-http-1.4.8.jar AsyncHttpClient cile ...

  4. Android 网络请求详解

    我们知道大多数的 Android 应用程序都是通过和服务器进行交互来获取数据的.如果使用 HTTP 协议来发送和接收网络数据,就免不了使用 HttpURLConnection 和 HttpClient ...

  5. android网络请求流程图,Android OKHttp系列1-流程总结

    1. 调用示例 同步方式: new Thread(new Runnable() { @Override public void run() { try { OkHttpClient client = ...

  6. Android 网络请求HttpURLConnection 和 HttpClient详解

    Android一般通过http协议向服务端接口发送请求,常用有POST和GET传输方式.这种请求通常借助于HttpClient,HttpClient 是 Apache Jakarta Common 下 ...

  7. android retrofit2.0 rxjava2,Android - 网络请求之 Retrofit2 + RxJava

    老婆保佑,代码无BUG 目录 引用 与其他开源请求库对比 Retrofit注解 使用 GET请求 POST请求 Retrofit2 + RxJava 基础使用 优化 封装Retrofit2 + RxJ ...

  8. Retrofit网络请求框架使用简析——Android网络请求框架(四)

    题记:-- 很累,累到想要放弃,但是放弃之后将会是一无所有,又不能放弃, 唯有坚持,唯有给自忆打气,才能更勇敢的走下去,因为无路可退,只能前行, 时光一去不复返,每一天都不可追回,所以要更珍惜每一存光 ...

  9. Volley网络请求框架简析——Android网络请求框架(三)

    题记-- 人来到这个世界上,只有两件事情,生与死, 一件事完了,另一件事还急什么? 有缘而来,无缘而去, 识自本心,见自本性 不起妄缘,无心无为 自由自在,动静自如 冷暖自知,则是修行 1.初始化一个 ...

最新文章

  1. 【Qt】通过QtCreator源码学习Qt(四):插件管理PluginManager
  2. HL7 Figure 2-1. Delimiter values(分隔符值)
  3. 学python是看书还是看视频-Python与豆瓣读书
  4. ORA-16014 与 ORA-00312
  5. python 函数参数注解_python-如何使用函数注释来验证函数调用类...
  6. MFC获取屏幕分辨率
  7. WWDC21 定档,苹果眼镜成最大猜想
  8. PolyCode编译(Linux)
  9. NeatUpload 同时选择并上传多个文件
  10. 莫言母亲的八大教育真经
  11. 【渝粤题库】陕西师范大学200091 东方文学专题研究 作业
  12. 屏蔽计算机电缆套什么定额,DJYPVP4*2*1.0计算机屏蔽电缆-DJYVP分屏蔽计算机电缆...
  13. Sexagenary Cycle(干支纪年)
  14. cla作用matlab,健身搞肌运动补剂小讲堂:共轭亚油酸CLA功效解析
  15. 程序员编程规范之注释
  16. 固定资产拆分比例怎么计算_固定资产折旧的计算方法
  17. BBS(仿博客园系统)项目03(主页搭建、个人站点搭建(侧边栏分类展示、标签展示、日期归档)、文章详情页相关功能实现)...
  18. Arcgis API For js 的离线部署
  19. Azure Command Line(Azure CLI)指南
  20. 【QT项目——视频播放器——解码】5.1decoder-5.10音频重采样

热门文章

  1. 程序员遇到bug时常见的30种反应
  2. ups容量计算和配置方法_山埔UPS电源后备时间计算方法
  3. ffmpeg调用directshow camera 并sdl渲染
  4. cmakefile 基础篇
  5. linux钩子函数和回调函数,Linux Kernel 学习笔记10:hook函数
  6. u-boot移植随笔:解决引导内核遇到undefined instruction的错误
  7. 【java】浅析JDK中ServiceLoader的源码
  8. 【Flink】Flink 操作HDFS报错 hadoop is not in the classpath/dependencies
  9. 《spring-boot学习》-05-spring boot中redis应用
  10. 为什么网上都推荐下载jdk8和jdk11,而没有人推荐最新的15