android网络编程

一、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);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值