前言
文本已經(jīng)收錄到我的Github個(gè)人博客蚤假,歡迎大佬們光臨寒舍:我的GIthub博客
看完本篇文章的当凡,可以看下帶你封裝自己的MVP+Retrofit+RxJava2框架(二)铣焊,里面封裝得到了改進(jìn)
本篇文章需要已經(jīng)具備的知識(shí):
-
MVP
的概念和基本使用 -
Retrofit
框架的基本使用 -
RxJava2
框架的基本使用 -
ButterKnife
框架的基本使用 -
Base
基類的概念
學(xué)習(xí)清單:
-
Activity
和Fragment
基類的封裝 -
MVP
的封裝使用
一.為什么要封裝這套框架呢?
在搞清楚這個(gè)問題之前末贾,我們回顧一下基本概念
RxJava
: ReactiveX
在JVM
上的一個(gè)實(shí)現(xiàn)筐钟,ReactiveX
使用Observable
序列組合異步和基于事件的程序;掌握了它秉继,你可以優(yōu)美地處理異步任務(wù)和事件的回調(diào)
Retrofit
:一個(gè) RESTful
的 HTTP
網(wǎng)絡(luò)請(qǐng)求框架的封裝祈噪,網(wǎng)絡(luò)請(qǐng)求的工作本質(zhì)上是OkHttp
完成,而 Retrofit
僅負(fù)責(zé) 網(wǎng)絡(luò)請(qǐng)求接口的封裝:掌握了它尚辑,你能優(yōu)美地進(jìn)行網(wǎng)絡(luò)請(qǐng)求辑鲤。
MVP
:一種解耦模型和視圖的模式,是現(xiàn)在很多公司的主流模式杠茬。
由此可見月褥,在平時(shí)的開發(fā)中熟練運(yùn)用這種模式,不僅可以滿足生活中大部分應(yīng)用程序的場(chǎng)景瓢喉,還可以為將來的工作積攢寶貴的實(shí)戰(zhàn)經(jīng)驗(yàn)宁赤。
二.核心用法
本項(xiàng)目基于
Android X
進(jìn)行構(gòu)建,完整代碼可在我的Github
上下載:帶你封裝自己的MVP+Retrofit+RxJava2框架
首先栓票,看一下我們項(xiàng)目的基本結(jié)構(gòu)决左,下面筆者將為大家詳細(xì)介紹每個(gè)類的相關(guān)信息
[圖片上傳失敗...(image-e9a3ee-1583233999196)]
2.1 基類Base
Base
基類是封裝了一些基類,方便后面新建新的Activity
或者Fragment
走贪,減少耦合
2.1.1 BaseActivity
這個(gè)類是
Activity
的基類佛猛,注意與下面的BaseMvpActivity
區(qū)分開
/**
* Description : BaseActivity 基類活動(dòng)
*
* @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);
}
/**
* 抽象方法:實(shí)例化Presenter
*/
protected abstract void initPresenter();
/**
* 抽象方法:初始化控件,一般在BaseActivity中通過ButterKnife來綁定坠狡,所以該方法內(nèi)部一般我們初始化界面相關(guān)的操作
*
* @return 控件
*/
protected abstract void initViews();
/**
* 抽象方法:得到布局id
*
* @return 布局id
*/
protected abstract int getLayoutId();
/**
* 啟動(dòng)Fragment
*
* @param id id
* @param fragment 碎片
*/
protected void startFragment(int id, Fragment fragment) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(id, fragment);
fragmentTransaction.commit();
}
}
2.1.2 BaseView
一個(gè)接口语御,說明了每一個(gè)
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 {
/**
* 顯示進(jìn)度框
*/
void showProgressDialog();
/**
* 關(guān)閉進(jìn)度框
*/
void hideProgressDialog();
/**
* 出錯(cuò)信息的回調(diào)
*
* @param result 錯(cuò)誤信息
*/
void onError(String result);
}
2.1.3 BaseMvpActivity
MVP
活動(dòng)的基類繼承自
BaseActivity
,它是MVP
活動(dòng)的基類贪婉,封裝好了Presenter
的相關(guān)操作
package com.users.xucanyou666.rxjava2_retrofit_mvp.base;
/**
* created by xucanyou666 MVP活動(dòng)的基類蓬推,封裝好了presenter的相關(guān)操作
* 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);
}
}
/**
* 創(chuàng)建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();
}
}
}
2.1.4 BaseFragment
Fragment
的基類需要注意的是,這里用了
ButterKnife
框架幻锁,對(duì)碎片進(jìn)行了綁定和解綁操作
/**
* Fragment的基類,封裝了一些Fragment的相關(guān)操作
* 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;
/**
* 恢復(fù)數(shù)據(jù)
*
* @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;
}
/**
* 運(yùn)行在onAttach之后边臼,可以接收別人傳遞過來的參數(shù)哄尔,實(shí)例化對(duì)象
* 可以解決返回的時(shí)候頁面空白的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;
}
/**
* 運(yùn)行在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;
}
/**
* 運(yùn)行在onCreateView之后
* 加載數(shù)據(jù)
*
* @param savedInstanceState
*/
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mPresenter.attachView(this);
}
/**
* 跳轉(zhuǎn)Fragment
*
* @param toFragment 跳轉(zhuǎn)去的fragment
*/
public void startFragment(Fragment toFragment) {
Log.d(TAG, "haha");
startFragment(toFragment, null);
}
/**
* 跳轉(zhuǎn)Fragment
*
* @param toFragment 跳轉(zhuǎn)到的fragment
* @param tag fragment的標(biāo)簽
*/
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進(jìn)行回退
* 類似于activity的OnBackPress
*/
public void onBack() {
getFragmentManager().popBackStack();
}
@Override
public void onDetach() {
mPresenter.detachView();
super.onDetach();
}
/**
* 初始化Fragment應(yīng)有的視圖
*
* @return view
*/
public abstract View initView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState);
/**
* 創(chuàng)建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;
}
}
2.1.5 BasePresenter
Presenter
的基類硼瓣,
CompositeDisposable
主要用途是及時(shí)取消訂閱究飞,以防止內(nèi)存泄漏,具體CompositeDisposable
的用法可參照Rxjava關(guān)于Disposable你應(yīng)該知道的事
/**
* created by xucanyou666
* on 2020/1/16 17:12
* email:913710642@qq.com
*/
public abstract class BasePresenter<V extends BaseView> {
//將所有正在處理的Subscription都添加到CompositeSubscription中堂鲤。統(tǒng)一退出的時(shí)候注銷觀察
private CompositeDisposable mCompositeDisposable;
private V baseView;
/**
* 和View綁定
*
* @param baseView
*/
public void attachView(V baseView) {
this.baseView = baseView;
}
/**
* 解綁View,該方法在BaseMvpActivity類中被調(diào)用
*/
public void detachView() {
baseView = null;
// 在界面退出等需要解綁觀察者的情況下調(diào)用此方法統(tǒng)一解綁,防止Rx造成的內(nèi)存泄漏
if (mCompositeDisposable != null) {
mCompositeDisposable.dispose();
}
}
/**
* 獲取View
*
* @return view
*/
public V getMvpView() {
return baseView;
}
/**
* 將Disposable添加,在每次網(wǎng)絡(luò)訪問之前初始化時(shí)進(jìn)行添加操作
*
* @param subscription subscription
*/
public void addDisposable(Disposable subscription) {
//csb 如果解綁了的話添加 sb 需要新的實(shí)例否則綁定時(shí)無效的
if (mCompositeDisposable == null || mCompositeDisposable.isDisposed()) {
mCompositeDisposable = new CompositeDisposable();
}
mCompositeDisposable.add(subscription);
}
}
2.1.6 MyApplication
- 封裝了一個(gè)可以全局獲取
Context
的方法媒峡,參考寫法自:《第一行代碼--第二版》- 注意:記得在
AndroidManifest
中注冊(cè)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;
}
}
2.2 工具類 Util
2.2.1 RetrofitManager
Retrofit
單例工具類
/**
* Retrofit單例工具類
* created by xucanyou666
* on 2020/1/16 16:38
* email:913710642@qq.com
*/
public class RetrofitManager {
private Retrofit mRetrofit;
//構(gòu)造器私有瘟栖,這個(gè)工具類只有一個(gè)實(shí)例
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();
}
/**
* 靜態(tài)內(nèi)部類單例模式
*
* @return
*/
public static RetrofitManager getInstance() {
return Inner.retrofitManager;
}
private static class Inner {
private static final RetrofitManager retrofitManager = new RetrofitManager();
}
/**
* 利用泛型傳入接口class返回接口實(shí)例
*
* @param ser 類
* @param <T> 類的類型
* @return Observable
*/
public <T> T createRs(Class<T> ser) {
return mRetrofit.create(ser);
}
}
2.2.2 RxJavaUtil
RxJava
的工具類,執(zhí)行線程調(diào)度工作
/**
* created by xucanyou666
* on 2019/11/17 19:20
* email:913710642@qq.com
*
* @author xucanyou666
*/
public class RxJavaUtil {
/**
* 線程調(diào)度工作
*
* @param observable 被觀察者
* @param <T> 類型
*/
public static <T> Observable toSubscribe(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
}
2.3 常量類 Contant
常量池谅阿,特別感謝
api open
網(wǎng)提供的免費(fèi)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/";
}
2.4 接口管理器 Contract
這里集中了一些
Model
層,Presenter
層,View
層的與詩歌相關(guān)的接口
/**
* 詩歌的接口管理器
* 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);
}
}
2.5 實(shí)體類 Entity
/**
* 詩歌的實(shí)體類
* created by xucanyou666
* on 2020/1/23 21:23
* email:913710642@qq.com
* API返回示例:
* {
* "content": "胡瓶落膊紫薄汗半哟,碎葉城西秋月團(tuán)。",
* "origin": "從軍行七首",
* "author": "王昌齡",
* "category": "古詩文-天氣-月亮"
* }
*/
public class PoetryEntity {
private String content; //詩歌內(nèi)容
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;
}
}
2.6 Retrofit
接口 iApiService
/**
* retrofit接口
* created by xucanyou666
* on 2020/1/23 21:25
* email:913710642@qq.com
*/
public interface GetPoetryEntity {
/**
* 獲取古詩詞
*
* @return 古詩詞
*/
@GET("all.json")
Observable<PoetryEntity> getPoetry();
}
2.7 視圖層 View
這里為了減少代碼量签餐,方便讀者們掌握核心操作寓涨,故
View
層都是用的同一個(gè)Presenter
和Model
,僅作學(xué)習(xí)參考
2.7.1 MainActivity
需要注意的是氯檐,這里
BaseMvpActivity<activity, presenter>
中Activity
填入的是當(dāng)前的Activity
戒良,Presenter
填入的是對(duì)應(yīng)的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;
}
}
}
2.7.2 MainFragment
/**
* 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);
}
}
2.8 Presenter
層
/**
* 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());
}
}
});
}
}
2.9 Model
層
/**
* 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();
}
}
2.10 app.build.gradle
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關(guān)聯(lián)
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
// Retrofit使用Gson轉(zhuǎn)換
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'
}
三.我在使用中遇到的問題
3.1 網(wǎng)絡(luò)權(quán)限忘記授予
- 解決措施:加上權(quán)限即可
<uses-permission android:name="android.permission.INTERNET" />
3.2 ButterKnife
框架版本問題
使用ButterKnife
框架的時(shí)候
當(dāng)是androidX
的時(shí)候,需要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"
當(dāng)是android 28
等其他版本的時(shí)候冠摄,可以導(dǎo)入8.4.0
版本的ButterKnife
(導(dǎo)入10.2.0
版本會(huì)出錯(cuò))
implementation 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
3.3 ButterKnife
需要Java 1.8以上的支持
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
3.4 Fragment
中點(diǎn)擊事件失效的問題
- 點(diǎn)擊事件失效發(fā)生的場(chǎng)景:
Fragment
中初始化控件沒有用ButterKnife
框架
解決措施如下:
A:方法一:
- 將控件的初始化放在
onCreateView
中 - 將控件的點(diǎn)擊事件的代碼放在
onActivityCreated
中
B:方法二:
- 在
Fragment
中使用ButterKnife
框架
如果文章對(duì)您有一點(diǎn)幫助的話糯崎,希望您能點(diǎn)一下贊,您的點(diǎn)贊河泳,是我前進(jìn)的動(dòng)力
本文參考鏈接: