前言:最近做一个安卓应用,实现安卓端从服务器端获取电影列表的功能,于是自己搭建了asp.net服务端并实现安卓代码,之前就用这种方式实现安卓和服务器通信,但是之前后端使用java编写,通过这个应用才知道,okhttp还能和asp.net的服务端通信。本着分享精神和记录作用,本文分享如何使用okhttp3+retroft2+rxjava实现网络通信功能。本文结合上一篇服务端程序,即可进行本地测试(测试时使用真机,且手机网络和服务端网络在同一局域网),文章地址:使用asp.net core web api创建web后台,并连接和使用Sql Server数据库_Zafir2023的博客-CSDN博客
一、引入okhttp3+retroft2+rxjava程序包
在模块build.gradle的依赖中加入如下依赖:
// 和网络请求相关的几个依赖库 @{
implementation 'com.squareup.okhttp3:okhttp:3.7.0'
implementation 'com.squareup.retrofit2:retrofit:2.0.2'
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
implementation 'com.squareup.okhttp3:logging-interceptor:3.5.0'
implementation 'com.umeng.analytics:analytics:6.1.4'
implementation 'io.reactivex:rxjava:1.1.1'
implementation 'io.reactivex:rxandroid:1.0.1'
// @}
二、增加网络安全配置文件并配置到清单文件
1、新增网络安全配置文件,在res/xml目录增加network_security_config.xml文件,内容如下:
2、在app/main目录AndroidManifest.xml文件中的application标签中添加属性。
android:networkSecurityConfig="@xml/network_security_config"
三、防止在访问服务器时出现SSLHandshakeException
在Activity的onCreate方法中调用如下方法。
// 忽略https的证书校验
public static void handleSSLHandshake() {
try {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
Log.d("TAG", "checkClientTrusted authType:" + authType);
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
Log.d("TAG", "checkServerTrusted authType:" + authType);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
Log.d("TAG", "checkServerTrusted getAcceptedIssuers");
return new X509Certificate[0];
}
}};
SSLContext sc = SSLContext.getInstance("TLS");
// trustAllCerts信任所有的证书
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
} catch (Exception ignored) {
}
}
四、定义网络通信接口和实体类
本文涉及到的实体类是Film电影信息类,此处先定义,切记,接口中的实体类和服务端的返回的实体类变量名和变量类型要一致(可以比服务器多,但是不能更少),这样才能正确接收服务端返回的所有数据。
Film.java
import android.graphics.Bitmap;
// 接收服务器下发的数据使用,服务器端返回的字段必须有,且变量名一样。可以增加一些变量,但是不能少于服务端返回类
public class Film {
public String film_name;
// 影片类型,战争片,爱情片...
public String film_type;
public String film_desc;
// base64格式的图片,接收服务端数据时用
public String film_pic;
// 显示到界面上时用
public Bitmap film_pic_bitmap;
// 影片评分,平均分
public String avg_score;
// 视频位置
public String film_video_url;
// 上架状态,待上架、已上架、已下架
public String film_status;
public String film_up_time;
public String film_down_time;
public String create_time;
public String update_time;
public String create_oper;
public String update_oper;
}
ApiService.java和服务端通信的接口,注意注解的名称和函数参数名、函数返回值类型和服务端程序对应上。
import java.util.List;
// import xxx.Film;
import retrofit2.Call;
import retrofit2.http.POST;
import retrofit2.http.Query;
/**
* Created by zzh
*/
public interface ApiService {
// 和服务端对应上,包括URL,返回值类型,参数名及参数类型
@POST("/FilmList")
Call> getFilmList(/*@Query("filmStatus")int filmStatus 此处可以添加请求参数,参数名和参数类型和服务器对应上即可*/);
}
RestClient.java类,初始化okhttp,设置转化库等,转化库使用retrofit2库中的gson。
// package xxx
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Created by zzh
*/
public class RestClient {
private static RestClient instance = new RestClient();
private ApiService api;
private RestClient() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
/*HttpLoggingInterceptor.Level共包含四个级别:NONE、BASIC、HEADER、BODY
NONE 不记录
BASIC 请求/响应行--> POST /greeting HTTP/1.1 (3-byte body)<-- HTTP/1.1 200 OK (22ms, 6-byte body)
HEADER 请求/响应行 + 头--> Host: example.comContent-Type: plain/textContent-Length: 3<-- HTTP/1.1 200 OK (22ms)Content-Type: plain/textContent-Length: 6
BODY 请求/响应行 + 头 + 体*/
//设置打印数据的级别,要打印所有log,可设置成BODY级别
interceptor.setLevel(HttpLoggingInterceptor.Level.NONE);
OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(30,TimeUnit.SECONDS).connectTimeout(30, TimeUnit.SECONDS).addInterceptor(interceptor).build();
// OkHttpClient okHttpClient = new OkHttpClient();
GsonBuilder builder = new GsonBuilder();
// Register an adapter to manage the date types as long values
builder.registerTypeAdapter(Date.class, new JsonDeserializer() {
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return new Date(json.getAsJsonPrimitive().getAsLong());
}
});
Gson gson = builder.create();
// baseUrl根据服务端IP地址做修改
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.31.197:7049/")
.client(okHttpClient)
.addConverterFactory(new NullOnEmptyConverterFactory())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
api = retrofit.create(ApiService.class);
}
public static ApiService api() {
return instance.api;
}
}
//处理返回为空的情况
class NullOnEmptyConverterFactory extends Converter.Factory {
@Override
public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
final Converter delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
return new Converter() {
@Override
public Object convert(ResponseBody body) throws IOException {
if (body.contentLength() == 0) return null;
return delegate.convert(body); }
};
}
}
五、程序中调用服务端接口获取数据方法
网络请求已经使用rxJava异步方式,无需在子线程中调用
/*** 获取影片信息列表 **/
private void getFilmItemList() {
// 取全部电影
RestClient.api().getFilmList()
.enqueue(new retrofit2.Callback>() {
@Override
public void onResponse(retrofit2.Call> call, retrofit2.Response> response) {
// 返回的数据自动转化成本地实体类数据
List filmItems = response.body();
if (filmItems != null)
Log.d(TAG, "getFilmList filmItems.size()=" + filmItems.size());
if(filmItems != null && filmItems.size() > 0) {
// 获取到的数据处理
}
}
@Override
public void onFailure(retrofit2.Call> call, Throwable t) {
Toast.makeText(getApplication(), "未获取到电影数据,请确认网络是否正常",Toast.LENGTH_SHORT).show();
}
});
}