本文属于技术的合并,所以不会对mvp、rxjava等的技术进行初始使用的讲解。建议对这些技术有一定基础后查看。
先看依赖
先在根目录(工程目录)build.gradle添加realm的插件安装
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "io.realm:realm-gradle-plugin:2.3.0"
}
再在app的gradle中添加
android {
apply plugin: 'realm-android'
}
dependencies {
implementation 'io.reactivex.rxjava2:rxjava:2.1.1'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'//配合rxjava2
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.8.1'//拦截器
}
1.mvp包分类:
Contact:(1)view接口(2)presenter接口 ----作为接口统筹调用,以供v层和p层的互相调用。也方便开发者理清逻辑
Activity:继承view接口持有presenter 的接口--持有接口不持有presenter实例,以保证层与层调用皆以接口调用。
----对应v层
presernter:继承presenter接口--网络或数据库耗时操作时使用rxjava2.0启动异步
----对应P层
model: (1)DBhelper--Realm(2)RetrofitService--retrofit2.0
数据库耗时操作和网络请求都在p层调用。
----对应M层
2.具体代码:
以登录为例子:
先看Contact:
public interface LoginContact {
interface view extends BaseView {
void loginSuccess();
}
interface presenter extends BasePresenter {
/**
* 登录
*/
void login(LoginRequest request);
}
}
如果熟悉MVP的会发现这里没有Model的接口。因为使用Retrofit的缘故,将网络请求Model是直接用RetroditService文件来直接调用,而数据库用的Realm,数据库存取用DBHelper。即Model层是共用两种文件:
- Retrofit的网络请求实体类
- Realm的数据存取实体类
写到persenter的实体类请求则会清晰明了些。如下:
public class LoginPresenter extends BasePresenterImpl<LoginContact.view>
implements LoginContact.presenter {
public LoginPresenter(LoginContact.view view) {
super(view);
}
@Override
public void login(LoginRequest request) {
Api.getInstance().login(GsonUtil.GsonString(request))
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
addDisposable(disposable);
view.showLoadingDialog("请求发起");
}
})
.map(new Function<LoginResponse, LoginResponse>() {
@Override
public LoginResponse apply(final LoginResponse baseBean) throws Exception {
DbHelper.getDbHelper().saveBaseInfo(baseBean);//保存登录数据
return baseBean;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<LoginResponse>() {
@Override
public void accept(LoginResponse baseBean) throws Exception {
view.loginSuccess();
view.dismissLoadingDialog();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
view.dismissLoadingDialog();
view.showToast("失败:" + throwable.getMessage());
ExceptionHelper.handleException(throwable);
}
});;
}
}
请求完毕后如果需要数据存储或数据的中转处理,建议是在转换回主线程前用map处理返回数据,并且用DbHelper的方法进行数据存储。然后再回到主线处理界面上的事项。
这样看从view层查看到presenter层就可以直观明了的查看到 请求和返回 的处理逻辑。
那么我们现在来了解下rxjava2.0和retrofit2.0是怎么结合
public class BaseApiImpl implements BaseApi {
private volatile static Retrofit retrofit = null;
protected Retrofit.Builder retrofitBuilder = new Retrofit.Builder();
protected OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
public BaseApiImpl(String baseUrl) {
retrofitBuilder.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder()
.setLenient()
.create()
))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpBuilder.addInterceptor(getLoggerInterceptor()).build())
.baseUrl(baseUrl);
}
/**
* 构建retroft
*
* @return Retrofit对象
*/
@Override
public Retrofit getRetrofit() {
if (retrofit == null) {
//锁定代码块
synchronized (BaseApiImpl.class) {
if (retrofit == null) {
retrofit = retrofitBuilder.build(); //创建retrofit对象
}
}
}
return retrofit;
}
@Override
public OkHttpClient.Builder setInterceptor(Interceptor interceptor) {
return httpBuilder.addInterceptor(interceptor);
}
@Override
public Retrofit.Builder setConverterFactory(Converter.Factory factory) {
return retrofitBuilder.addConverterFactory(factory);
}
/**
* 日志拦截器
* 将你访问的接口信息
*
* @return 拦截器
*/
public HttpLoggingInterceptor getLoggerInterceptor() {
//日志显示级别
HttpLoggingInterceptor.Level level = HttpLoggingInterceptor.Level.HEADERS;
//新建log拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.d("ApiUrl", "--->" + message);
}
});
loggingInterceptor.setLevel(level);
return loggingInterceptor;
}
}
BaseApiImpl是用来给Retrofit实体类工具 继承的基础类,这里将retrofit的实例创建好,并且可以在此做基础的日志拦截。下面为实体类工具代码:
public class Api extends BaseApiImpl {
private static Api api = new Api(RetrofitService.BASE_URL);
public Api(String baseUrl) {
super(baseUrl);
}
public static RetrofitService getInstance() {
return api.getRetrofit().create(RetrofitService.class);
}
}
在我们上面presenter的登录调用就用了Api.getInstance()方法。目的就是拿到RetrofitService的retrofit实体类,进行网络的调用。
下面来看下RetrofitService代码:
public interface RetrofitService {
String BASE_URL = "http://IP:port/api/";
@GET("self/getUserSelfInfo")
Observable<LoginResponse> login(@Body String request);
}
@GET和@Body都是Retrofit的使用方式。而Observable就是rxjava的订阅类,此类可以进行异步的发起。
我们在写完这些后,以后有新的网络请求只需要在RetrofitService这个文件中添加对应的请求。就可以在presenter中直接请求了。
这样就对以后的代码编写节约了很多时间。
现在我们再看下Realm的使用:
public class DbHelper {
public static volatile DbHelper dbHelper;
public static DbHelper getDbHelper(){
if(dbHelper ==null){
synchronized (DbHelper.class){
if(dbHelper == null){
dbHelper =new DbHelper();
}
}
}
return dbHelper;
}
public static RealmResults<BaseInfo> getBaseInfos(){
return Realm.getDefaultInstance().where(BaseInfo.class).findAll();
}
public void saveBaseInfo(final LoginResponse baseBean){
Realm.getDefaultInstance().executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
BaseInfo ba = new BaseInfo();
ba.setName(baseBean.getData().getUserName());
ba.setUserId(baseBean.getData().getUserId());
realm.copyToRealm(ba);
}
});
}
}
除去初始的Realm配置。直接看DbHepler,使用单例保证dbHelper不会在同一时间被不同线程调用,再将添加所需要的数据库操作方法即可。
这样就有完整的基础框架。如果大家对这样的代码感兴趣的话可以点击下方下载。