MVP抽取

一俊啼、為什么要抽取左医?

  • 每一個(gè)頁(yè)面都用到同樣的內(nèi)容授帕,每次都寫(xiě)同樣的代碼,浪費(fèi)浮梢;
  • 規(guī)定代碼的編寫(xiě)規(guī)范跛十;
  • 多人開(kāi)發(fā)維護(hù)變得簡(jiǎn)單。

二秕硝、抽取那些內(nèi)容

  • 通用的操作

三芥映、設(shè)計(jì)模式

  • 模板設(shè)計(jì)模式(讓子類(lèi)的工作模式都按照父類(lèi)的執(zhí)行)
  • 單例設(shè)計(jì)模式

四、具體抽取

  1. MVP抽取概述圖


    image.png
  2. 網(wǎng)絡(luò)抽取
  • ①創(chuàng)建網(wǎng)絡(luò)管理類(lèi)远豺,使用雙檢索單例設(shè)計(jì)模式獲取對(duì)象
public class HttpUtil {
    private static HttpUtil httpUtil;

    public static HttpUtil getHttpUtil() {
        if (httpUtil == null) {
            synchronized (HttpUtil.class) {
                if (httpUtil == null) {
                    httpUtil = new HttpUtil();
                }
            }
        }
        return httpUtil;
    }
}
  • ②在網(wǎng)絡(luò)管理器的構(gòu)造中獲取Retrofit的builder對(duì)象
public class HttpUtil {
    private final Retrofit.Builder builder;
    private HttpUtil() {
        builder = new Retrofit.Builder()
                .client(getHttpClient())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create());
    }
}
  • ③使用泛型獲取ApiService對(duì)象
//一個(gè)返回T類(lèi)型的方法奈偏,T的類(lèi)型的泛型
public class HttpUtil {
    public <T> T getService(String baseurl, Class<T> mClass) {
        return builder.baseUrl(baseurl).build().create(mClass);
    }
}
  • ④自定義OKHTTPClient,設(shè)置超時(shí)躯护、請(qǐng)求頭惊来、攔截器等
public class HttpUtil {
     private OkHttpClient getHttpClient() {
        //設(shè)置本地緩存文件
        File file = new File(Constants.PATH_CACHE);
        //設(shè)置緩存
        Cache cache = new Cache(file, 1024 * 1024 * 50);

        //設(shè)置日志攔截器
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        if (BuildConfig.DEBUG) {
            httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
        } else {
            httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.NONE);
        }

        return new OkHttpClient.Builder()
                .cache(cache)
                .addInterceptor(httpLoggingInterceptor)
                .addNetworkInterceptor(new MyCacheIntercepter())
                .addInterceptor(new MyCacheIntercepter())
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true)
                .build();
    }

    //網(wǎng)絡(luò)攔截器
    public class MyCacheIntercepter implements Interceptor {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();

            if (!isNetworkAvailable(GeekApplication.getApplication())) {
                request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();

            }

            Response originalResponse = chain.proceed(request);

            if (isNetworkAvailable(GeekApplication.getApplication())) {
                int maxAge = 0;
                return originalResponse.newBuilder()
                        .removeHeader("Pragma")
                        .header("Cache-Control", "public ,max-age=" + maxAge)
                        .build();
            } else {
                int maxStale = 15 * 60;
                return originalResponse.newBuilder()
                        .removeHeader("Pragma")
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                        .build();
            }
        }
    }

    public static boolean isNetworkAvailable(Context context) {
        if (context != null) {
            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo info = cm.getActiveNetworkInfo();
            if (info != null) {
                return info.isAvailable();
            }
        }
        return false;
    }
}
  • ⑤抽取線程切換
public class RxUtil {
    /**
     * <T ,T > T轉(zhuǎn)換前的對(duì)象類(lèi)型, T 轉(zhuǎn)換后的對(duì)象類(lèi)型
     *
     * @param <T>
     * @return
     */
    public static <T> ObservableTransformer<T, T> rxObservableTransformer() {
        return new ObservableTransformer<T, T>() {
            @Override
            public ObservableSource<T> apply(Observable<T> upstream) {
                return upstream.observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io());
            }
        };
    }
}
  • ⑥觀察者抽取
public abstract class BaseObserver<T> implements Observer<T> {
    @Override
    public void onSubscribe(Disposable d) {
        Log.e("TAG", "訂閱成功");
    }

    @Override
    public void onError(Throwable e) {
        onFail("onError:" + e.getMessage());
    }

    @Override
    public void onComplete() {
        Log.e("TAG", "請(qǐng)求完成");
    }

    @Override
    public void onNext(T value) {
        onSuccess(value);
    }

    public abstract void onFail(String error);

    protected abstract void onSuccess(T t);
}
  • ⑦使用
