一、工程项目

包名解释:
base:基类库。
BaseDto类为服务器返回公共实体;BaseHttpSubscriber类自定义请求服务器被观察者;BaseRepository类请求网络数据基类。
exception:异常类模块。
ApiException类前端自定义Exception;ServerException类服务器返回的Exception;ExceptionEngine类拦截各种异常处理。
https:Retrofit+OkHttp封装网络请求模块。
ApiService接口API的Retrofit注解;RequetRetrofit类的网络请求用到的Retrofit+OkHttp。
interceptor:自定义网络请求拦截器。
model:数据模型层。定义实体类。
repository:数据仓库。包括网络数据获取,sqlite小型数据库,文件File,SharedPreferences数据存储。
view:视图层。主要包含Activity+Fragment实体类。
viewmodel:VM视图模型层。
二、引用类库
implementation 'com.alibaba:fastjson:1.1.70.android' implementation 'com.google.code.gson:gson:2.8.4' implementation 'com.squareup.okhttp3:okhttp:3.11.0' //Rxlifecycle implementation 'com.trello:rxlifecycle:0.3.1' implementation 'com.trello:rxlifecycle-components:0.3.1' implementation 'com.github.bumptech.glide:glide:4.8.0' implementation 'com.squareup.retrofit2:converter-gson:2.+' //必须使用 implementation 'com.lzy.net:okgo:3.0.4' implementation 'com.squareup.okio:okio:1.5.0' implementation 'io.reactivex.rxjava2:rxjava:2.2.2' implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.+' implementation 'com.squareup.retrofit2:retrofit:2.+' implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' implementation 'com.squareup.retrofit2:converter-gson:2.+' implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' implementation 'com.facebook.stetho:stetho:1.4.2' implementation 'com.facebook.stetho:stetho-okhttp3:1.4.2' // 依赖以下两个库,会自动引用基础库与Android库 implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.1.0' implementation 'com.trello.rxlifecycle2:rxlifecycle-navi:2.1.0' implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.7.0@aar' implementation 'com.jakewharton.rxbinding:rxbinding:1.+' // Lifecycles, LiveData 和 ViewModel implementation "android.arch.lifecycle:extensions:1.1.0" // 对 RxJava 的支持 implementation "android.arch.persistence.room:rxjava2:1.0.0-alpha5"
三、网络请求封装
1、BaseDto类
package com.ylink.frameworkdemo.base;
import java.io.Serializable;
/**
* 服务器返回公共实体
*
* @param <T>
* @author twilight
* @Time 2019-07-21
*/
public class BaseDto<T> implements Serializable {
private String statusCode;
private String statusDesc;
private T data;
public String getStatusCode() {
return statusCode;
}
public void setStatusCode(String statusCode) {
this.statusCode = statusCode;
}
public String getStatusDesc() {
return statusDesc;
}
public void setStatusDesc(String statusDesc) {
this.statusDesc = statusDesc;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
2、BaseHttpSubscriber类
package com.ylink.frameworkdemo.base;
import androidx.lifecycle.MutableLiveData;
import com.ylink.frameworkdemo.Constant;
import com.ylink.frameworkdemo.exception.ApiException;
import com.ylink.frameworkdemo.exception.ExceptionEngine;
import com.ylink.frameworkdemo.exception.ServerException;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
/**
* 自定义请服务器被观察者
*
* @author twilight
* @Time 2019-07-21
*/
public class BaseHttpSubscriber<T> implements Subscriber<BaseDto<T>> {
//异常类
private ApiException ex;
public BaseHttpSubscriber() {
data = new MutableLiveData();
}
private MutableLiveData<BaseDto<T>> data;
public MutableLiveData<BaseDto<T>> get() {
return data;
}
public void set(BaseDto<T> t) {
this.data.setValue(t);
}
public void onFinish(BaseDto<T> t) {
set(t);
}
@Override
public void onSubscribe(Subscription s) {
// 观察者接收事件 = 1个
s.request(1);
}
@Override
public void onNext(BaseDto<T> t) {
if (t.getStatusCode().equals(Constant.RespCode.R000)) {
onFinish(t);
} else{
ex = ExceptionEngine.handleException(new ServerException(t.getStatusCode(), t.getStatusDesc()));
getErrorDto(ex);
}
}
@Override
public void onError(Throwable t) {
ex = ExceptionEngine.handleException(t);
getErrorDto(ex);
}
/**
* 初始化错误的dto
*
* @param ex
*/
private void getErrorDto(ApiException ex) {
BaseDto dto = new BaseDto();
dto.setStatusCode(ex.getStatusCode());
dto.setStatusDesc(ex.getStatusDesc());
onFinish((BaseDto<T>) dto);
}
@Override
public void onComplete() {
}
}
3、BaseRepository类
package com.ylink.frameworkdemo.base;
import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
/**
* Repository基类
*
* @author twilight
* @Time 2019-07-21
*/
public class BaseRepository {
/**
* 请求网络
* @param flowable
* @param <T>
* @return
*/
public <T> BaseHttpSubscriber<T> request(Flowable<BaseDto<T>> flowable){
BaseHttpSubscriber<T> baseHttpSubscriber = new BaseHttpSubscriber<>(); //RxJava Subscriber回调
flowable.subscribeOn(Schedulers.io()) //解决背压
.observeOn(AndroidSchedulers.mainThread())
.subscribe(baseHttpSubscriber);
return baseHttpSubscriber;
}
}
4、ApiException类
package com.ylink.frameworkdemo.exception;
/**
* 前端自定义Exception
*/
public class ApiException extends Exception {
private String statusCode;//错误码
private String statusDesc;//错误信息
public ApiException(Throwable throwable, String statusCode) {
super(throwable);
this.statusCode = statusCode;
}
public ApiException(String statusCode, String statusDesc) {
this.statusCode = statusCode;
this.statusDesc = statusDesc;
}
public String getStatusCode() {
return statusCode;
}
public void setStatusCode(String statusCode) {
this.statusCode = statusCode;
}
public String getStatusDesc() {
return statusDesc;
}
public void setStatusDesc(String statusDesc) {
this.statusDesc = statusDesc;
}
}5、ServerException类
package com.ylink.frameworkdemo.exception;
/**
* 服务器返回的Exception
*/
public class ServerException extends RuntimeException {
private String statusCode;//错误码
private String statusDesc;//错误信息
public ServerException(String statusCode, String statusDesc) {
this.statusCode = statusCode;
this.statusDesc = statusDesc;
}
public String getStatusCode() {
return statusCode;
}
public void setStatusCode(String statusCode) {
this.statusCode = statusCode;
}
public String getStatusDesc() {
return statusDesc;
}
public void setStatusDesc(String statusDesc) {
this.statusDesc = statusDesc;
}
}6、ExceptionEngine类
package com.ylink.frameworkdemo.exception;
import android.net.ParseException;
import android.util.MalformedJsonException;
import com.google.gson.JsonParseException;
import org.json.JSONException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import retrofit2.HttpException;
public class ExceptionEngine {
//客户端报错
public static final int UN_KNOWN_ERROR = 9000;//未知错误
public static final int ANALYTIC_SERVER_DATA_ERROR = 9001;//解析(服务器)数据错误
public static final int ANALYTIC_CLIENT_DATA_ERROR = 9002;//解析(客户端)数据错误
public static final int CONNECT_ERROR = 9003;//网络连接错误
public static final int TIME_OUT_ERROR = 9004;//网络连接超时
public static final int UNKNOWNHOSTEXCEPTION = 9005;//网络连接超时
public static ApiException handleException(Throwable e) {
ApiException ex;
if (e instanceof HttpException) { //HTTP错误
HttpException httpExc = (HttpException) e;
ex = new ApiException(e, String.valueOf(httpExc.code()));
ex.setStatusDesc("网络错误,请稍后再试"); //均视为网络错误
return ex;
} else if (e instanceof ServerException) { //服务器返回的错误
ServerException serverExc = (ServerException) e;
ex = new ApiException(serverExc, serverExc.getStatusCode());
ex.setStatusDesc(serverExc.getStatusDesc());
return ex;
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException || e instanceof MalformedJsonException) { //解析数据错误
ex = new ApiException(e, String.valueOf(ANALYTIC_SERVER_DATA_ERROR));
ex.setStatusDesc("客户端异常,请稍后再试");
return ex;
} else if (e instanceof ConnectException) {//连接网络错误
ex = new ApiException(e, String.valueOf(CONNECT_ERROR));
ex.setStatusDesc("网络连接错误,请稍后再试");
return ex;
} else if (e instanceof SocketTimeoutException) {//网络超时
ex = new ApiException(e, String.valueOf(TIME_OUT_ERROR));
ex.setStatusDesc("网络连接超时,请稍后再试");
return ex;
}
else if (e instanceof UnknownHostException) {//网络异常
ex = new ApiException(e, String.valueOf(UNKNOWNHOSTEXCEPTION));
ex.setStatusDesc("网络异常,请检查您的网络连接");
return ex;
}
else { //未知错误
ex = new ApiException(e, String.valueOf(UN_KNOWN_ERROR));
ex.setStatusDesc("系统异常,请稍后再试");
return ex;
}
}
}7、ApiService接口
package com.ylink.frameworkdemo.https;
import com.ylink.frameworkdemo.Constant;
import com.ylink.frameworkdemo.base.BaseDto;
import com.ylink.frameworkdemo.model.dto.LoginDto;
import com.ylink.frameworkdemo.model.vo.LoginVo;
import io.reactivex.Flowable;
import retrofit2.http.Body;
import retrofit2.http.POST;
/**
* api接口
*
* @author twilight
* @Time 2019-07-21
*
* retrofit的注解学习https://blog.csdn.net/qiang_xi/article/details/53959437
*/
public interface ApiService {
/**
* 登录
* @param loginVo
* @return
*/
@POST(Constant.Server.LOGIN)
Flowable<BaseDto<LoginDto>> login(@Body LoginVo loginVo);
}
8、RequestRetrofit类
package com.ylink.frameworkdemo.https;
import android.util.Log;
import com.facebook.stetho.okhttp3.StethoInterceptor;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.ylink.frameworkdemo.Constant;
import com.ylink.frameworkdemo.interceptor.AddCookiesInterceptor;
import com.ylink.frameworkdemo.interceptor.ReceivedCookiesInterceptor;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* 普通的网络请求用到的Retrofit
*/
public class RequetRetrofit {
private static final String TAG = "RequetRetrofit";
/**
* 创建okhttp相关对象
*/
private static OkHttpClient okHttpClient;
/**
* 创建Retrofit相关对象
*/
private static Retrofit retrofit;
public static <T> T getInstance(final Class<T> service) {
if (okHttpClient == null) {
synchronized (RequetRetrofit.class) {
if(okHttpClient == null) {
/**
* 创建okhttp相关对象
*/
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) { //访问网络请求,和服务端响应请求时。将数据拦截并输出
Log.d(TAG, "log: " + message);
}
}).setLevel(HttpLoggingInterceptor.Level.BODY)) //Log等级
.connectTimeout(Constant.Server.TIME_OUT, TimeUnit.SECONDS) //超时时间
.readTimeout(Constant.Server.TIME_OUT, TimeUnit.SECONDS)
.writeTimeout(Constant.Server.TIME_OUT, TimeUnit.SECONDS)
.addNetworkInterceptor(new StethoInterceptor())
.addInterceptor(new AddCookiesInterceptor()) //
.addInterceptor(new ReceivedCookiesInterceptor())
.build();
}
}
}
if (retrofit == null) {
synchronized (RequetRetrofit.class) {
if(retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(Constant.Server.ROOT_URL) //BaseUrl
.client(okHttpClient) //请求的网络框架
.addConverterFactory(GsonConverterFactory.create()) //解析数据格式
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 使用RxJava作为回调适配器
.build();
}
}
}
return retrofit.create(service);
}
}
9、AddCookiesInterceptor类
package com.ylink.frameworkdemo.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.ylink.frameworkdemo.Constant;
import com.ylink.frameworkdemo.utils.SPUtil;
import java.io.IOException;
import java.util.List;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* 自定义拦截器刷新sessionId 非首次请求的处理
* @author twilight
* @Time 2019-07-21
*/
public class AddCookiesInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
String cookieStr = SPUtil.getData(Constant.SP.SP, Constant.SP.SESSION_ID, String.class, null);
List<String> cookies = JSONObject.parseArray(cookieStr, String.class);
if (cookies != null) {
for (String cookie : cookies) {
builder.addHeader("Cookie", cookie);
}
}
return chain.proceed(builder.build());
}
}
10、ReceivedCookiesInterceptor类
package com.ylink.frameworkdemo.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.ylink.frameworkdemo.Constant;
import com.ylink.frameworkdemo.utils.SPUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.Interceptor;
import okhttp3.Response;
/**
* 自定义拦截器刷新sessionId 首次请求的处理
* @author twilight
* @Time 2019-07-21
*/
public class ReceivedCookiesInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
List<String> cookies = new ArrayList<>();
for (String header : originalResponse.headers("Set-Cookie")) {
cookies.add(header);
}
String cookieStr = JSONObject.toJSONString(cookies);
SPUtil.putData(Constant.SP.SP, Constant.SP.SESSION_ID, cookieStr);
}
return originalResponse;
}
}
四、创建model层
1、登录Vo
package com.ylink.frameworkdemo.model.vo;
import java.io.Serializable;
/**
* 登陆vo
*
* @author twilight
* @Time 2020-01-14
*/
public class LoginVo implements Serializable {
private String password;
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
2、登录Dto
package com.ylink.frameworkdemo.model.dto;
import java.io.Serializable;
/**
* 登陆返回实体
*
* @author twilight
* @Time 2020-01-14
*/
public class LoginDto implements Serializable {
private String id;
private String userName;
private String userAccount;
private String sex;
private String age;
private String telephone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserAccount() {
return userAccount;
}
public void setUserAccount(String userAccount) {
this.userAccount = userAccount;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
}
五、创建Repository
1、ILoginRepository接口类
package com.ylink.frameworkdemo.repository.network.impl;
import androidx.lifecycle.LiveData;
import com.ylink.frameworkdemo.base.BaseDto;
import com.ylink.frameworkdemo.model.dto.LoginDto;
import com.ylink.frameworkdemo.model.vo.LoginVo;
/**
* 登录
*/
public interface ILoginRepository {
/**
* 登录
* @param loginVo
* @return
*/
LiveData<BaseDto<LoginDto>> login(LoginVo loginVo);
}
2、LoginRepository接口实现类
package com.ylink.frameworkdemo.repository.network;
import androidx.lifecycle.LiveData;
import com.ylink.frameworkdemo.base.BaseDto;
import com.ylink.frameworkdemo.base.BaseRepository;
import com.ylink.frameworkdemo.https.ApiService;
import com.ylink.frameworkdemo.https.RequetRetrofit;
import com.ylink.frameworkdemo.model.dto.LoginDto;
import com.ylink.frameworkdemo.model.vo.LoginVo;
import com.ylink.frameworkdemo.repository.network.impl.ILoginRepository;
/**
* 登录
*/
public class LoginRepository extends BaseRepository implements ILoginRepository {
/**
* 登录
* @param loginVo
* @return
*/
@Override
public LiveData<BaseDto<LoginDto>> login(LoginVo loginVo) {
return request(RequetRetrofit.getInstance(ApiService.class).login(loginVo)).get();
}
}
六、创建View层
1、创建activity_login.xml文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".view.login.LoginActivity">
<EditText
android:id="@+id/username"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="96dp"
android:layout_marginEnd="24dp"
android:hint="用户名"
android:inputType="textEmailAddress"
android:selectAllOnFocus="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/password"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:hint="密码"
android:imeOptions="actionDone"
android:inputType="textPassword"
android:selectAllOnFocus="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/username" />
<Button
android:id="@+id/login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginStart="48dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="48dp"
android:layout_marginBottom="64dp"
android:enabled="true"
android:text="登录"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/password"
app:layout_constraintVertical_bias="0.2" />
</androidx.constraintlayout.widget.ConstraintLayout>2、创建LoginActivity类
package com.ylink.frameworkdemo.view.login;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import com.ylink.frameworkdemo.Constant;
import com.ylink.frameworkdemo.R;
import com.ylink.frameworkdemo.base.BaseDto;
import com.ylink.frameworkdemo.model.dto.LoginDto;
import com.ylink.frameworkdemo.model.vo.LoginVo;
import com.ylink.frameworkdemo.viewmodel.login.LoginViewmodel;
public class LoginActivity extends AppCompatActivity {
EditText usernameEditText;
EditText passwordEditText;
Button loginButton;
private LoginViewmodel viewmodel;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
viewmodel = ViewModelProviders.of(this).get(LoginViewmodel.class);
usernameEditText = findViewById(R.id.username);
passwordEditText = findViewById(R.id.password);
loginButton = findViewById(R.id.login);
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
}
});
}
/**
* 登录
*/
private void login(){
String username = usernameEditText.getText().toString().trim();
String password = passwordEditText.getText().toString().trim();
LoginVo loginVo = new LoginVo();
loginVo.setUsername(username);
loginVo.setPassword(password);
viewmodel.login(loginVo).observe(this, new Observer<BaseDto<LoginDto>>() {
@Override
public void onChanged(@Nullable BaseDto<LoginDto> loginDtoBaseDto) {
if(loginDtoBaseDto.getStatusCode().equals(Constant.RespCode.R000)){
Toast.makeText(LoginActivity.this,"登录成功",Toast.LENGTH_LONG).show();
}else{
Toast.makeText(LoginActivity.this,loginDtoBaseDto.getStatusDesc(),Toast.LENGTH_LONG).show();
}
}
});
}
}
七、创建ViewModel层
package com.ylink.frameworkdemo.viewmodel.login;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.ylink.frameworkdemo.base.BaseDto;
import com.ylink.frameworkdemo.model.dto.LoginDto;
import com.ylink.frameworkdemo.model.vo.LoginVo;
import com.ylink.frameworkdemo.repository.network.LoginRepository;
import com.ylink.frameworkdemo.repository.network.impl.ILoginRepository;
/**
* 登录页面viewmodel
*/
public class LoginViewmodel extends ViewModel {
public LiveData<BaseDto<LoginDto>> login(LoginVo loginVo){
ILoginRepository loginRepository = new LoginRepository();
return loginRepository.login(loginVo);
}
}
版权声明:本文为longlonghaohao原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。