public interface RequestCallback {
void onSuccess(T t);
}
public interface RequestMultiplyCallback extends RequestCallback {
void onFail(BaseException e);
}
复制代码
此外,为了在网络请求成功但业务逻辑请求失败时(例如,请求参数缺失、Token失效等),可以抛出详细的失败信息,需要自定义 BaseException
public class BaseException extends RuntimeException {
private int errorCode = HttpCode.CODE_UNKNOWN;
public BaseException() {
}
public BaseException(int errorCode, String errorMessage) {
super(errorMessage);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
}
复制代码
实现具体的异常类
public class ParamterInvalidException extends BaseException {
public ParamterInvalidException() {
super(HttpCode.CODE_PARAMETER_INVALID, “参数有误”);
}
}
public class TokenInvalidException extends BaseException {
public TokenInvalidException() {
super(HttpCode.CODE_TOKEN_INVALID, “Token失效”);
}
}
···
复制代码
为了提升性能,Retrofit 一般是设计成单例模式。为了应对应用中 BaseUrl 可能有多个的情况(本文提供的Demo就是如此),此处使用 Map 来存储多个 Retrofit 实例
public class RetrofitManagement {
private static final long READ_TIMEOUT = 6000;
private static final long WRITE_TIMEOUT = 6000;
private static final long CONNECT_TIMEOUT = 6000;
private final Map<String, Object> serviceMap = new ConcurrentHashMap<>();
private RetrofitManagement() {
}
public static RetrofitManagement getInstance() {
return RetrofitHolder.retrofitManagement;
}
private static class RetrofitHolder {
private static final RetrofitManagement retrofitManagement = new RetrofitManagement();
}
private Retrofit createRetrofit(String url) {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.readTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.MILLISECONDS)
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
.addInterceptor(new HttpInterceptor())
.addInterceptor(new HeaderInterceptor())
.addInterceptor(new FilterInterceptor())
.retryOnConnectionFailure(true);
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(httpLoggingInterceptor);
builder.addInterceptor(new ChuckInterceptor(ContextHolder.getContext()));
}
OkHttpClient client = builder.build();
return new Retrofit.Builder()
.client(client)
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
ObservableTransformer<BaseResponseBody, T> applySchedulers() {
return observable -> observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(result -> {
switch (result.getCode()) {
case HttpCode.CODE_SUCCESS: {
return createData(result.getData());
}
case HttpCode.CODE_TOKEN_INVALID: {
throw new TokenInvalidException();
}
case HttpCode.CODE_ACCOUNT_INVALID: {
throw new AccountInvalidException();
}
default: {
throw new ServerResultException(result.getCode(), result.getMsg());
}
}
});
}
private Observable createData(T t) {
return Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) {
try {
emitter.onNext(t);
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}
});
}
T getService(Class clz) {
return getService(clz, HttpConfig.BASE_URL_WEATHER);
}
T getService(Class clz, String host) {
T value;
if (serviceMap.containsKey(host)) {
Object obj = serviceMap.get(host);
if (obj == null) {
value = createRetrofit(host).create(clz);
serviceMap.put(host, value);
} else {
value = (T) obj;
}
} else {
value = createRetrofit(host).create(clz);
serviceMap.put(host, value);
}
return value;
}
}
复制代码
此外还需要一个自定义的 Observer 来对数据请求结果进行自定义回调
public class BaseSubscriber extends DisposableObserver {
private BaseViewModel baseViewModel;
private RequestCallback requestCallback;
public BaseSubscriber(BaseViewModel baseViewModel) {
this.baseViewModel = baseViewModel;
}
BaseSubscriber(BaseViewModel baseViewModel, RequestCallback requestCallback) {
this.baseViewModel = baseViewModel;
this.requestCallback = requestCallback;
}
@Override
public void onNext(T t) {
if (requestCallback != null) {
requestCallback.onSuccess(t);
}
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
if (requestCallback instanceof RequestMultiplyCallback) {
RequestMultiplyCallback callback = (RequestMultiplyCallback) requestCallback;
if (e instanceof BaseException) {
callback.onFail((BaseException) e);
} else {
callback.onFail(new BaseException(HttpCode.CODE_UNKNOWN, e.getMessage()));
}
} else {
if (baseViewModel == null) {
Toast.makeText(ContextHolder.getContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
} else {
baseViewModel.showToast(e.getMessage());
}
}
}
@Override
public void onComplete() {
}
}
复制代码
四、BaseRemoteDataSource 与 BaseRepo
上文所介绍的 RequestCallback、RetrofitManagement 与 BaseSubscriber 还是一个个单独的个体,还需要一个链接器来将之串起来,这个链接器的实现类即 BaseRemoteDataSource
在这里,对 BaseRemoteDataSource 的定位是将之当成一个接口实现者,即在 RemoteDataSource 中实际调用各个请求接口,并通过 RxJava 来控制 loading 弹出以及销毁的时机
一般而言,BaseRemoteDataSource 的实现类中声明的是具有相关逻辑的接口。例如,对于登录模块,可声明一个 LoginDataSource,对于设置模块,可以声明一个 SettingsDataSource
public abstract class BaseRemoteDataSource {
private CompositeDisposable compositeDisposable;
private BaseViewModel baseViewModel;
public BaseRemoteDataSource(BaseViewModel baseViewModel) {
this.compositeDisposable = new CompositeDisposable();
this.baseViewModel = baseViewModel;
}
protected T getService(Class clz) {
return RetrofitManagement.getInstance().getService(clz);
}
protected T getService(Class clz, String host) {
return RetrofitManagement.getInstance().getService(clz, host);
}
private ObservableTransformer<BaseResponseBody, T> applySchedulers() {
return RetrofitManagement.getInstance().applySchedulers();
}
protected void execute(Observable observable, RequestCallback callback) {
execute(observable, new BaseSubscriber<>(baseViewModel, callback), true);
}
protected void execute(Observable observable, RequestMultiplyCallback callback) {
execute(observable, new BaseSubscriber<>(baseViewModel, callback), true);
}
public void executeWithoutDismiss(Observable observable, Observer observer) {
execute(observable, observer, false);
}
private void execute(Observable observable, Observer observer, boolean isDismiss) {
Disposable disposable = (Disposable) observable
.throttleFirst(500, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(applySchedulers())
.compose(isDismiss ? loadingTransformer() : loadingTransformerWithoutDismiss())
.subscribeWith(observer);
addDisposable(disposable);
}
private void addDisposable(Disposable disposable) {
compositeDisposable.add(disposable);
}
public void dispose() {
if (!compositeDisposable.isDisposed()) {
compositeDisposable.dispose();
}
}
private void startLoading() {
if (baseViewModel != null) {
baseViewModel.startLoading();
}
}
private void dismissLoading() {
if (baseViewModel != null) {
baseViewModel.dismissLoading();
}
}
private ObservableTransformer<T, T> loadingTransformer() {
return observable -> observable
.subscribeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(disposable -> startLoading())
.doFinally(this::dismissLoading);
}
private ObservableTransformer<T, T> loadingTransformerWithoutDismiss() {
return observable -> observable
.subscribeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(disposable -> startLoading());
}
}
复制代码
除了 BaseRemoteDataSource 外,还需要一个 BaseRepo。对 BaseRepo 的定位是将其当做一个接口调度器,其持有 BaseRemoteDataSource 的实例并中转 ViewModel 的接口调用请求,并可以在 BaseRepo 分担一部分数据处理逻辑
public class BaseRepo {
protected T remoteDataSource;
public BaseRepo(T remoteDataSource) {
this.remoteDataSource = remoteDataSource;
}
}
复制代码
这样,ViewModel 不关心接口的实际调用实现,方便以后更换 BaseRemoteDataSource 的实现方式,且将一部分的数据处理逻辑放到了 BaseRepo ,有利于逻辑的复用
五、实践操作(1)-请求天气数据
上文讲了一些基础组件的逻辑实现以及对其的定位,此小节就以一个请求天气数据的接口为例,来介绍如何具体实现一个网络请求的整体流程
首先是声明接口
public interface ApiService {
@Headers({HttpConfig.HTTP_REQUEST_TYPE_KEY + “:” + HttpConfig.HTTP_REQUEST_WEATHER})
@GET(“onebox/weather/query”)
Observable<BaseResponseBody> queryWeather(@Query(“cityname”) String cityName);
}
复制代码
增加的头部信息是为了标明该接口的请求类型,因为本文作为 demo 的几个接口所用到的 baseUrl 以及 请求key 并不相同,因此通过声明头部来为接口动态指定请求参数,而这就需要用到 Retrofit 的拦截器了
public class FilterInterceptor implements Interceptor {
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
Request originalRequest = chain.request();
HttpUrl.Builder httpBuilder = originalRequest.url().newBuilder();
Headers headers = originalRequest.headers();
if (headers != null && headers.size() > 0) {
String requestType = headers.get(HttpConfig.HTTP_REQUEST_TYPE_KEY);
if (!TextUtils.isEmpty(requestType)) {
switch (requestType) {
case HttpConfig.HTTP_REQUEST_WEATHER: {
httpBuilder.addQueryParameter(HttpConfig.KEY, HttpConfig.KEY_WEATHER);
break;
}
case HttpConfig.HTTP_REQUEST_QR_CODE: {
httpBuilder.addQueryParameter(HttpConfig.KEY, HttpConfig.KEY_QR_CODE);
break;
}
case HttpConfig.HTTP_REQUEST_NEWS: {
httpBuilder.addQueryParameter(HttpConfig.KEY, HttpConfig.KEY_NEWS);
break;
}
}
}
}
Request.Builder requestBuilder = originalRequest.newBuilder()
.removeHeader(HttpConfig.HTTP_REQUEST_TYPE_KEY)
.url(httpBuilder.build());
return chain.proceed(requestBuilder.build());
}
}
复制代码
声明 BaseRemoteDataSource 的实现类 WeatherDataSource
public class WeatherDataSource extends BaseRemoteDataSource implements IWeatherDataSource {
public WeatherDataSource(BaseViewModel baseViewModel) {
super(baseViewModel);
}
@Override
public void queryWeather(String cityName, RequestCallback responseCallback) {
execute(getService(ApiService.class).queryWeather(cityName), responseCallback);
}
}
复制代码
声明 BaseRepo 的实现类 WeatherRepo
public class WeatherRepo extends BaseRepo {
public WeatherRepo(IWeatherDataSource remoteDataSource) {
super(remoteDataSource);
}
public MutableLiveData queryWeather(String cityName) {
MutableLiveData weatherMutableLiveData = new MutableLiveData<>();
remoteDataSource.queryWeather(cityName, new RequestCallback() {
@Override
public void onSuccess(Weather weather) {
weatherMutableLiveData.setValue(weather);
}
});
return weatherMutableLiveData;
}
}
复制代码
还需要一个 WeatherViewModel,View 层通过调用 queryWeather() 方法在请求成功时触发 weatherLiveData 更新数据,View 层已事先监听 weatherLiveData,并在数据更新时就可以立即收到最新数据
public class WeatherViewModel extends BaseViewModel {
private MutableLiveData weatherLiveData;
private WeatherRepo weatherRepo;
public WeatherViewModel() {
weatherLiveData = new MutableLiveData<>();
weatherRepo = new WeatherRepo(new WeatherDataSource(this));
}
public void queryWeather(String cityName) {
weatherRepo.queryWeather(cityName).observe(lifecycleOwner, new Observer() {
@Override
public void onChanged(@Nullable Weather weather) {
weatherLiveData.setValue(weather);
}
});
}
public MutableLiveData getWeatherLiveData() {
return weatherLiveData;
}
}
复制代码
在 QueryWeatherActivity 中打印出接口的请求结果
public class QueryWeatherActivity extends BaseActivity {
private static final String TAG = “QueryWeatherActivity”;
private WeatherViewModel weatherViewModel;
private EditText et_cityName;
private TextView tv_weather;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_query_weather);
et_cityName = findViewById(R.id.et_cityName);
tv_weather = findViewById(R.id.tv_weather);
}
@Override
protected ViewModel initViewModel() {
weatherViewModel = LViewModelProviders.of(this, WeatherViewModel.class);
weatherViewModel.getWeatherLiveData().observe(this, this::handlerWeather);
return weatherViewModel;
}
private void handlerWeather(Weather weather) {
StringBuilder result = new StringBuilder();
for (Weather.InnerWeather.NearestWeather nearestWeather : weather.getData().getWeather()) {
result.append(“\n\n”).append(new Gson().toJson(nearestWeather));
}
tv_weather.setText(result.toString());
}
public void queryWeather(View view) {
tv_weather.setText(null);
weatherViewModel.queryWeather(et_cityName.getText().toString());
}
}
复制代码
也许有人会觉得为了请求一个接口需要建立三个实现类(WeatherDataSource、WeatherRepo、WeatherViewModel)以及一个接口(IQrCodeDataSource)有点繁琐,但这是想要划分职责并实现逻辑与UI相隔离的必然结果。WeatherDataSource 用来实现接口的实际调用,只负责请求数据并传递请求结果。WeatherRepo 用来屏蔽 WeatherViewModel 对 WeatherDataSource 的感知,并承担起一部分数据处理逻辑。WeatherViewModel 用于实现逻辑与 UI 的隔离,并保障数据不因为页面重建而丢失。这样,Activity 就可以尽量只承担数据呈现的职责,而不必掺杂数据处理逻辑
六、实践操作(2)-请求生成二维码
此处再来看一个例子,用于生成指定内容的二维码
public class QrCodeDataSource extends BaseRemoteDataSource implements IQrCodeDataSource {
public QrCodeDataSource(BaseViewModel baseViewModel) {
super(baseViewModel);
}
@Override
public void createQrCode(String text, int width, RequestCallback callback) {
execute(getService(ApiService.class, HttpConfig.BASE_URL_QR_CODE).createQrCode(text, width), callback);
}
}
复制代码
此处接口请求回来的只是一段 base64 编码的字符串,而外部希望获取到的自然是一个可以直接使用的 Bitmap ,因此可以在 Repo 中先对数据进行转换后再传递到外部
public class QrCodeRepo extends BaseRepo {
public QrCodeRepo(IQrCodeDataSource remoteDataSource) {
super(remoteDataSource);
}
public MutableLiveData createQrCode(String text, int width) {
MutableLiveData liveData = new MutableLiveData<>();
remoteDataSource.createQrCode(text, width, new RequestCallback() {
@SuppressLint(“CheckResult”)
@Override
public void onSuccess(QrCode qrCode) {
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(@NonNull ObservableEmitter emitter) throws Exception {
Bitmap bitmap = base64ToBitmap(qrCode.getBase64_image());
emitter.onNext(bitmap);
emitter.onComplete();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
@Override
public void accept(@NonNull Bitmap bitmap) throws Exception {
qrCode.setBitmap(bitmap);
liveData.setValue(qrCode);
}
});
}
});
return liveData;
}
private static Bitmap base64ToBitmap(String base64String) {
byte[] decode = Base64.decode(base64String, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(decode, 0, decode.length);
}
}
复制代码
public class QrCodeViewModel extends BaseViewModel {
private MutableLiveData qrCodeLiveData;
private QrCodeRepo qrCodeRepo;
public QrCodeViewModel() {
qrCodeLiveData = new MutableLiveData<>();
qrCodeRepo = new QrCodeRepo(new QrCodeDataSource(this));
}
public void createQrCode(String text, int width) {
qrCodeRepo.createQrCode(text, width).observe(lifecycleOwner, new Observer() {
@Override
public void onChanged(@Nullable QrCode qrCode) {
qrCodeLiveData.setValue(qrCode);
}
});
}
public MutableLiveData getQrCodeLiveData() {
要如何成为Android架构师?
搭建自己的知识框架,全面提升自己的技术体系,并且往底层源码方向深入钻研。
大多数技术人喜欢用思维脑图来构建自己的知识体系,一目了然。这里给大家分享一份大厂主流的Android架构师技术体系,可以用来搭建自己的知识框架,或者查漏补缺;
对应这份技术大纲,我也整理了一套Android高级架构师完整系列的视频教程,主要针对3-5年Android开发经验以上,需要往高级架构师层次学习提升的同学,希望能帮你突破瓶颈,跳槽进大厂;
最后我必须强调几点:
1.搭建知识框架可不是说你整理好要学习的知识顺序,然后看一遍理解了能复制粘贴就够了,大多都是需要你自己读懂源码和原理,能自己手写出来的。
2.学习的时候你一定要多看多练几遍,把知识才吃透,还要记笔记,这些很重要! 最后你达到什么水平取决你消化了多少知识
3.最终你的知识框架应该是一个完善的,兼顾广度和深度的技术体系。然后经过多次项目实战积累经验,你才能达到高级架构师的层次。
你只需要按照在这个大的框架去填充自己,年薪40W一定不是终点,技术无止境
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
o qrCodeRepo;
public QrCodeViewModel() {
qrCodeLiveData = new MutableLiveData<>();
qrCodeRepo = new QrCodeRepo(new QrCodeDataSource(this));
}
public void createQrCode(String text, int width) {
qrCodeRepo.createQrCode(text, width).observe(lifecycleOwner, new Observer() {
@Override
public void onChanged(@Nullable QrCode qrCode) {
qrCodeLiveData.setValue(qrCode);
}
});
}
public MutableLiveData getQrCodeLiveData() {
要如何成为Android架构师?
搭建自己的知识框架,全面提升自己的技术体系,并且往底层源码方向深入钻研。
大多数技术人喜欢用思维脑图来构建自己的知识体系,一目了然。这里给大家分享一份大厂主流的Android架构师技术体系,可以用来搭建自己的知识框架,或者查漏补缺;
[外链图片转存中…(img-4s4WabRC-1714574905606)]
对应这份技术大纲,我也整理了一套Android高级架构师完整系列的视频教程,主要针对3-5年Android开发经验以上,需要往高级架构师层次学习提升的同学,希望能帮你突破瓶颈,跳槽进大厂;
最后我必须强调几点:
1.搭建知识框架可不是说你整理好要学习的知识顺序,然后看一遍理解了能复制粘贴就够了,大多都是需要你自己读懂源码和原理,能自己手写出来的。
2.学习的时候你一定要多看多练几遍,把知识才吃透,还要记笔记,这些很重要! 最后你达到什么水平取决你消化了多少知识
3.最终你的知识框架应该是一个完善的,兼顾广度和深度的技术体系。然后经过多次项目实战积累经验,你才能达到高级架构师的层次。
你只需要按照在这个大的框架去填充自己,年薪40W一定不是终点,技术无止境
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!