HttpManager.getHttpManager().getApiService(ApiService.baseLoginUrl, ApiService.class)
                .login(name, pwd)
                .compose(RxUtil.<LoginBean>rxTransform())
                .subscribe(new BaseObserver<LoginBean>() {
                    @Override
                    public void onSucess(LoginBean loginBean) {
                        if (loginBean.getErrorCode() == 0) {
                            callBack.onSucess(loginBean);
                        } else {
                            callBack.onFail(loginBean.getErrorMsg());
                        }
                    }

                    @Override
                    public void onFail(String error) {
                        callBack.onFail(error);
                    }
                });
  1. BaseActivity抽取
public abstract class BaseActivity<P extends BasePresenter, V extends BaseView> extends AppCompatActivity {
    protected P mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());
        ButterKnife.bind(this);
        mPresenter = initPresenter();
        if (mPresenter != null) {
            mPresenter.setmView(initMvpView());
        }
        initView();//有些子類(lèi)需要棺滞,有些不需要
        initData();//有些子類(lèi)需要唁盏,有些不需要内狸,僅僅用普通方法即可
        initListener();
    }

    protected abstract void initListener();

    protected abstract void initData();

    protected abstract void initView();

    protected abstract V initMvpView();

    protected abstract P initPresenter();

    protected abstract int getLayoutId();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mPresenter.onDestory();
        mPresenter = null;
    }
}
  • BaseModel抽取
public class BaseModel {
    public CompositeDisposable mCompositeDisposable;

    public void onDestory() {
        //切斷所有的Disposable對(duì)象
        if (mCompositeDisposable != null){
            mCompositeDisposable.clear();
        }
    }

    public void addDisposable(Disposable d){
        if (mCompositeDisposable == null){
            mCompositeDisposable = new CompositeDisposable();
        }
        mCompositeDisposable.add(d);
    }
}
  • BaseView 抽取
public interface BaseView<T> {
    void onSucess(T t);

    void onFail(String error);
}
  • BasePresenter抽取
public abstract class BasePresenter<V extends BaseView> {
    protected V mView;
    private ArrayList<BaseModel> mModels = new ArrayList<>();

    public BasePresenter() {
        initMdel();
    }

    protected abstract void initMdel();

    public void setmView(V mView) {
        this.mView = mView;
    }

    public void onDestory() {
        //打斷P層和V層的聯(lián)系,
        mView = null;
        //掐斷網(wǎng)絡(luò)請(qǐng)求
        if (mModels.size() > 0) {
            for (BaseModel model : mModels) {
                model.onDestory();
            }
            mModels.clear();
        }
    }

    public void addModel(BaseModel model) {
        mModels.add(model);
    }
}
  • callback抽取
public interface BaseCallBack<T> {
    void onSuccess(T t);
    void onFail(String error);
}

MVP抽取案例使用(登錄)

  • M層
public class LoginModel extends BaseModel {
    public void login(String name, String password, BaseCallBack<String, String> callBack) {
        if (true) {
            callBack.onSuccess("登錄成功");
        } else {
            callBack.onFail("登錄失敗");
        }
    }
}
  • P層
public class LoginPresenter extends BasePresenter<LoginView, LoginModel> implements BaseCallBack<String, String> {
    public void login(String name, String pwd) {
        if (model != null) {
            model .login(name, pwd, this);
        }
    }

    @Override
    public void onSuccess(String s) {
        if (view != null) {
            view .onSuccess(s);
        }
    }

    @Override
    public void onFail(String s) {
        if (view  != null) {
            view .onFail(s);
        }
    }
}
  • V層
public class MainActivity extends BaseActivity<LoginPresenter, LoginView, LoginModel> implements LoginView {

    @BindView(R.id.btn_login)
    Button btnLogin;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected LoginView initMvpView() {
        return this;
    }

    @Override
    protected LoginModel initMvpModel() {
        return new LoginModel();
    }

    @Override
    protected LoginPresenter initPresnter() {
        return new LoginPresenter();
    }

    @Override
    public void onSuccess(String s) {
        Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onFail(String s) {
        Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
    }

    @OnClick({R.id.btn_login})
    public void onViewClicked(View view) {
                baseP.login("", "");   
    }

MVP與普通的分離(不需要mvp的頁(yè)面)

  • 不需要MVP
public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());
        ButterKnife.bind(this);
        initMvp();
        initView();//有些子類(lèi)需要,有些不需要
        initData();//有些子類(lèi)需要厘擂,有些不需要,僅僅用普通方法即可
        initListener();
    }

   private void initMvp() {}

    protected abstract int getLayoutId();
    
    private void initListener() {}

    private void initData() {}

    protected void initView() {}
}
  • 需要mvp
public abstract class BaseMVPActivity<P extends BasePresenter, V extends BaseView, M extends BaseModel> extends BaseActivity {
    protected P presenter;

    @Override
    public void initMvp() {
        super.initMvp();

        presenter = initPresnter();
        if (presenter != null) {
            presenter.addView(initMvpView());
            presenter.addModel(initMvpModel());
        }
    }

    protected abstract V initMvpView();

    protected abstract M initMvpModel();

    protected abstract P initPresnter();
}

Fragment抽取

  • 沒(méi)有MVP抽取基類(lèi)
public abstract class BaseFragment extends Fragment {

