赞
踩

Okhttp3脑图
网络访问的高效性要求,可以说是为高效而生

OkHttp3设计思路
(1)添加网络访问权限并添加库依赖
<uses-permission android:name="android.permission.INTERNET" />
compile 'com.squareup.okhttp3:okhttp:3.4.1'
(2)GET
- OkHttpClient client = new OkHttpClient();
-
- String run(String url) throws IOException {
- Request request = new Request.Builder()
- .url(url)
- .build();
-
- Response response = client.newCall(request).execute();
- return response.body().string();
- }
(3)POST
- public static final MediaType JSON
- = MediaType.parse("application/json; charset=utf-8");
-
- OkHttpClient client = new OkHttpClient();
-
- String post(String url, String json) throws IOException {
- RequestBody body = RequestBody.create(JSON, json);
- Request request = new Request.Builder()
- .url(url)
- .post(body)
- .build();
- Response response = client.newCall(request).execute();
- return response.body().string();
- }
(3)异步调用
使用enqueue方法,将call放入请求队列,然后okHttp会在线程池中进行网络访问;只需要在适当的时候(需要操作UI的时候)发送一个消息给主线程的Handler(取决于Looper,使用Looper.getMainLooper()创建的Handler就是主线程Handler)就可以了~
- private Handler mHandler;
- private TextView mTxt;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_home);
- mTxt = (TextView) findViewById(R.id.txt);
- mHandler = new Handler(Looper.getMainLooper()){
- @Override
- public void handleMessage(Message msg) {
- mTxt.setText((String) msg.obj);
- }
- };
- OkHttpClient client = new OkHttpClient();
- Request request = new Request.Builder().url("https://github.com").build();
- client.newCall(request).enqueue(new Callback() {
- @Override
- public void onFailure(Call call, IOException e) {
- }
- @Override
- public void onResponse(Call call, Response response) throws IOException {
- Message msg = new Message();
- msg.what=0;
- msg.obj = response.body().string();
- mHandler.sendMessage(msg);
- }
- });
- }

(4)HTTP头部的设置和读取
HTTP 头的数据结构是 Map<String, List<String>>类型。也就是说,对于每个 HTTP 头,可能有多个值。但是大部分 HTTP 头都只有一个值,只有少部分 HTTP 头允许多个值。OkHttp的处理方式是:
header(name,value)来设置HTTP头的唯一值addHeader(name,value)来补充新值header(name)读取唯一值或多个值的最后一个值headers(name)获取所有值- OkHttpClient client = new OkHttpClient();
-
- Request request = new Request.Builder()
- .url("https://github.com")
- .header("User-Agent", "My super agent")
- .addHeader("Accept", "text/html")
- .build();
-
- Response response = client.newCall(request).execute();
- if (!response.isSuccessful()) {
- throw new IOException("服务器端错误: " + response);
- }
-
- System.out.println(response.header("Server"));
- System.out.println(response.headers("Set-Cookie"));
(5)表单提交
- RequestBody formBody = new FormEncodingBuilder()
- .add("query", "Hello")
- .build();
(6)文件上传
使用MultipartBuilder指定MultipartBuilder.FORM类型并通过addPart方法添加不同的Part(每个Part由Header和RequestBody两部分组成),最后调用builde()方法构建一个RequestBody。
- MediaType MEDIA_TYPE_TEXT = MediaType.parse("text/plain");
- RequestBody requestBody = new MultipartBuilder()
- .type(MultipartBuilder.FORM)
- .addPart(
- Headers.of("Content-Disposition", "form-data; name=\"title\""),
- RequestBody.create(null, "测试文档"))
- .addPart(
- Headers.of("Content-Disposition", "form-data; name=\"file\""),
- RequestBody.create(MEDIA_TYPE_TEXT, new File("input.txt")))
- .build();
(7)使用流的方式发送POST请求
- OkHttpClient client = new OkHttpClient();
- final MediaType MEDIA_TYPE_TEXT = MediaType.parse("text/plain");
- final String postBody = "Hello World";
-
- RequestBody requestBody = new RequestBody() {
- @Override
- public MediaType contentType() {
- return MEDIA_TYPE_TEXT;
- }
- @Override
- public void writeTo(BufferedSink sink) throws IOException {
- sink.writeUtf8(postBody);
- }
- @Override
- public long contentLength() throws IOException {
- return postBody.length();
- }
- };
-
- Request request = new Request.Builder()
- .url("http://www.baidu.com")
- .post(requestBody)
- .build();
- Response response = client.newCall(request).execute();
- if (!response.isSuccessful()) {
- throw new IOException("服务器端错误: " + response);
- }
- System.out.println(response.body().string());

