最近的项目用到了底部弹窗的效果,网上百度了很多类似写好的控件,其中也不乏好的实现。但是为了方便以后扩展,总结了一下前人的经验自己写了实现了一个类:
目前安卓开发者普遍使用AS开发,所以这里只介绍引入依赖的配置方式,同时引入Gson作为后面数据解析的工具。
implementation 'com.squareup.okhttp3:okhttp:3.2.0'
implementation 'com.google.code.gson:gson:2.3.1'
引入库以后,需要在AndroidManifest文件中获取网络访问权限
<uses-permission android:name="android.permission.INTERNET" />
//同步GET方法,execute()方法会抛出IOException异常
private void syncNetAccess() throws IOException {
//创建OkHttpClient实例,主要用于请求网络
OkHttpClient okHttpClient = new OkHttpClient();
//创建Request实例,可以配置接口地址和请求头
Request okRequest = new Request.Builder().url("http://toutiao-ali.juheapi.com/toutiao/index?type=caijing").build();
//GET请求,用Response接受相应结果
Response response = okHttpClient.newCall(okRequest).execute();
//response.body()为请求返回数据 JSON格式
Log.d("+++++++++++++++++++++",response.body().string();
}
这里值得注意的一点,response.body()这个方法很奇怪只能获取一次,再调用只能得到null,所以有打印结果习惯的不要入坑。
另外一个坑就是线程的问题,现在的系统版本不支持在主线程,这样请求的话会报错:NetworkOnMainThreadException。
解决的办法有两种:一是添加StrictMode约束。
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
另外一种就是创建一个新的线程Thread,因为代码比较多,为了不干扰本文的主题在这里就不详说了。
那么,既然同步请求这么坑爹,为什么不直接使用异步请求呢?
//异步GET方法
private void asyncNetAccess() throws IOException {
//创建OkHttpClient实例,主要用于请求网络
OkHttpClient okHttpClient = new OkHttpClient();
//创建Request实例,这里配置了请求头 addHeader()
Request okRequest = new Request.Builder().url("http://toutiao-ali.juheapi.com/toutiao/index?type=caijing").addHeader("name","value").build();
//GET请求,回调函数中获取相应结果
okHttpClient.newCall(okRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("+++++++++++++++++++++",e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("+++++++++++++++++++++",response.body().string());
}
});
}
异步请求初始化的方式差不多,只是在最后请求网络的时候改为enqueue()方法,并且创建了一个Callback来异步处理返回的结果,不在需要Response接收。其中,onFailure和onResponse分别是访问失败和成功的回调。
值得注意的,这里创建Request实例的时候加入了响应头Header,应用场景嘛比如一些公共的接口会需要身份验证,就需要添加响应头。这里是通过addHeader()方法添加key-value值,有点类似Map。
具体使用:
addHeader(“name”,“value”) 。当然响应头可以add多次,并且后面也会讲到批量添加的方法。
POST请求也有异步和同步之分,发送请求的时候与GET请求是一样的,在这里不再介绍赘述。参照异步POST请求代码就知道同步代码是什么样的了。
//异步POST方法
private void asyncPostAccess() throws IOException {
//创建OkHttpClient实例,主要用于请求网络
OkHttpClient okHttpClient = new OkHttpClient();
//创建表单请求体
FormBody.Builder formBody = new FormBody.Builder();
//传递键值对参数
formBody.add("name","shuaige");
//创建Request实例,设置POST参数
Request okRequest = new Request.Builder().url("http://toutiao-ali.juheapi.com/toutiao/index?type=caijing").post(formBody.build()).build();
//POST异步请求,回调函数中获取相应结果
okHttpClient.newCall(okRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("+++++++++++++++++++++",e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("+++++++++++++++++++++",response.body().string());
}
});
}
FormBody.Builder作为请求参数的创建,配置request时候调用post()方法传入请求参数的同时规定了请求方式,就是这么简单~
通过上面的例子我们不难发现,在每次请求网络之前都会创建一个实例:
OkHttpClient okHttpClient = new OkHttpClient();
并且每次网络访问都需要
okHttpClient.newCall() 来实现。
这显然不符合面向对象的的三要素的要求(更不符合懒蛋程序猿的要求)。作为当之无愧的程序猿,我义无反顾的对网络请求做了一个封装,方便以后其他项目上使用。
首先创建一个工具类:OkHttpUtil,这里为了节省一下内存我用了单例模式来封装,毕竟网络请求是APP的核心嘛,用的次数不要太多。
至于单例怎么写本着能省则省态度,以后找一章专门来记录。不多说,下面上代码。
package com.dzh.goldmarket.util;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
public class OkHttpUtil {
private volatile static OkHttpUtil okHttpUtil = null;
private static OkHttpClient okHttpClient = null;
public static OkHttpUtil getInstance(){
if(okHttpUtil == null){
synchronized (OkHttpUtil.class){
if(okHttpUtil == null){
okHttpUtil=new OkHttpUtil();
if(okHttpClient==null)
okHttpClient = new OkHttpClient();
}
}
}
return okHttpUtil;
}
public void doGet(String url, Headers headers, Callback callback){
Request.Builder builder = new Request.Builder().url(url);
//添加响应头集合
if(headers!=null)
builder.headers(headers);
Request okRequest = builder.build();
okHttpClient.newCall(okRequest).enqueue(callback);
}
public void doGet(String url, Callback callback){
this.doGet(url,null,callback);
}
}
这里doGet()实现了一个异步GET请求,其中Headers参数是响应头集合,Callback作为回调函数用法跟之前差不多,下面是具体的调用方法:
private void getNewsList() throws IOException, JSONException {
String url="http://toutiao-ali.juheapi.com/toutiao/index?type=caijing";
//name-value格式,可以多个header, 如("name1","value1","name2","value2")
Headers headers =Headers.of("Authorization","APPCODE vjpawenapasj23sfln3");
Request okRequest = new Request.Builder().url("http://toutiao-ali.juheapi.com/toutiao/index?type=caijing").addHeader("Authorization","APPCODE 545beb7b4c744411988486b946bca3cd").build();
OkHttpUtil.getInstance().doGet(url,headers,new Callback(){
@Override
public void onFailure(Call call, IOException e) {
Log.d("+++++++++++++++++++++",e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String bodyStr = response.body().string();
//Gson解析返回结果
newsEntities = new Gson().fromJson(bodyStr, NewsResultEntity.class);
//多线程,需要在UI线程中更新APP界面
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
newListView.setAdapter(new NewsAdapter(newsEntities.getResult().getData(),getActivity()));
}
});
}
});
}
需要注意一点:异步网络请求是多线程调用的,一般网络请求伴随的就是更新UI界面,所以需要调用runOnUiThread()方法在主线程中更新APP界面。
OK,使用方法和封装方法到此结束,开发中遇到的坑也一一列举。另外网络请求的一些配置,如超时、缓存等,以后有需要再慢慢补充。
(督促自己养成编写项目笔记的习惯,留作日后复习使用)