注:这里以注册为例概述网络请求的完整流程,是指以我的方式、流程进行的请求,不同的开发者有不同的思路和流程
一.引入包(如果有哪些找不到文件的,那就是缺包了)、网络权限
api com.squareup.retrofit2:adapter-rxjava:2.5.0
api com.squareup.retrofit2:converter-gson:2.5.0
api io.reactivex.rxjava2:rxjava:2.0.1
api android.arch.lifecycle:viewmodel:1.1.1
api android.arch.lifecycle:livedata:1.1.1
<uses-permission android:name="android.permission.INTERNET"/>
二.注册布局、RegisterActivity准备好,其中需要如下代码:
public RegisterViewModel registerViewModel;//申明
registerViewModel = ViewModelProviders.of(this).get(RegisterViewModel.class);//在onCreate方法
registerViewModel.register(****);//点击注册按钮后校验输入符合要求后调用
/*接收网络请求后的结果
register、0、1、2建议建一个常量类,这样子方便对应(效果图中有)
要对应RegisterViewModel 中返回的
0、2若是没有特殊处理可以合并,因只处理成功的逻辑,其它全部当失败Toast显示
*/
private void initEven() {
LiveDataBus.get().with("register", MessageModel.class).observe(this, messageModel -> {
switch (messageModel.what) {
//网络错误
case 0:
//处理逻辑
break;
//成功
case 1:
//获取registerViewModel 传来的数据,类型跟传的一致
RegisterResponse response= (RegisterResponse) messageModel.obj;
break;
//失败
case 2:
//处理逻辑
break;
}
});
}
三.RegisterViewModel用到的一层一层写下去
3.1 RegisterViewModel继承ViewModel,RegisterResponse是后台返回成功时的模型
public class RegisterViewModel extends ViewModel {
private WebRepository webRepository;
//register、0、1、2 activity接收返回的数据要对应这里的
public void register(***) {
webRepository = new WebRepository(application);
Map<String, String> registerMap = new HashMap<>();
registerMap.put("***", ***); //把参数全部加入到registerMap
//后面调用的方法都要跟BaseCallBean<RegisterResponse>一样
Observable<BaseCallBean<RegisterResponse>> observable = webRepository.register(registerMap);
observable.subscribeOn(Schedulers.io())//请求数据的事件发生在io线程
.observeOn(AndroidSchedulers.mainThread())//请求完成后在主线程更显UI
.subscribe(new Observer<BaseCallBean<RegisterResponse>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
sendLiveDataBus(0,e.toString);
}
@Override
public void onNext(BaseCallBean<RegisterResponse> register) {
if (10000 ==register.getStatus() && null != register.getData()) {//成功,跟后台约定好,返回10000是成功
sendLiveDataBus(1,register.getData())
} else {//请求异常的:用户已存在、验证失败……
sendLiveDataBus(2,register.getMsg())
}
}
});
}
private void sendLiveDataBus(int what, Object data) {
new Handler(application.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// 在这里执行你要想的操作 比如直接在这里更新ui或者调用回调在 在回调中更新ui
LiveDataBus.get().with("register").postValue(new MessageModel(what, data));
}
});
}
}
public class BaseCallBean<T> {
public int status;
public String msg;
public T data;
@Override
public String toString() {
return "BaseCallBean{" +
"status=" + status +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
3.2 WebRepository数据代理
public class WebRepository {
private final Webservice webservice;//服务器
private BaseHeader header;//封装的请求头
public WebRepository(final Application application) {
BaseHeader header = Header();
this.header = header;
String baseUrl = ****;//基础的url,会拼接到后面的注解url形成完整的url进行网络请求
webservice = new Webservice(baseURL, header);
}
//BaseCallBean<RegisterResponse>跟调用register方法的一样
public Observable<BaseCallBean<RegisterResponse>> register(Map<String, String> registerMap) {
return webservice.register(registerMap);
}
public BaseHeader Header() {//赋值请求头,每次请求都要赋值,因登录前后的请求头不一样
BaseHeader baseHeader = new BaseHeader();
baseHeader.setUserId(***);
return baseHeader;
}
}
3.3.1 Webservice服务器
public class Webservice {
private RetrofitHelper helper;
public Webservice(String baseURL,BaseHeader header) {
helper = RetrofitHelper.getInstance(baseURL,header);
}
public Observable<BaseCallBean<RegisterResponse>> register(Map<String,String> registerMap) {
return helper.getServer().register(registerMap);
}
}
3.3.2 BaseHeader请求头(是一个类)
public class BaseHeader {
private String ***;
public String get***() {
return ***;
}
public void set***(String ***) {
***= ***;
}
public Map<String, String> toMap() {//这个方法是Retrofit获取请求头时调用的
Map<String, String> headers = new HashMap<>();
if (null != get***()) {
headers.put("***", get***());
}
return headers;
}
}
3.3.4 RetrofitHelper
public class RetrofitHelper {
public static RetrofitHelper getInstance(String baseURL, BaseHeader header) {
return new RetrofitHelper(baseURL, header);
}
private RetrofitHelper(String baseURL, BaseHeader header) {
this.baseURL = baseURL;
this.header = header;
init();
}
private void init() {
//OkHttpClient.Builder创建OkHttpClient,用它来对OkHttpClient进行一些设置
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
//不设置一些改变的属性,则会默认,eg:连接超时
OkHttpClient client = clientBuilder
.connectTimeout(10, TimeUnit.SECONDS)//连接超时设置
.readTimeout(20, TimeUnit.SECONDS)//读超时设置
.writeTimeout(20, TimeUnit.SECONDS)//写超时设置
.addInterceptor(chain -> {//自定义拦截器,为请求添加统一的请求头……
Request.Builder builder = chain.request().newBuilder();
if (null != header) {
Map<String, String> headersMap = header.toMap();//将请求头数据写入
for (Map.Entry<String, String> entry : headersMap.entrySet()) {
if (null != entry.getValue()) {
builder.addHeader(entry.getKey(), entry.getValue());//将数据添加到请求头中
}
}
}
Request request = builder.build();
return chain.proceed(request);
})
.build();//构建client
mRetrofit = new Retrofit.Builder()
.baseUrl(baseURL)//请求的基础Url,后面会拼接上注解的url形成完整的URL
.client(client)//设置自定义的OkHttpClient
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))//支持Gson解析
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//支持RxJava
.build();
}
public UserService getServer() {
return mRetrofit.create(UserService.class);//创建 网络请求接口 的实例
}
3.3.5服务UserService
- 方法注解:@GET、@POST、@PUT、@DELETE、@PATH、@HEAD、@OPTIONS、@HTTP
- 标记注解:@FormUrlEncoded、@Multipart、@Streaming
- 参数注解:@Query、@QueryMap、@Body、@Field、@FieldMap、@Part、@PartMap、@Path、@Header、@Headers、@Url
public interface UserService {
@FormUrlEncoded //API规定采用请求格式x-www-form-urlencoded,即表单形式
@POST("api/AppLogin/Register")
//WebRepository中的baseUrl拼接上api/AppLogin/Register形成完整的请求url,
//记得baseUrl以/结束(因前面的没有以/开始)
//@FieldMap向后台提交字段,所有的字段都在里面;后台返回的要符合json格式,不然会报转换失败错误
Observable<BaseCallBean<RegisterResponse>> register(@FieldMap Map<String,String> registerMap);
}
3.4 LiveDataBus
public final class LiveDataBus {
private final Map<String, BusMutableLiveData<Object>> bus;
private LiveDataBus() {
bus = new HashMap<>();
}
private static class SingletonHolder {
private static final LiveDataBus DEFAULT_BUS = new LiveDataBus();
}
public static LiveDataBus get() {
return SingletonHolder.DEFAULT_BUS;
}
public synchronized <T> Observable<T> with(String key, Class<T> type) {
if (!bus.containsKey(key)) {
bus.put(key, new BusMutableLiveData<>());
}
return (Observable<T>) bus.get(key);
}
public Observable<Object> with(String key) {
return with(key, Object.class);
}
public interface Observable<T> {
void setValue(T value);
void postValue(T value);
void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer);
}
private static class BusMutableLiveData<T> extends MutableLiveData<T> implements Observable<T> {
private class PostValueTask implements Runnable {
private Object newValue;
public PostValueTask(@NonNull Object newValue) {
this.newValue = newValue;
}
@Override
public void run() {
setValue((T) newValue);
}
}
private Map<Observer, Observer> observerMap = new HashMap<>();
private Handler mainHandler = new Handler(Looper.getMainLooper());
@Override
public void postValue(T value) {
mainHandler.post(new PostValueTask(value));
}
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
super.observe(owner, observer);
try {
hook(observer);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void observeForever(@NonNull Observer<T> observer) {
if (!observerMap.containsKey(observer)) {
observerMap.put(observer, new ObserverWrapper(observer));
}
super.observeForever(observerMap.get(observer));
}
@Override
public void removeObserver(@NonNull Observer<T> observer) {
Observer realObserver = null;
if (observerMap.containsKey(observer)) {
realObserver = observerMap.remove(observer);
} else {
realObserver = observer;
}
super.removeObserver(realObserver);
}
private void hook(@NonNull Observer<T> observer) throws Exception {
//get wrapper's version
Class<LiveData> classLiveData = LiveData.class;
Field fieldObservers = classLiveData.getDeclaredField("mObservers");
fieldObservers.setAccessible(true);
Object objectObservers = fieldObservers.get(this);
Class<?> classObservers = objectObservers.getClass();
Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
methodGet.setAccessible(true);
Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
Object objectWrapper = null;
if (objectWrapperEntry instanceof Map.Entry) {
objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
}
if (objectWrapper == null) {
throw new NullPointerException("Wrapper can not be bull!");
}
Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();
Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
fieldLastVersion.setAccessible(true);
//get livedata's version
Field fieldVersion = classLiveData.getDeclaredField("mVersion");
fieldVersion.setAccessible(true);
Object objectVersion = fieldVersion.get(this);
//set wrapper's version
fieldLastVersion.set(objectWrapper, objectVersion);
}
}
private static class ObserverWrapper<T> implements Observer<T> {
private Observer<T> observer;
public ObserverWrapper(Observer<T> observer) {
this.observer = observer;
}
@Override
public void onChanged(@Nullable T t) {
if (observer != null) {
if (isCallOnObserve()) {
return;
}
observer.onChanged(t);
}
}
private boolean isCallOnObserve() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
if (stackTrace != null && stackTrace.length > 0) {
for (StackTraceElement element : stackTrace) {
if ("android.arch.lifecycle.LiveData".equals(element.getClassName()) &&
"observeForever".equals(element.getMethodName())) {
return true;
}
}
}
return false;
}
}
}
3.5效果图(前面以注册为例,这里是截图创建密码的,都是一样的)
注:整个项目中不要大量使用 LiveDataBus,不然会产生很多的常量声明(是项目逐渐变大后发现的,每一对传送、接收(eg:register的viewmodel、activity中的要一致,login中要另外声明不一样的……如下图)),如果可以用回调的方式接收数据的就用回调的方式。