    private Unbinder unbinder;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(getLayoutId(), container, false);
        unbinder = ButterKnife.bind(this, view);
        initMvp();
        initView();
        initData();
        initListener();
        return view;
    }

    protected void initMvp() {

    }

    protected void initListener() {

    }

    protected void initData() {

    }

    protected void initView() {

    }

    protected abstract int getLayoutId();

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        unbinder.unbind();
    }
}
  • 有MVP抽取
public abstract class BaseMvpFragment<P extends BasePresenter, V extends BaseView, M extends BaseModel> extends BaseFragment {
    protected P presenter;

    @Override
    protected void initMvp() {
        super.initMvp();
        presenter = initPresnter();
        if (presenter != null) {
            presenter.addView(initMvpView());
            presenter.addModel(initMvpModel());
        }
    }

    protected abstract V initMvpView();

    protected abstract M initMvpModel();

    protected abstract P initPresnter();
}

應(yīng)用崩潰問(wèn)題解決

  • 背景:在發(fā)動(dòng)網(wǎng)絡(luò)請(qǐng)求的時(shí)候锰瘸,退出當(dāng)前activity
  • 沖突:此時(shí)如果回到主線程更新UI刽严,APP會(huì)崩潰
  • 解決:當(dāng)activity退出時(shí),調(diào)用DisPosable.dispose()切斷觀察者和被觀察者的連接避凝,使得觀察者無(wú)法收到事件&響應(yīng)事件舞萄;當(dāng)出現(xiàn)多個(gè)disposable時(shí),采用Rxjava內(nèi)置容器CompositeDisposable進(jìn)行統(tǒng)一管理
CompositeDisposable compositeDisposable = new CompositeDisposable()管削;
//添加disposable到CompositeDisposable容器
compositeDisposable .add();
//清空CompositeDisposable容器倒脓,在父類(lèi)的baseModel中調(diào)用
compositeDisposable .clear();
  • 在BaseModel中
public abstract class BaseModel {
    
    //用于統(tǒng)一切斷觀察者與被觀察者關(guān)系
    CompositeDisposable compositeDisposable = new CompositeDisposable();

    //切斷,P層調(diào)用
    public void destroy() {
        compositeDisposable.clear();
    }
}
  • 在basePresenter中
public class BasePresenter<V extends BaseView, M extends BaseModel> {

    //創(chuàng)建一個(gè)集合含思,用于存放model
    protected ArrayList<BaseModel> list = new ArrayList<BaseModel>();

    protected V view;
    protected M model;

    public void addView(V v) {
        this.view = v;
    }

    public void addModel(M m) {
        this.model = m;
        //添加model
        list.add(m);
    }

    public void destroy() {
        //如果view不為空崎弃,頁(yè)面關(guān)閉,強(qiáng)制置空含潘,否則內(nèi)存泄漏
        if (view != null) {
            view = null;
        }

        //如果頁(yè)面關(guān)閉饲做,切斷網(wǎng)絡(luò)請(qǐng)求的觀察者與被觀察者的關(guān)系
        if (list.size() > 0) {
            for (BaseModel baseModel : list) {
                baseModel.destroy();
            }
        }

        //如果model不為空,置空
        if (model != null) {
            model = null;
        }
    }
}
  • 在BaseMVPActivity中
  /**
     * 頁(yè)面關(guān)閉時(shí)
     * 切斷連接
     * 內(nèi)存回收
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenter != null) {
            presenter.destroy();
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末遏弱,一起剝皮案震驚了整個(gè)濱河市盆均,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌漱逸,老刑警劉巖泪姨,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異饰抒,居然都是意外死亡肮砾,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)循集,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)唇敞,“玉大人,你說(shuō)我怎么就攤上這事咒彤〗幔” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵镶柱,是天一觀的道長(zhǎng)旷档。 經(jīng)常有香客問(wèn)我,道長(zhǎng)歇拆,這世上最難降的妖魔是什么鞋屈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任范咨,我火速辦了婚禮,結(jié)果婚禮上厂庇,老公的妹妹穿的比我還像新娘渠啊。我一直安慰自己,他們只是感情好权旷,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布替蛉。 她就那樣靜靜地躺著,像睡著了一般拄氯。 火紅的嫁衣襯著肌膚如雪躲查。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,021評(píng)論 1 291
  • 那天译柏,我揣著相機(jī)與錄音镣煮,去河邊找鬼。 笑死鄙麦,一個(gè)胖子當(dāng)著我的面吹牛典唇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播黔衡,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蚓聘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了盟劫?” 一聲冷哼從身側(cè)響起夜牡,我...
    開(kāi)封第一講書(shū)人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎侣签,沒(méi)想到半個(gè)月后塘装,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡影所,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蹦肴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猴娩。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阴幌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出卷中,到底是詐尸還是另有隱情矛双,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布蟆豫,位于F島的核電站议忽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏十减。R本人自食惡果不足惜栈幸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一愤估、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧速址,春花似錦玩焰、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至闹炉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間润樱,已是汗流浹背渣触。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留壹若,地道東北人嗅钻。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像店展,于是被迫代替她去往敵國(guó)和親养篓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350