(8)缓存控制
强制不缓存,关键:noCache()
- Request request = new Request.Builder()
- .cacheControl(new CacheControl.Builder().noCache().build())
- .url("http://publicobject.com/helloworld.txt")
- .build();
缓存策略由服务器指定,关键:maxAge(0, TimeUnit.SECONDS)
- Request request = new Request.Builder()
- .cacheControl(new CacheControl.Builder()
- .maxAge(0, TimeUnit.SECONDS)
- .build())
- .url("http://publicobject.com/helloworld.txt")
- .build();
强制缓存,关键:onlyIfCached()
- Request request = new Request.Builder()
- .cacheControl(new CacheControl.Builder()
- .onlyIfCached()
- .build())
- .url("http://publicobject.com/helloworld.txt")
- .build();
- Response forceCacheResponse = client.newCall(request).execute();
- if (forceCacheResponse.code() != 504) {
- // The resource was cached! Show it.
- } else {
- // The resource was not cached.
- }
允许使用旧的缓存,关键:maxStale(365, TimeUnit.DAYS)
- Request request = new Request.Builder()
- .cacheControl(new CacheControl.Builder()
- .maxStale(365, TimeUnit.DAYS)
- .build())
- .url("http://publicobject.com/helloworld.txt")
- .build();
分析:从错误的线程调用,是因为在主线程中操作UI,这在Android中是不允许的,所以需要切换到主线程中进行UI操作。
解决:参见 (6)异步调用
分析:Cookies由CookieJar统一管理,所以只需要对CookieJar进行设置就可以达到目的了。
解决:
- OkHttpClient mHttpClient = new OkHttpClient.Builder().cookieJar(new CookieJar() {
- private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
- //Tip:key是String类型且为url的host部分
- @Override
- public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
- cookieStore.put(url.host(), cookies);
- }
- @Override
- public List<Cookie> loadForRequest(HttpUrl url) {
- List<Cookie> cookies = cookieStore.get(url.host());
- return cookies != null ? cookies : new ArrayList<Cookie>();
- }
- }).build();
方案1:使用PersistentCookieJar
在Project的Build.gradle中添加Maven库
- allprojects {
- repositories {
- ...
- maven { url "https://jitpack.io" }
- }
- }
在引入依赖库
compile 'com.github.franmontiel:PersistentCookieJar:v0.9.3'
创建并使用PersistentCookieJar
- ClearableCookieJar cookieJar =
- new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(context));
-
- OkHttpClient okHttpClient = new OkHttpClient.Builder()
- .cookieJar(cookieJar)
- .build();
方案2:参考android-async-http库写一个
参考两个类,一个是 PersistentCookieStore.java,另一个是 SerializableCookie.java。参见参考文档中的 OkHttp3实现Cookies管理及持久化,里面已经够详细了。
下面这段代码似乎没错,不是说OkHttp会在线程池中访问网络吗?怎么会报这种错误??
- @Override
- protected void onResume() {
- super.onResume();
- Request request = new Request.Builder()
- .url("https://github.com")
- .build();
- okHttpClient.newCall(request).enqueue(new Callback() {
- @Override
- public void onFailure(Call call, IOException e) {
- }
-
- @Override
- public void onResponse(Call call, final Response response) throws IOException {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- String string = response.body().string(); //注意
- helloTxt.setText(string);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- });
- }
- });
- }

解决:在标注的那一行 response.body().string(),是在主线程中运行的,从响应中获取响应体属于网络操作,所以报错。解决方法是将这一行移到 runOnUiThread 方法前面。
转载自:https://www.jianshu.com/p/457d3ab27584
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。