一、HTTP协议原理: 1.简介:HTTP是一个属于应用层的面向对象的协议,由于其简洁欸,快速的方式,适用于分布式超媒体信息系统。 2.特点: (1)支持C/S(客户/服务器)模式。 (2)简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST,每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。 (3)灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。 (4)无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。 (5)无状态:HTTP协议是无状态协议,无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。 3.HTTP URL格式: http://host[“:”port][abs_path] http表示要通过HTTP协议来定位网络资源;host表示合法的Internet主机域名或者IP地址;port指定一个端口号,为空则使用默认端口80;abs_path指定请求资源的URI(Web上任意的可用资源)。
二、Okhttp3使用: 1.Android Studio 配置gradle:
compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'com.squareup.okio:okio:1.7.0'2.添加网络权限:
<uses-permission android:name="android.permission.INTERNET"/>3.异步get请求:
private void getAsynHttp() { mOkHttpClient=new OkHttpClient(); Request.Builder requestBuilder = new Request.Builder().url("http://www.baidu.com"); //可以省略,默认是GET请求 requestBuilder.method("GET",null); Request request = requestBuilder.build(); Call mcall= mOkHttpClient.newCall(request); mcall.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { if (null != response.cacheResponse()) { String str = response.cacheResponse().toString(); Log.i("wangshu", "cache---" + str); } else { response.body().string(); String str = response.networkResponse().toString(); Log.i("wangshu", "network---" + str); } runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), "请求成功", Toast.LENGTH_SHORT).show(); } }); } }); }4.异步Post请求:
private void postAsynHttp() { mOkHttpClient=new OkHttpClient(); RequestBody formBody = new FormBody.Builder() .add("size", "10") .build(); Request request = new Request.Builder() .url("http://api.1-blog.com/biz/bizserver/article/list.do") .post(formBody) .build(); Call call = mOkHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { String str = response.body().string(); Log.i("wangshu", str); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), "请求成功", Toast.LENGTH_SHORT).show(); } }); } }); }5.异步上传文件: 上传文件本身也是一个post请求,首先定义上传文件类型:
public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");将sdcard根目录的hehehe.txt文件上传到服务器上:
private void postAsynFile() { mOkHttpClient=new OkHttpClient(); File file = new File("/sdcard/hehehe.txt"); Request request = new Request.Builder() .url("https://api.github.com/markdown/raw") .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file)) .build(); mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { Log.i("cjf666",response.body().string()); } }); }如果想同步上传文件,只需要调用
mOkHttpClient.newCall(request).execute()就可以了。 不要忘了加权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>6.异步下载文件:
private void downAsynFile() { mOkHttpClient = new OkHttpClient(); String url = "https://img-my.csdn.net/uploads/201603/26/1458988468_5804.jpg"; Request request = new Request.Builder().url(url).build(); mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) { InputStream inputStream = response.body().byteStream(); FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(new File("/sdcard/cjf666.jpg")); byte[] buffer = new byte[2048]; int len = 0; while ((len = inputStream.read(buffer)) != -1) { fileOutputStream.write(buffer, 0, len); } fileOutputStream.flush(); } catch (IOException e) { Log.i("cjf666", "IOException"); e.printStackTrace(); } Log.d("cjf666", "文件下载成功"); } }); }7.异步上传Multipart文件: 这种场景很常用,我们有时会上传文件同时还需要传其他类型的字段,OkHttp3实现起来很简单,需要注意的是没有服务器接收我这个Multipart文件,所以这里只是举个例子,具体的应用还要结合实际工作中对应的服务器。 首先定义上传文件类型:
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png"); private void sendMultipart(){ mOkHttpClient = new OkHttpClient(); RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("title", "wangshu") .addFormDataPart("image", "wangshu.jpg", RequestBody.create(MEDIA_TYPE_PNG, new File("/sdcard/wangshu.jpg"))) .build(); Request request = new Request.Builder() .header("Authorization", "Client-ID " + "...") .url("https://api.imgur.com/3/image") .post(requestBody) .build(); mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { Log.i("wangshu", response.body().string()); } }); }8.设置超时时间和缓存: 和OkHttp2.x有区别的是不能通过OkHttpClient直接设置超时时间和缓存了,而是通过OkHttpClient.Builder来设置,通过builder配置好OkHttpClient后用builder.build()来返回OkHttpClient,所以我们通常不会调用new OkHttpClient()来得到OkHttpClient,而是通过builder.build():
File sdcache = getExternalCacheDir(); int cacheSize = 10 * 1024 * 1024; OkHttpClient.Builder builder = new OkHttpClient.Builder() .connectTimeout(15, TimeUnit.SECONDS) .writeTimeout(20, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) .cache(new Cache(sdcache.getAbsoluteFile(), cacheSize)); OkHttpClient mOkHttpClient=builder.build();9.取消请求: 使用call.cancel()可以立即停止掉一个正在执行的call。如果一个线程正在写请求或者读响应,将会引发IOException。当用户离开一个应用时或者跳到其他界面时,使用Call.cancel()可以节约网络资源,另外不管同步还是异步的call都可以取消。 也可以通过tags来同时取消多个请求。当你构建一请求时,使用RequestBuilder.tag(tag)来分配一个标签。之后你就可以用OkHttpClient.cancel(tag)来取消所有带有这个tag的call。 为了模拟这个场景我们首先创建一个定时的线程池:
private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);接下来的代码为:
private void cancel(){ final Request request = new Request.Builder() .url("http://www.baidu.com") .cacheControl(CacheControl.FORCE_NETWORK) .build(); Call call=null; call = mOkHttpClient.newCall(request); final Call finalCall = call; //1毫秒后取消call executor.schedule(new Runnable() { @Override public void run() { finalCall.cancel(); } }, 1, TimeUnit.MILLISECONDS); call.enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) { } @Override public void onResponse(final Response response) { if (null != response.cacheResponse()) { String str = response.cacheResponse().toString(); Log.i("cjf666", "cache---" + str); } else { try { response.body().string(); } catch (IOException e) { Log.i("cjf666", "IOException"); e.printStackTrace(); } String str = response.networkResponse().toString(); Log.i("cjf666", "network---" + str); } } }); Log.i("cjf666", "是否取消成功"+call.isCanceled()); }10.封装: 对Okhttp封装最需要解决的是以下两点: (1)避免重复调用代码。 (2)将请求结果回掉改为UI线程。 如果想使用Okhttp封装的开源库,推荐: https://github.com/pengjianbo/OkHttpFinal
三、Retrofit的使用: Retrofit2底层是基于Okhttp的。 1.使用前准备:
dependencies { ... compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.squareup.retrofit2:converter-scalars:2.1.0'//ConverterFactory的String依赖包 }2.Manifest中添加权限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>3.准备一个api接口用于测试: https://api.douban.com/v2/book/search?q=金瓶梅&tag=&start=0&count=1 用浏览器打开,返回内容:
{"count":1,"start":0,"total":535,"books":[{"rating":{"max":10,"numRaters":3684,"average":"8.5","min":0},"subtitle":"张竹坡批评第一奇书","author":["兰陵笑笑生"],"pubdate":"1991","tags":[{"count":1534,"name":"金瓶梅","title":"金瓶梅"},{"count":982,"name":"古典文学","title":"古典文学"},{"count":647,"name":"兰陵笑笑生","title":"兰陵笑笑生"},{"count":623,"name":"小说","title":"小说"},{"count":471,"name":"中国古典文学","title":"中国古典文学"},{"count":321,"name":"中国文学","title":"中国文学"},{"count":281,"name":"中国","title":"中国"},{"count":277,"name":"古典","title":"古典"}],"origin_title":"(明)兰陵笑笑生","image":"https://img1.doubanio.com\/mpic\/s10069398.jpg","binding":"","translator":[],"catalog":"\n ","pages":"","images":{"small":"https://img1.doubanio.com\/spic\/s10069398.jpg","large":"https://img1.doubanio.com\/lpic\/s10069398.jpg","medium":"https://img1.doubanio.com\/mpic\/s10069398.jpg"},"alt":"https:\/\/book.douban.com\/subject\/1456692\/","id":"1456692","publisher":"齐鲁出版社","isbn10":"7533300815","isbn13":"9787533300814","title":"金瓶梅","url":"https:\/\/api.douban.com\/v2\/book\/1456692","alt_title":"(明)兰陵笑笑生","author_intro":"","summary":"本书由王汝梅与李昭恂、于凤树校点。","series":{"id":"4279","title":"明代四大奇书"},"price":"268.00元"}]}4.新建一个实体类Book(GsonFormat): 5.定义一个接口:
public interface RetrofitService { @GET("book/search") Call<Book> getSearchBook(@Query("q") String name, @Query("tag") String tag, @Query("start") int start, @Query("count") int count); }这个方法做的其实很简单,就是拼接一个URL,然后进行网络请求。这里我们拼接的URL就是上文我们的测试URL:https://api.douban.com/v2/book/search?q=金瓶梅&tag=&start=0&count=1; 在这个URL中book/search就是GET后的值,而?后的q、tag、start、count等入参就是这个方法的入参。 上面我们用的Get方法,当然我们也可以根据需要选择用其他方法:
* GET ----------查找资源(查) * POST --------修改资源(改) * PUT ----------上传文件(增) * DELETE ----删除文件(删) * HEAD--------只请求页面的首部6.Retrofit注解:@Query
前面的例子就用了Query用来查询参数。
erface IpService{ @GET("getIpInfo.php") Call<IpModel> getIpMsg(@Query("ip")String ip); } @QueryMap如果Query参数比较多,那么可以通过@QueryMap方式将所有的参数集成在一个Map统一传递。
erface BlueService { @GET("book/search") Call<BookSearchResponse> getSearchBooks(@QueryMap Map<String, String> options); @Path @Path用来替换路径 public interface ApiStores { @GET("adat/sk/{cityId}.html") Call<ResponseBody> getWeather(@Path("cityId") String cityId); }; } @Body @Body与@POST注解一起使用,提供查询主体内容,其中ApiInfo是一个bean类erface ApiStores { @POST("client/shipper/getCarType") Call<ResponseBody> getCarType(@Body ApiInfo apiInfo); } @Headers interface SomeService { @GET("some/endpoint") @Headers("Accept-Encoding: application/json") Call<ResponseBody> getCarType(); } @Headers用来添加头部信息,上面用的是固定头部,也可以采用动态头部SomeService { @GET("some/endpoint") Call<SomeResponse> someEndpoint( @Header("Location") String location); }@Multipart
@Mulipart用来上传文件
public interface FileUploadService { @Multipart @POST("upload") Call<ResponseBody> upload(@Part("description") RequestBody description, @Part MultipartBody.Part file); }
