本文屬於技術的合併,所以不會對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不會在同一時間被不同線程調用,再將添加所需要的數據庫操作方法即可。
這樣就有完整的基礎框架。如果大家對這樣的代碼感興趣的話可以點擊下方下載。