說說我自己常用的 Android 架構(gòu)

先列出一些常用的依賴挂签,想必看到下面的依賴大家也能明白接下來要講的是什么?

//所需依賴
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'io.reactivex.rxjava2:rxjava:2.0.5'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'

沒錯(cuò)盼产,就是老生常談的 MVP 模式饵婆。

retrofit2作為網(wǎng)絡(luò)請(qǐng)求,gson作為json解析器戏售。注意這里是用的最新的 rxjava2 和 jakewharton 大大開源的 retrofit2-rxjava2-adapter 作為橋接器侨核。最新的版本可以前往各自的 Github 上查看草穆。

mvp

基礎(chǔ)架構(gòu)

我個(gè)人比較喜歡在項(xiàng)目下新建一個(gè) Android library 的模塊,取名為core芹关,主要作用是負(fù)責(zé)網(wǎng)絡(luò)層和數(shù)據(jù)層续挟。像數(shù)據(jù)實(shí)體類,數(shù)據(jù)庫操作侥衬,SharedPreferences緩存诗祸,網(wǎng)絡(luò)請(qǐng)求都可以放在 core 模塊下,主要目的就是徹底將UI和數(shù)據(jù)層完全分開(物理層面上)轴总。

網(wǎng)絡(luò)模塊

需要一個(gè) RetrofitHelper 單例模塊支持直颅,主要是為 OkHttp 設(shè)置請(qǐng)求參數(shù)屬性和初始化 Api 接口服務(wù)。

OkHttp的參數(shù)設(shè)置

OkHttp上可以設(shè)置的參數(shù)很多怀樟,像緩存功偿,頭部信息,超時(shí)時(shí)間往堡,重連等信息都可以在 OkHttpClient 初始化設(shè)置時(shí)統(tǒng)一設(shè)置械荷。

private void initOkHttp() {
    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    
    //設(shè)置統(tǒng)一的請(qǐng)求頭部參數(shù)
    builder.addInterceptor(apikey);
    //設(shè)置緩存
    builder.addNetworkInterceptor(cacheInterceptor);
    builder.addInterceptor(cacheInterceptor);
    builder.cache(cache);
    //設(shè)置超時(shí)
    builder.connectTimeout(10, TimeUnit.SECONDS);
    builder.readTimeout(20, TimeUnit.SECONDS);
    builder.writeTimeout(20, TimeUnit.SECONDS);
    //錯(cuò)誤重連
    builder.retryOnConnectionFailure(true);
    okHttpClient = builder.build();
}

初始化項(xiàng)目 api 接口

一般來說,一個(gè)項(xiàng)目的網(wǎng)絡(luò)返回?cái)?shù)據(jù)都有統(tǒng)一的返回?cái)?shù)據(jù)虑灰,比如有一個(gè)定義好的返回碼 resultCode吨瞎,數(shù)據(jù)返回信息 resultInfo,以及最重要的數(shù)據(jù)對(duì)象 returnObject穆咐。所以我們需要一個(gè)類來進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)套接颤诀。

public class ApiResponse<T> {
    private int resultCode;
    private T returnObject;
    private Object ruturnInfo;

    // get 和 set 方法
    //...
}

定義好數(shù)據(jù)類型,就輪到 Retrofit 與網(wǎng)絡(luò)接口進(jìn)行聯(lián)動(dòng)对湃,首先需要一個(gè)能夠定義 api 接口的地方 Apis

public interface Apis {

    /**
     * 獲取啟動(dòng)頁圖片
     *
     * @return
     */
    @FormUrlEncoded
    @POST("getSplashImg")
    Observable<ApiResponse<SplashImageBean>> getStartImg(@Field("uid") String uid,@Field("size") String size);

    //其他的api
    ...
}

之后回到 RetrofitHelper 中初始化接口服務(wù)崖叫。

// 接口服務(wù)
apis = getApiService(HttpUtils.BASEURL, Apis.class);

// 接口服務(wù)初始化方法
private <T> T getApiService(String baseUrl, Class<T> clz) {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build();
    return retrofit.create(clz);
}

搭建 MVP 架構(gòu)

此方法是從 Google 的開源項(xiàng)目android-architecture 上的 todo?mvp?rxjava 篇演變而來。一樣需要定義 View 和 Presenter 接口以及Presenter的實(shí)現(xiàn)拍柒,只不過加了一層 RxPresenter 對(duì)Rxjava的優(yōu)化心傀,防止內(nèi)存的泄露。

MVP的基礎(chǔ)類

首先要為mvp模式定下基礎(chǔ)接口 BaseViewBasePresenter拆讯。
Presenter 需要綁定 View 才能回調(diào) View 里面的各種方法剧包,所以直接在類聲明的時(shí)候?qū)?View 綁定。
同理往果,View 里面需要一個(gè) Presenter 去處理數(shù)據(jù),故定義一個(gè) setPresenter() 方法來提醒(所以不設(shè)置也行)一铅。

public interface BaseView {
    void setPresenter();
}

public interface BasePresenter<T extends BaseView> {
    void attachView(T view);
    void detachView();
}

Rxjava 在 MVP 上的優(yōu)化

之前在寫 MoeMusic開源項(xiàng)目 的時(shí)候完全沒有考慮到 Rxjava 在與 Retrofit 結(jié)合請(qǐng)求網(wǎng)絡(luò)請(qǐng)求的時(shí)候會(huì)存在內(nèi)存泄露的問題陕贮,所以在這個(gè)模塊上利用 Rxjava 的訂閱和取消訂閱功能消除內(nèi)存泄露的問題。

/**
 * @author cpacm
 * @date 2017/2/26
 * @desciption 可取消訂閱的 rxpresenter,防止rxjava引起的內(nèi)存泄露
 */

public abstract class RxPresenter<T extends BaseView> implements BasePresenter<T> {
    protected T view;
    protected CompositeDisposable compositeDisposable;

    protected void unDisposable() {
        if (compositeDisposable != null) {
            compositeDisposable.clear();
        }
    }

    protected void addDisposable(Disposable disposable) {
        if (compositeDisposable == null) {
            compositeDisposable = new CompositeDisposable();
        }
        compositeDisposable.add(disposable);
    }

    @Override
    public void attachView(T view) {
        this.view = view;
    }

    @Override
    public void detachView() {
        this.view = null;
        unDisposable();
    }
}

原理很簡單潘飘,就是使用 CompositeDisposable 來訂閱 rxjava 發(fā)射的事件肮之,之后在 detachView() 解綁的時(shí)候取消訂閱掉缺。

MVP 的簡單使用

在使用前,我們先建一個(gè) BaseActivity 作為所有的Activity的基類戈擒,并將其生命周期與 MVP 模塊關(guān)聯(lián)起來眶明。

public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity implements BaseView {

    protected T presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setPresenter();
        if (presenter != null) {
            presenter.attachView(this);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenter != null) {
            presenter.detachView();
        }
    }
}

使用示例

需求:一個(gè) app 的啟動(dòng)頁,啟動(dòng)頁圖片來自服務(wù)器筐高。

SplashContract 中定義各個(gè)接口要回調(diào)的方法搜囱。

public interface SplashContract {

    interface View extends BaseView {
        void showSplash(SplashImageBean bean);
    }

    interface Presenter extends BasePresenter<View> {
        void getSplashData();
    }
}

SplashPresenter 中實(shí)現(xiàn)網(wǎng)絡(luò)的請(qǐng)求和view的回調(diào)

public class SplashPresenter extends RxPresenter<SplashContract.View> implements SplashContract.Presenter {

    private Apis zqswApis;

    public SplashPresenter() {
        zqswApis = RetrofitHelper.getInstance().getApis();
    }

    @Override
    public void getSplashData() {
        Disposable disposable = zqswApis.getStartImg("","")
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(new Consumer<ApiResponse<SplashImageBean>>() {
                    @Override
                    public void accept(ApiResponse<SplashImageBean> splashImageBean) throws Exception {
                        view.showSplash(splashImageBean.getReturnObject());
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e("cpacm", throwable.toString());
                    }
                });
        addDisposable(disposable);
    }
}

SplashActivity 實(shí)現(xiàn)UI完成整個(gè)需求

/**
 * @author cpacm
 * @date 2017/2/16
 * @desciption 啟動(dòng)界面
 */

public class SplashActivity extends BaseActivity<SplashPresenter> implements SplashContract.View {

    private ImageView bgView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        bgView = (ImageView) findViewById(R.id.background);
        presenter.getSplashData();
    }

    @Override
    public void setPresenter() {
        presenter = new SplashPresenter();
    }

    @Override
    public void showSplash(SplashImageBean bean) {
        Glide.with(this)
                .load(bean.getImageUrl())
                .into(bgView);
        Toast.makeText(this, bean.toString(), Toast.LENGTH_SHORT).show();
    }

}

項(xiàng)目源碼見下方鏈接

結(jié)論

整個(gè)框架小巧而精致,而且看起來也不怎么復(fù)雜柑土,個(gè)人項(xiàng)目或者小型的團(tuán)隊(duì)項(xiàng)目應(yīng)付起來應(yīng)該是綽綽有余了蜀肘,不過大型的項(xiàng)目應(yīng)該還需要擴(kuò)展或者采用其他的架構(gòu)來應(yīng)付繁瑣的需求。


MvpDemo源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末稽屏,一起剝皮案震驚了整個(gè)濱河市扮宠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狐榔,老刑警劉巖坛增,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異薄腻,居然都是意外死亡收捣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門被廓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坏晦,“玉大人,你說我怎么就攤上這事嫁乘±バ觯” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵蜓斧,是天一觀的道長仓蛆。 經(jīng)常有香客問我,道長挎春,這世上最難降的妖魔是什么看疙? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮直奋,結(jié)果婚禮上能庆,老公的妹妹穿的比我還像新娘。我一直安慰自己脚线,他們只是感情好搁胆,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般渠旁。 火紅的嫁衣襯著肌膚如雪攀例。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天顾腊,我揣著相機(jī)與錄音粤铭,去河邊找鬼。 笑死杂靶,一個(gè)胖子當(dāng)著我的面吹牛梆惯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伪煤,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼加袋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了抱既?” 一聲冷哼從身側(cè)響起职烧,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎防泵,沒想到半個(gè)月后蚀之,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捷泞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年足删,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锁右。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡失受,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咏瑟,到底是詐尸還是另有隱情拂到,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布码泞,位于F島的核電站兄旬,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏余寥。R本人自食惡果不足惜领铐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宋舷。 院中可真熱鬧绪撵,春花似錦、人聲如沸祝蝠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至改艇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坟岔,已是汗流浹背谒兄。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留社付,地道東北人承疲。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像鸥咖,于是被迫代替她去往敵國和親燕鸽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容