文本已經收錄到個人Github我的博客,歡迎大佬們光臨寒舍:個人GIthub博客java
看完本篇文章的,能夠看下帶你封裝本身的MVP+Retrofit+RxJava2框架(二),裏面封裝獲得了改進react
在搞清楚這個問題以前,咱們回顧一下基本概念android
RxJava: ReactiveX 在JVM上的一個實現,ReactiveX使用Observable序列組合異步和基於事件的程序;掌握了它,你能夠優美地處理異步任務和事件的回調git
Retrofit:一個 RESTful 的 HTTP 網絡請求框架的封裝,網絡請求的工做本質上是 OkHttp 完成,而 Retrofit 僅負責 網絡請求接口的封裝:掌握了它,你能優美地進行網絡請求。github
MVP:一種解耦模型和視圖的模式,是如今不少公司的主流模式。json
因而可知,在平時的開發中熟練運用這種模式,不只能夠知足生活中大部分應用程序的場景,還能夠爲未來的工做積攢寶貴的實戰經驗。api
本項目基於Android X 進行構建,完整代碼可在個人github上下載:帶你封裝本身的MVP+Retrofit+RxJava2框架網絡
首先,看一下咱們項目的基本結構,下面筆者將爲你們詳細介紹每一個類的相關信息app
Base基類是封裝了一些基類,方便後面新建新的activity或者fragment,減小耦合框架
這個類是Activity的基類,注意與下面的BaseMvpActivity區分開
/** * Description : BaseActivity 基類活動 * * @author XuCanyou666 * @date 2020/2/2 */
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
initPresenter();
initViews();
ButterKnife.bind(this);
}
/** * 抽象方法:實例化Presenter */
protected abstract void initPresenter();
/** * 抽象方法:初始化控件,通常在BaseActivity中經過ButterKnife來綁定,因此該方法內部通常咱們初始化界面相關的操做 * * @return 控件 */
protected abstract void initViews();
/** * 抽象方法:獲得佈局id * * @return 佈局id */
protected abstract int getLayoutId();
/** * 啓動Fragment * * @param id id * @param fragment 碎片 */
protected void startFragment(int id, Fragment fragment) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(id, fragment);
fragmentTransaction.commit();
}
}
複製代碼
一個接口,說明了每個View基本須要的一些操做
package com.users.xucanyou666.rxjava2_retrofit_mvp.base;
/** * created by xucanyou666 * on 2020/1/31 18:26 * email:913710642@qq.com */
public interface BaseView {
/** * 顯示進度框 */
void showProgressDialog();
/** * 關閉進度框 */
void hideProgressDialog();
/** * 出錯信息的回調 * * @param result 錯誤信息 */
void onError(String result);
}
複製代碼
繼承自BaseActivity,它是MVP活動的基類,封裝好了presenter的相關操做
package com.users.xucanyou666.rxjava2_retrofit_mvp.base;
/** * created by xucanyou666 MVP活動的基類,封裝好了presenter的相關操做 * on 2019/12/24 20:53 * email:913710642@qq.com */
public abstract class BaseMvpActivity<V extends BaseView, P extends BasePresenter> extends BaseActivity {
private P presenter;
/** * 初始化presenter */
@Override
protected void initPresenter() {
presenter = createPresenter();
if (presenter != null) {
presenter.attachView((V) this);
}
}
/** * 建立presenter * * @return Presenter */
protected abstract P createPresenter();
/** * 獲得presenter * * @return presenter */
protected P getPresenter() {
return presenter;
}
/** * 銷燬 */
@Override
protected void onDestroy() {
super.onDestroy();
if (presenter != null) {
presenter.detachView();
}
}
}
複製代碼
須要注意的是,這裏用了ButterKnife框架,對碎片進行了綁定和解綁操做
/** * Fragment的基類,封裝了一些Fragment的相關操做 * created by xucanyou666 * on 2020/1/31 16:21 * email:913710642@qq.com */
public abstract class BaseFragment<T extends BasePresenter> extends Fragment implements BaseView {
protected T mPresenter;
protected Context mContext;
protected Bundle mBundle;
protected Unbinder unbinder;
protected View view;
/** * 恢復數據 * * @param outState bundle */
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (mBundle != null) {
outState.putBundle("bundle", mBundle);
}
}
/** * 綁定activity * * @param context context */
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
mContext = context;
}
/** * 運行在onAttach以後,能夠接收別人傳遞過來的參數,實例化對象 * 能夠解決返回的時候頁面空白的bug * * @param savedInstanceState */
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mBundle = savedInstanceState.getBundle("bundle");
} else {
mBundle = getArguments() == null ? new Bundle() : getArguments();
}
//初始化presenter
mPresenter = initPresenter();
}
protected T getPresenter() {
return mPresenter;
}
/** * 運行在onCreate以後,生成View視圖 * * @param inflater * @param container * @param savedInstanceState * @return */
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = initView(inflater, container, savedInstanceState);
unbinder = ButterKnife.bind(this, view);
return view;
}
/** * 運行在onCreateView以後 * 加載數據 * * @param savedInstanceState */
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mPresenter.attachView(this);
}
/** * 跳轉Fragment * * @param toFragment 跳轉去的fragment */
public void startFragment(Fragment toFragment) {
Log.d(TAG, "haha");
startFragment(toFragment, null);
}
/** * 跳轉Fragment * * @param toFragment 跳轉到的fragment * @param tag fragment的標籤 */
public void startFragment(Fragment toFragment, String tag) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.hide(this).add(android.R.id.content, toFragment, tag);
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.commitAllowingStateLoss();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
/** * fragment進行回退 * 相似於activity的OnBackPress */
public void onBack() {
getFragmentManager().popBackStack();
}
@Override
public void onDetach() {
mPresenter.detachView();
super.onDetach();
}
/** * 初始化Fragment應有的視圖 * * @return view */
public abstract View initView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState);
/** * 建立presenter * * @return <T extends BasePresenter> 必須是BasePresenter的子類 */
public abstract T initPresenter();
/** * 獲得context * * @return context */
@Override
public Context getContext() {
return mContext;
}
/** * 獲得bundle * * @return bundle */
public Bundle getBundle() {
return mBundle;
}
/** * 獲得fragment * * @return fragment */
public Fragment getFragment() {
return this;
}
}
複製代碼
Presenter的基類,這裏的CompositeDisposable主要用途是及時取消訂閱,以防止內存泄漏,具體CompositeDisposable的用法可參照Rxjava關於Disposable你應該知道的事
/** * created by xucanyou666 * on 2020/1/16 17:12 * email:913710642@qq.com */
public abstract class BasePresenter<V extends BaseView> {
//將全部正在處理的Subscription都添加到CompositeSubscription中。統一退出的時候註銷觀察
private CompositeDisposable mCompositeDisposable;
private V baseView;
/** * 和View綁定 * * @param baseView */
public void attachView(V baseView) {
this.baseView = baseView;
}
/** * 解綁View,該方法在BaseMvpActivity類中被調用 */
public void detachView() {
baseView = null;
// 在界面退出等須要解綁觀察者的狀況下調用此方法統一解綁,防止Rx形成的內存泄漏
if (mCompositeDisposable != null) {
mCompositeDisposable.dispose();
}
}
/** * 獲取View * * @return view */
public V getMvpView() {
return baseView;
}
/** * 將Disposable添加,在每次網絡訪問以前初始化時進行添加操做 * * @param subscription subscription */
public void addDisposable(Disposable subscription) {
//csb 若是解綁了的話添加 sb 須要新的實例不然綁定時無效的
if (mCompositeDisposable == null || mCompositeDisposable.isDisposed()) {
mCompositeDisposable = new CompositeDisposable();
}
mCompositeDisposable.add(subscription);
}
}
複製代碼
封裝了一個能夠全局獲取context的方法,參考寫法自:《第一行代碼--第二版》,須要注意的是,記得在AndroidManifest中註冊application
package com.users.xucanyou666.rxjava2_retrofit_mvp.base;
import android.app.Application;
import android.content.Context;
/** * 基類 * created by xucanyou666 * on 2019/11/2 14:46 * email:913710642@qq.com * @author xucanyou666 */
public class MyApplication extends Application {
private static Context context;
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
public static Context getContext() {
return context;
}
}
複製代碼
Retrofit單例工具類
/** * Retrofit單例工具類 * created by xucanyou666 * on 2020/1/16 16:38 * email:913710642@qq.com */
public class RetrofitManager {
private Retrofit mRetrofit;
//構造器私有,這個工具類只有一個實例
private RetrofitManager() {
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
httpClientBuilder.connectTimeout(15, TimeUnit.SECONDS);
mRetrofit = new Retrofit.Builder()
.client(httpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
}
/** * 靜態內部類單例模式 * * @return */
public static RetrofitManager getInstance() {
return Inner.retrofitManager;
}
private static class Inner {
private static final RetrofitManager retrofitManager = new RetrofitManager();
}
/** * 利用泛型傳入接口class返回接口實例 * * @param ser 類 * @param <T> 類的類型 * @return Observable */
public <T> T createRs(Class<T> ser) {
return mRetrofit.create(ser);
}
}
複製代碼
RxJava的工具類,執行線程調度工做
/** * created by xucanyou666 * on 2019/11/17 19:20 * email:913710642@qq.com * * @author xucanyou666 */
public class RxJavaUtil {
/** * 線程調度工做 * * @param observable 被觀察者 * @param <T> 類型 */
public static <T> Observable toSubscribe(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
}
複製代碼
常量池,特別感謝api open網提供的免費api
/** * created by xucanyou666 * on 2019/11/17 19:01 * email:913710642@qq.com */
public class StaticQuality {
public static final String BASE_URL="https://api.gushi.ci/";
}
複製代碼
這裏集中了一些Model層,Presenter層,View層的與詩歌相關的接口
/** * 詩歌的接口管理器 * created by xucanyou666 * on 2020/2/2 15:33 * email:913710642@qq.com */
public interface IPoetryContract {
interface IPoetryModel {
/** * 獲得詩歌 * * @return 詩歌 */
Observable<PoetryEntity> getPoetry();
}
interface IPoetryPresenter {
void getPoetry();
}
interface IPoetryView extends BaseView {
/** * @param author 做者 */
void searchSuccess(String author);
}
}
複製代碼
/** * 詩歌的實體類 * created by xucanyou666 * on 2020/1/23 21:23 * email:913710642@qq.com * API返回示例: * { * "content": "胡瓶落膊紫薄汗,碎葉城西秋月團。", * "origin": "從軍行七首", * "author": "王昌齡", * "category": "古詩文-天氣-月亮" * } */
public class PoetryEntity {
private String content; //詩歌內容
private String origin; //來源
private String author; //做者
private String category; //分類
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}
複製代碼
/** * retrofit接口 * created by xucanyou666 * on 2020/1/23 21:25 * email:913710642@qq.com */
public interface GetPoetryEntity {
/** * 獲取古詩詞 * * @return 古詩詞 */
@GET("all.json")
Observable<PoetryEntity> getPoetry();
}
複製代碼
這裏爲了減小代碼量,方便讀者們掌握核心操做,故View層都是用的同一個presenter和model,僅做學習參考
須要注意的是,這裏BaseMvpActivity<activity, presenter>,activity填入的是當前的activity,presenter填入的是對應的presenter
/** * Description : MainActivity * * @author XuCanyou666 * @date 2020/2/3 */
public class MainActivity extends BaseMvpActivity<MainActivity, PoetryPresenter> implements IPoetryContract.IPoetryView {
@BindView(R.id.btn_get_poetry)
Button btnGetPoetry;
@BindView(R.id.tv_poetry_author)
TextView tvPoetryAuthor;
@BindView(R.id.btn_goto_fragment)
Button btnGotoFragment;
@BindView(R.id.ll)
LinearLayout ll;
@Override
protected void initViews() {
}
@Override
protected int getLayoutId() {
return R.layout.activity_main;
}
@Override
protected PoetryPresenter createPresenter() {
return PoetryPresenter.getInstance();
}
@Override
public void searchSuccess(String author) {
tvPoetryAuthor.setText(author);
}
@Override
public void showProgressDialog() {
}
@Override
public void hideProgressDialog() {
}
@Override
public void onError(String result) {
Toast.makeText(MyApplication.getContext(), result, Toast.LENGTH_SHORT).show();
}
@OnClick({R.id.btn_get_poetry, R.id.btn_goto_fragment})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.btn_get_poetry:
getPresenter().getPoetry();
break;
case R.id.btn_goto_fragment:
startFragment(R.id.ll, new MainFragment());
break;
default:
break;
}
}
}
複製代碼
/** * Description : MainFragment * * @author XuCanyou666 * @date 2020/2/2 */
public class MainFragment extends BaseFragment<PoetryPresenter> implements IPoetryContract.IPoetryView {
@BindView(R.id.btn_get_poetry)
Button btnGetPoetry;
@BindView(R.id.tv_poetry_author)
TextView tvPoetryAuthor;
@Override
public View initView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
@Override
public PoetryPresenter initPresenter() {
return PoetryPresenter.getInstance();
}
@Override
public void showProgressDialog() {
}
@Override
public void hideProgressDialog() {
}
@Override
public void onError(String result) {
Toast.makeText(MyApplication.getContext(), result, Toast.LENGTH_SHORT).show();
}
@OnClick(R.id.btn_get_poetry)
public void onViewClicked() {
getPresenter().getPoetry();
}
@Override
public void searchSuccess(String author) {
tvPoetryAuthor.setText(author);
}
}
複製代碼
/** * created by xucanyou666 * on 2020/1/16 17:09 * email:913710642@qq.com */
public class PoetryPresenter extends BasePresenter<IPoetryContract.IPoetryView> implements IPoetryContract.IPoetryPresenter {
private static final String TAG = "PoetryPresenter";
private PoetryEntity mPoetryEntity;
private PoetryModel mPoetryModel;
private PoetryPresenter() {
mPoetryModel = PoetryModel.getInstance();
}
public static PoetryPresenter getInstance() {
return Inner.instance;
}
private static class Inner {
private static final PoetryPresenter instance = new PoetryPresenter();
}
/** * 獲得詩歌 */
@Override
public void getPoetry() {
Observable observable = mPoetryModel.getPoetry().doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
addDisposable(disposable);
}
});
observable = RxJavaUtil.toSubscribe(observable);
observable.subscribe(new Observer<PoetryEntity>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(PoetryEntity poetryEntity) {
mPoetryEntity = poetryEntity;
}
@Override
public void onError(Throwable e) {
getMvpView().onError(e.getMessage());
Log.d(TAG, "onError: " + e.getMessage());
}
@Override
public void onComplete() {
if (mPoetryEntity != null) {
getMvpView().searchSuccess(mPoetryEntity.getAuthor());
}
}
});
}
}
複製代碼
/** * created by xucanyou666 * on 2020/1/16 17:06 * email:913710642@qq.com */
public class PoetryModel implements IPoetryContract.IPoetryModel {
private PoetryModel() {
}
public static PoetryModel getInstance() {
return Inner.instance;
}
private static class Inner {
private static final PoetryModel instance = new PoetryModel();
}
/** * 獲取古詩詞 * * @return 古詩詞 */
@Override
public Observable<PoetryEntity> getPoetry() {
return RetrofitManager.getInstance().createRs(GetPoetryEntity.class).getPoetry();
}
}
複製代碼
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId "com.users.xucanyou666.rxjava2_retrofit_mvp"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
// RxJava
implementation 'io.reactivex.rxjava2:rxjava:2.1.12'
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
// Retrofit和jxjava關聯
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
// Retrofit使用Gson轉換
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
// RxAndroid
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
//引入ButterKnife
implementation "com.jakewharton:butterknife:10.2.0"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
annotationProcessor "com.jakewharton:butterknife-compiler:10.2.0"
implementation "com.google.android.material:material:1.0.0"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
複製代碼
解決措施:加上權限便可
<uses-permission android:name="android.permission.INTERNET" />
複製代碼
使用ButterKnife框架的時候
當是androidX的時候,須要implementation 10.2.0版本的ButterKnife
//引入ButterKnife
implementation "com.jakewharton:butterknife:10.2.0"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
annotationProcessor "com.jakewharton:butterknife-compiler:10.2.0"
複製代碼
當是android 28等其餘版本的時候,能夠導入8.4.0版本的ButterKnife(導入10.2.0版本會出錯)
implementation 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
複製代碼
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
複製代碼
點擊事件發生的場景:Fragment中初始化控件沒有用butterKnife框架
解決措施以下:
若是文章對您有一點幫助的話,但願您能點一下贊,您的點贊,是我前進的動力
本文參考連接: