基于android MVP的Rss閱讀器

最近在學(xué)習(xí)Android的MVP模式姑曙,就是MVP(Model-View-Presenter)伤靠,網(wǎng)上大部分都是比較小的例子啼染,所以想拿個小項目練手焕梅,于是采用了基于Rss feed的閱讀器。

  • 1.Rss feeds是什么徒欣?
    是一種根據(jù)XML特性所制定的格式打肝,將網(wǎng)頁內(nèi)容抽取出來,讀者訂閱RSS Feed争便,即可直接取得有興趣的資訊断医。簡單來說就是一種xml。

  • 2.MVP是什么?
    解決的問題: 不讓activity承接太多任務(wù) 斩启;使得Model和View層解耦醉锅,職責(zé)劃分明顯硬耍。結(jié)構(gòu)清晰。(感覺說了跟白說一樣)

  • 1.Model 提供數(shù)據(jù)

  • 2.View 負(fù)責(zé)顯示

  • 3.Presenter 邏輯的處理狸窘,作為Model和view的中間者坯认,從Model獲得數(shù)據(jù)然后控制view顯示.

  • 3.整個結(jié)構(gòu):

Paste_Image.png

具體效果:

Paste_Image.png
  • 4.怎么獲取Rss feeds數(shù)據(jù)鹃操?
    我在這里使用的是volley + 自定義的xmlRequest荆隘,在文章底部有附源碼,主要寫大概獲取和使用方式:
    首先自定義一個接口:
public interface RssLoadListener {   
    void onLoadSuccess(List<RssInfo> rssInfoList);//獲取成功
    void onLoadFailed();//獲取失敗
    void onNetWorkFailed();//網(wǎng)絡(luò)錯誤
    void onBeginLoad();//開始任務(wù)
}

讓RssLoad下載任務(wù)時候?qū)崿F(xiàn)這個接口并作為參數(shù)傳進(jìn)去, 有了這個接口晶渠,我們在哪里需要下載數(shù)據(jù)的時候就可以這么應(yīng)用:

new RssLoadModel(context,RSS_URL).excute(new RssLoadListener() {

            @Override
            public void onBeginLoad() {
                view.showLoading();//剛開始下載的操作
            }

            @Override
            public void onLoadSuccess(List<RssInfo> rssInfoList) {
                //下載成功后要做什么
                Log.d(TAG, "onLoadSuccess: ");
                view.hideLoading();
                view.showResult(rssInfoList);
            }

            @Override
            public void onLoadFailed() {
                //若失敗后的操作
                view.hideLoading();
                view.showError("更新失敗");
            }

            @Override
            public void onNetWorkFailed() {
                view.showError("網(wǎng)絡(luò)不可用");
                view.hideLoading();
            }
        });

RssLoadModel是一個負(fù)責(zé)下載的類,里面的excute方法負(fù)責(zé)下載褒脯,并在下載過程中調(diào)用loadListener的方法,比如下載成功就調(diào)用onLoadSuccess(),至于里面成功后應(yīng)該怎么做到涂,就由你來實現(xiàn)践啄,這樣就達(dá)到了將狀態(tài)和結(jié)果傳遞出去的目的沉御。

public void excute(final RssLoadListener loadListener){
        if(!NetworkUtils.isNetworkAvailable(context)){
            loadListener.onNetWorkFailed();//網(wǎng)絡(luò)失敗
            return;
        }
        loadListener.onBeginLoad();
        XMLRequest xmlRequest = new XMLRequest(Constants.RSS_URL,
                new Response.Listener<XmlPullParser>() {
                    @Override
                    public void onResponse(XmlPullParser response) {
                        parserXML(response);
                        loadListener.onLoadSuccess(rssInfos);
                        //調(diào)用下載成功,將結(jié)果傳遞出去
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e("TAG", error.getMessage(), error);
                loadListener.onLoadFailed();//失敗
            }
        });
        mQueue.add(xmlRequest);
    }

XMLRequest 是繼承volley.Request的自定義類吠裆,volley是一個方便Android應(yīng)用網(wǎng)絡(luò)操作框架,默認(rèn)沒有XML的request衩婚,(寶寶不開心):

public class XMLRequest extends Request<XmlPullParser> {

    private final Response.Listener<XmlPullParser> mListener;

    public XMLRequest(int method, String url, Response.Listener<XmlPullParser> listener,
                      Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }

    public XMLRequest(String url, Response.Listener<XmlPullParser> listener, Response.ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        //需要加非春,否則獲取不到數(shù)據(jù)
        HashMap<String, String> headers = new HashMap<String, String>();
        headers.put("Content-Type", "application/xml; charset=utf-8");
        return headers;
    }
    @Override
    protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) {
        try {
            String xmlString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers));
            Log.d("tag",xmlString);
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = factory.newPullParser();
            xmlPullParser.setInput(new StringReader(xmlString));
            return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (XmlPullParserException e) {
            return Response.error(new ParseError(e));
        }
    }

    @Override
    protected void deliverResponse(XmlPullParser response) {
        mListener.onResponse(response);
    }
}
  • 5.mvp

VIew

  • MvpView類是個接口
  public interface MvpView {}
  • RssView 繼承MvpView接口奇昙,具體的View類需要實現(xiàn)下面的方法敌完,那么presenter想要控制具體的View類操作滨溉,就可以直接調(diào)用showLoading()等方法,這也是針對抽象編程闽撤。
public interface RssView extends MvpView {
    /**
     * 顯示loading下載視圖
     */
    void showLoading();

    /**
     *隱藏loading下載視圖
     */
    void hideLoading();

    /**
     * 顯示錯誤信息
     * @param msg
     */
    void showError(String msg);

    /**
     * 顯示結(jié)果
     * @param rssInfos
     */
    void showResult(List<RssInfo> rssInfos);

    /**
     * @param title
     */
    void showTitle(String title);
}
  • 具體的view類脯颜,這里使用Activity作為view類,現(xiàn)在很多都將fragment作為view闸餐,將activity控制生命周期舍沙。
public class MainActivity extends BaseActivty implements RssView

需要實現(xiàn)下列方法,實現(xiàn)這么多方法給誰調(diào)用呢壹无?當(dāng)然是Presenter了和媳。

@Override
    public void showLoading() {//顯示下載
        progressWheel.setVisibility(View.VISIBLE);
        swipeRefreshLayout.setVisibility(View.GONE);
        ValueAnimator progressFadeInAnim = ObjectAnimator.ofFloat(progressWheel, "alpha", 0, 1, 1);
        progressFadeInAnim.start();
    }

    @Override
    public void hideLoading() {//隱藏下載
        progressWheel.setVisibility(View.GONE);
        swipeRefreshLayout.setVisibility(View.VISIBLE);
        swipeRefreshLayout.setRefreshing(false); // close refresh animator
        ValueAnimator progressFadeInAnim = ObjectAnimator.ofFloat(progressWheel, "alpha", 1, 0, 0);
        progressFadeInAnim.start();
    }

    @Override
    public void showError(String msg) {//顯示錯誤信息
        new SweetAlertDialog(this, SweetAlertDialog.ERROR_TYPE)
                .setTitleText("Oops...")
                .setContentText(msg)
                .show();
    }

    @Override
    public void showResult(List<RssInfo> rssInfos) {//顯示結(jié)果
        rssAdapter.setRssInfos(rssInfos);
    }

    @Override
    public void showTitle(String title) {//顯示標(biāo)題
        ((TextView)findViewById(R.id.toolbar_title)).setText(title);
    }

Presenter

MvpPresenter 接口留瞳,里面有兩個方法attachView和detachView骚秦,負(fù)責(zé)將MvpView 綁定在MvpPresenter和解除綁定;例如在view類里面調(diào)用
presenter.attachView(this);
將自己傳進(jìn)去作箍,這樣 presenter就擁有了view,也就可以隨心所欲地控制view了荧止,Presenter通過接口進(jìn)行對View的操作阶剑。

public interface MvpPresenter<V extends MvpView> {
  /**
   * 將 MvpView 綁定在MvpPresenter
   * @param view
   */
  public void attachView(V view);

  /**
   * @param retainInstance
   * 解除綁定
   */
  public void detachView(boolean retainInstance);
}

RssPresenter類

/**
 * Created by cheng on 16-7-24.
 */
public class RssPresenter implements MvpPresenter<RssView> {
    private final static String TAG = "RssPresenter";

    private RssView view;
    private Context context;

    public RssPresenter(Context context) {
        this.context = context;
    }

    public void startLoadTask() {//開始下載
        if (view == null) {
            Log.w(TAG, "please attach view first.");
            return;
        }

        new RssLoadModel(context,RSS_URL).excute(new RssLoadListener() {
            @Override
            public void onBeginLoad() {
                view.showLoading();
            }

            @Override
            public void onLoadSuccess(List<RssInfo> rssInfoList) {
                Log.d(TAG, "onLoadSuccess: ");
                view.hideLoading();
                view.showResult(rssInfoList);
            }

            @Override
            public void onLoadFailed() {
                view.hideLoading();
                view.showError("更新失敗");
            }

            @Override
            public void onNetWorkFailed() {
                view.showError("網(wǎng)絡(luò)不可用");
                view.hideLoading();
            }
        });
    }

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

    @Override
    public void detachView(boolean retainInstance) {
        // TODO

    }
}
  • 6.以上只是一部分源碼素邪,具體的源碼:
    https://github.com/ChyengJason/RssReader

  • 7.不足
    mvp clean模式
    對mvp理解還比較淺顯猪半,繼續(xù)學(xué)習(xí)。沽甥。乏奥。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市歇式,隨后出現(xiàn)的幾起案子胡野,更是在濱河造成了極大的恐慌,老刑警劉巖龙巨,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熊响,死亡現(xiàn)場離奇詭異汗茄,居然都是意外死亡,警方通過查閱死者的電腦和手機递览,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進(jìn)店門绞铃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嫂侍,“玉大人,你說我怎么就攤上這事菲盾”云埽” “怎么了?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長南捂。 經(jīng)常有香客問我溺健,道長,這世上最難降的妖魔是什么剖膳? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮甸饱,結(jié)果婚禮上仑濒,老公的妹妹穿的比我還像新娘墩瞳。我一直安慰自己,他們只是感情好热凹,可當(dāng)我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布瞭吃。 她就那樣靜靜地躺著歪架,像睡著了一般霹陡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上攒霹,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天催束,我揣著相機與錄音伏社,去河邊找鬼。 笑死速妖,一個胖子當(dāng)著我的面吹牛聪黎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锦秒,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼旅择,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脖咐?” 一聲冷哼從身側(cè)響起汇歹,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤产弹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胶果,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斤斧,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡撬讽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了游昼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甘苍。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖烘豌,靈堂內(nèi)的尸體忽然破棺而出载庭,到底是詐尸還是另有隱情,我是刑警寧澤廊佩,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布囚聚,位于F島的核電站,受9級特大地震影響罐寨,放射性物質(zhì)發(fā)生泄漏靡挥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一鸯绿、第九天 我趴在偏房一處隱蔽的房頂上張望跋破。 院中可真熱鬧簸淀,春花似錦、人聲如沸毒返。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拧簸。三九已至劲绪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盆赤,已是汗流浹背贾富。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牺六,地道東北人颤枪。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像淑际,于是被迫代替她去往敵國和親畏纲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,455評論 2 359

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