(新瓶舊酒)谷歌官方MVP項(xiàng)目學(xué)習(xí)--淺入源碼

人笨學(xué)的慢啊。。抓緊學(xué)習(xí)MVP

網(wǎng)上MVP的學(xué)習(xí)資料也是多如牛毛,來看看谷歌爸爸是怎么教我們MVP的吧

項(xiàng)目介紹


Google把這個項(xiàng)目命名為:Android架構(gòu)藍(lán)圖。

這個項(xiàng)目也是金誠先生推薦的2017年Android百大框架排行榜中的一個

android-architecture
一句話介紹:google提供的Android當(dāng)下各種基本框架
上榜理由:看完它彤悔,mvp,mvvm都將入切瓜砍菜索守,秋風(fēng)掃落葉一般...
github https://github.com/googlesamples/android-architecture
作者:google

項(xiàng)目的目的是通過展示各種架構(gòu)app的不同方式來幫助開發(fā)者解決架構(gòu)問題晕窑。項(xiàng)目中通過不同的架構(gòu)概念及方式實(shí)現(xiàn)了功能相同的app。你可以用示例來當(dāng)做參考卵佛,或是干脆拿來當(dāng)做創(chuàng)建app項(xiàng)目的基礎(chǔ)杨赤。項(xiàng)目中,希望大家能把關(guān)注點(diǎn)集中到代碼結(jié)構(gòu)截汪、整體架構(gòu)疾牲、可測試性、可維護(hù)性這四個方面衙解。當(dāng)然實(shí)現(xiàn)app有很多種方式阳柔,千萬不要把它當(dāng)做定式。

項(xiàng)目中有哪些示例


目前已經(jīng)穩(wěn)定的示例有

  • todo-mvp(mvp基礎(chǔ)架構(gòu)示例)
  • todo?mvp?clean (基于mvp基礎(chǔ)架構(gòu)項(xiàng)目蚓峦,使用了clean架構(gòu)的概念)
  • todo?mvp?dagger (基于mvp基礎(chǔ)架構(gòu)項(xiàng)目舌剂,使用了dagger2進(jìn)行依賴注入)
  • todo?mvp?rxjava (基于mvp基礎(chǔ)架構(gòu)項(xiàng)目济锄,使用了rxjava2進(jìn)行依賴注入)
  • todo-mvp-databinding(基于mvp基礎(chǔ)架構(gòu)項(xiàng)目,使用了數(shù)據(jù)綁定組件)

仍在進(jìn)展中的示例有

  • dev?todo?mvp?tablet (Adds a master and detail view for tablets.)
  • dev?todo?mvvm?rxjava (Based on the todo-rxjava sample, this version incorporates the Model?View?ViewModel pattern.)
  • dev?todo?mvvm?live (Uses lifecycle-aware Architecture Components (except Room), and the Data Binding library with an MVVM architecture.)
  • dev?todo?mvp?room (Uses Room, the persistence library from the Architecture Components, as a local data source with an MVP architecture.)
  • dev-todo-mvp-kotlin (Conversion of todo-mvp to Kotlin.)

顯而易見的霍转,本渣只能從todo-mvp(mvp基礎(chǔ)架構(gòu)示例)項(xiàng)目中探索谷歌爸爸告訴我們的荐绝,對MVP架構(gòu)的最本源的揭示。

todo-mvp

應(yīng)用程序的名字是todo-mvp(待辦清單-mvp)谴忧,為此項(xiàng)目中的其他示例提供了基礎(chǔ)很泊。該樣本旨在:

  • 提供基本的Model-View-Presenter(MVP)架構(gòu)角虫,而不使用任何架構(gòu)框架沾谓。
  • 作為比較和對比本項(xiàng)目其他樣本的參考點(diǎn)。

todo-mvp示例使用以下依賴關(guān)系:

  • 常見的Android支持庫 - com.android.support戳鹅。*命名空間中的軟件包提供向后兼容性和其他功能均驶。
  • Android測試支持庫 - 用于支持UI測試的框架,使用Espresso和AndroidJUnitRunner枫虏。
  • Mockito - 用于實(shí)施單元測試的框架妇穴。
  • Guava - 谷歌的一組核心庫,通常用于Android應(yīng)用程序隶债。

設(shè)計(jì)app


該應(yīng)用程序由四個UI頁面組成:

  • Tasks - 用于管理任務(wù)列表腾它。
  • TaskDetail - 用于讀取或刪除任務(wù)。
  • AddEditTask - 用于創(chuàng)建或編輯任務(wù)死讹。
  • Statistics - 顯示與任務(wù)相關(guān)的統(tǒng)計(jì)信息瞒滴。

在這個應(yīng)用程序以及其他基于它的版本中,每個功能頁面都使用以下類和接口:

  • 一個Activity用來管理fragment和presenter的創(chuàng)建;
  • 一個定義View和Presenter接口的Contract接口;
  • 一個實(shí)現(xiàn)了View接口的Fragment;
  • 一個實(shí)現(xiàn)了Presenter接口的presenter.

曾經(jīng)的架構(gòu)

追溯到2012年我們的代碼庫使用的是基本結(jié)構(gòu)赞警,那個時候我們沒有使用任何第三方網(wǎng)絡(luò)類庫妓忍,而且AsyncTask也是我們的好朋友。當(dāng)時的架構(gòu)可以大致表示為下圖愧旦。


代碼被劃分為兩層結(jié)構(gòu):

  • Data Layer(數(shù)據(jù)層)負(fù)責(zé)從REST API或者持久數(shù)據(jù)存儲區(qū)檢索和存儲數(shù)據(jù)世剖;
  • View Layer(視圖層)的職責(zé)是處理并將數(shù)據(jù)展示在UI上。

APIProvider提供了一些方法笤虫,使Activity和Fragment能夠很容易的實(shí)現(xiàn)與REST API的數(shù)據(jù)交互旁瘫。這些方法使用URLConnection和AsyncTask在一個單獨(dú)的線程內(nèi)執(zhí)行網(wǎng)絡(luò)請求,然后通過回調(diào)將結(jié)果返回給Activity琼蚯。

按照同樣的方式境蜕,CacheProvider 所包含的方法負(fù)責(zé)從SharedPreferences和SQLite數(shù)據(jù)庫檢索和存儲數(shù)據(jù)。同樣使用回調(diào)的方式凌停,將結(jié)果傳回Activity粱年。

存在的問題:

  • 使用這種結(jié)構(gòu),最主要的問題在于View Layer持有太多的職責(zé)罚拟。Activitty和Fragment變得非常龐大并且難以維護(hù)台诗。

MVP架構(gòu)

MVP 是如何建立起關(guān)系來的完箩?

首先,M 只在 P 中使用拉队,與 V 無關(guān)弊知,因此 M 只要傳入 P 中即可。
P 與 V 之間的關(guān)系是這樣的:V 和 P 互相保存對方的實(shí)例粱快。V 在需要進(jìn)行數(shù)據(jù)操作邏輯的時候不自己做秩彤,而是交給 P 來做,P 完成之后調(diào)用 V 中的方法實(shí)現(xiàn)界面更新事哭。

所以PRESENTER 的作用是承擔(dān)業(yè)務(wù)邏輯和相應(yīng)的UI邏輯漫雷。 而View層幾乎沒有任何邏輯操作,它只是將presenter 的命令轉(zhuǎn)換為UI操作鳍咱,并且監(jiān)聽用戶的操作降盹,然后傳遞給Presenter 。

可以看到這里的View指的是Fragment谤辜,一是因?yàn)镚oogle推薦使用Fragment而不是Activity來顯示內(nèi)容蓄坏,二是Fragment作為View既能解決layout作為View導(dǎo)致的雞肋,也不會使Activity功能太過膨脹(這里Activity是一個總體的Controller丑念,讓Fragment和Presenter進(jìn)行連接)
左邊那塊就是Model了涡戳,Presenter先到內(nèi)存中的緩存進(jìn)行查詢,如果沒有脯倚,才到本地?cái)?shù)據(jù)源或者遠(yuǎn)程數(shù)據(jù)源請求

項(xiàng)目包結(jié)構(gòu)


除BaseView渔彰、BasePresenter兩個接口,其他package都以業(yè)務(wù)功能來劃分的:
addedittask —— 添加任務(wù)
data —— 數(shù)據(jù)源
statistics —— 任務(wù)統(tǒng)計(jì)
taskdetail —— 任務(wù)詳情
tasks —— 任務(wù)列表
util —— 工具類

項(xiàng)目MVP實(shí)現(xiàn)方式

這節(jié)我們就具體來看官方示例到底是如何實(shí)現(xiàn)mvp的挠将。這里我們先看下總體的輪廓胳岂,關(guān)于項(xiàng)目中業(yè)務(wù)代碼我們僅列出了添加任務(wù)頁(addedittask )的相關(guān)類,其他業(yè)務(wù)代碼類似舔稀。(手繪乳丰,諸兄勿棄)

基類

Presenter基類:

public interface BasePresenter {
    void start();
}

// 例子中這個start()方法都在Fragment的onResume()中調(diào)用,作用是presenter開始獲取數(shù)據(jù)并調(diào)用view中方法改變界面顯示内贮。

@Override
    public void onResume() {
        super.onResume();
        mPresenter.start();
    }

View基類:

public interface BaseView<T> {
    void setPresenter(T presenter);
}

BaseView中含方法setPresenter产园,該方法作用是在將presenter實(shí)例傳入view中,其調(diào)用時機(jī)是presenter實(shí)現(xiàn)類的構(gòu)造函數(shù)中夜郁。

Contract 契約類

不同于其他的MVP項(xiàng)目什燕,官方的MVP架構(gòu)中都定義有xxContract契約類,把P層和V層的接口統(tǒng)一寫在契約類中竞端,能夠更清晰的看到在Presenter層和View層中有哪些功能屎即,方便我們以后的維護(hù)。

public interface AddEditTaskContract {

    interface View extends BaseView<Presenter> {

        void showEmptyTaskError();

        void showTasksList();
        //設(shè)置標(biāo)題
        void setTitle(String title);

        void setDescription(String description);

        boolean isActive();
    }

    interface Presenter extends BasePresenter {

        void saveTask(String title, String description);

        void populateTask();

        boolean isDataMissing();
    }
}

addedittask功能模塊分析


來具體看一個模塊

AddEditTaskActivity —— Activity
AddEditTaskContract —— 在這里定義了兩個子接口View和Presenter,和他們的方法技俐。實(shí)現(xiàn)了BaseView乘陪、BasePresenter兩個接口
AddEditTaskFragment —— 實(shí)現(xiàn)AddEditTaskContract.View接口
AddEditTaskPresenter —— 實(shí)現(xiàn)AddEditTaskContract.Presenter接口

從Acitivty入手
public class AddEditTaskActivity extends AppCompatActivity {
  protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.addtask_act);

        // Set up the toolbar.
        ...
        // Add Fragment  
        ...
       // Create the presenter
        mAddEditTaskPresenter = new AddEditTaskPresenter(
                taskId,
                Injection.provideTasksRepository(getApplicationContext()),
                addEditTaskFragment,
                shouldLoadDataFromRepo);
     }
}

我們可以看到一共做了三件事,初始化toolbar雕擂,添加fragment啡邑,創(chuàng)建一個presenter
在創(chuàng)建presenter的時候,把fragment傳了進(jìn)去井赌,實(shí)現(xiàn)了V和P的綁定
因此Activity在項(xiàng)目中是一個全局的控制者谤逼,負(fù)責(zé)創(chuàng)建view以及presenter實(shí)例,并將二者聯(lián)系起來仇穗。

View實(shí)現(xiàn)

AddEditTaskFragment 中通過實(shí)現(xiàn)BaseView中的setPresenter()流部,將Presenter和View關(guān)聯(lián)起來。

@Override
    public void onResume() {
        super.onResume();
        mPresenter.start();
    }

    @Override
    public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {
        mPresenter = checkNotNull(presenter);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        //fab屬于view控件仪缸,也歸fragment控制
        FloatingActionButton fab =
                (FloatingActionButton) getActivity().findViewById(R.id.fab_edit_task_done);
        fab.setImageResource(R.drawable.ic_done);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //響應(yīng)點(diǎn)擊事件 分發(fā)給P層
                mPresenter.saveTask(mTitle.getText().toString(), mDescription.getText().toString());
            }
        });
    }

  • Fragment作為每一個View接口的實(shí)現(xiàn), 主要負(fù)責(zé)數(shù)據(jù)顯示和在用戶交互時調(diào)用Presenter, 但是例子代碼中也是有一些直接操作的部分, 比如點(diǎn)擊開啟另一個Activity, 點(diǎn)擊彈出菜單(菜單項(xiàng)的點(diǎn)擊仍然是調(diào)用presenter的方法)贵涵。
  • View接口中定義的方法多為showXXX()方法列肢。
@Override
public boolean isActive() {
  return isAdded();
}
  • 在Presenter中數(shù)據(jù)回調(diào)的方法中, 先檢查View.isActive()是否為true, 來保證對Fragment的操作安全恰画。

Presenter實(shí)現(xiàn)

AddEditTaskPresenter是AddEditTaskContract.Presenter的實(shí)現(xiàn)

public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
            @NonNull AddEditTaskContract.View addTaskView, boolean shouldLoadDataFromRepo) {
        mTaskId = taskId;
        mTasksRepository = checkNotNull(tasksRepository);
        mAddTaskView = checkNotNull(addTaskView);
        mIsDataMissing = shouldLoadDataFromRepo;

        mAddTaskView.setPresenter(this);
    }
  • 從Presenter的構(gòu)造函數(shù)可以看出,傳入了TasksRepository 和addEditTaskFragment瓷马,也就是M和V拴还。
  • 構(gòu)造中先用guava的checkNotNull() 檢查是否為空, 然后賦值到字段; 之后再調(diào)用View的setPresenter()方法把Presenter傳回View中引用。
  • New Presenter的操作是在每一個Activity的onCreate()里做的: 先添加了Fragment(View), 然后把它作為參數(shù)傳給了Presenter. 這里并沒有存Presenter的引用欧聘。
@Override
    public void start() {
        if (!isNewTask() && mIsDataMissing) {
            populateTask();
        }
    }
@Override
    public void populateTask() {
        if (isNewTask()) {
            throw new RuntimeException("populateTask() was called but task is new.");
        }
        mTasksRepository.getTask(mTaskId, this);
    }
  • Presenter的start()方法在Fragment 的onResume() 的時候調(diào)用, 這時候通過M層的mTasksRepository 新建或者修改數(shù)據(jù); 其他方法均對應(yīng)于用戶在UI上的交互操作片林。

Model實(shí)現(xiàn)細(xì)節(jié)

該項(xiàng)目中Model層最大的特點(diǎn)是被賦予了數(shù)據(jù)獲取的職責(zé),與我們平常Model層只定義實(shí)體對象截然不同怀骤。實(shí)例中费封,數(shù)據(jù)的獲取、存儲蒋伦、數(shù)據(jù)狀態(tài)變化都是Model層的任務(wù)弓摘,Presenter會根據(jù)需要調(diào)用該層的數(shù)據(jù)處理邏輯并在需要時將回調(diào)傳入。

  • Model是在P構(gòu)造的時候傳入痕届,繼承的接口是TasksDataSource韧献,實(shí)例是TaskRepository

TasksDataSource是一個接口. 接口中定義了Presenter查詢數(shù)據(jù)的回調(diào)接口, 還有一些增刪改查的方法。

public interface TasksDataSource {
    // 加載數(shù)據(jù)的回調(diào)
    interface LoadTasksCallback {
        //成功回調(diào)
        void onTasksLoaded(List<Task> tasks);
        //失敗回調(diào)
        void onDataNotAvailable();
    }
    // 獲取數(shù)據(jù)的回調(diào)
    interface GetTaskCallback {

        void onTaskLoaded(Task task);

        void onDataNotAvailable();
    }

    void getTasks(@NonNull LoadTasksCallback callback);
    //通過id獲得數(shù)據(jù)
    void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);
    //保存task
    void saveTask(@NonNull Task task);
    ...
}
  • Model只有一個類研叫, 即TasksRepository锤窑, 它還是一個單例。
    它由手動實(shí)現(xiàn)的注入類Injection類提供:
public class Injection {

    public static TasksRepository provideTasksRepository(@NonNull Context context) {
        checkNotNull(context);
        return TasksRepository.getInstance(FakeTasksRemoteDataSource.getInstance(),
                TasksLocalDataSource.getInstance(context));
    }
}

TasksRepository的構(gòu)造如下:

private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
                        @NonNull TasksDataSource tasksLocalDataSource) {
    mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
    mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}
  • TaskRepository中首先定義了兩個數(shù)據(jù)源嚷炉,一個負(fù)責(zé)數(shù)據(jù)庫操作mTasksLocalDataSource渊啰,另一個負(fù)責(zé)網(wǎng)絡(luò)數(shù)據(jù)mTasksRemoteDataSoure。TaskRepository類中還有一個內(nèi)存緩存的實(shí)現(xiàn)申屹。

  • TasksLocalDataSource是TasksDataSource接口的實(shí)現(xiàn)绘证,里面是一些對數(shù)據(jù)庫的增刪改查的操作走搁。

  • 而在TasksDataSource的兩個內(nèi)部接口LoadTasksCallback和GetTaskCallback是Model層的回調(diào)接口。真正實(shí)現(xiàn)是在Presenter層迈窟∷街玻可以把成功獲取到的數(shù)據(jù)傳遞給Presenter層,比如AddEditTaskPresenter就實(shí)現(xiàn)了TasksDataSource.GetTaskCallback接口车酣。

  • AddEditTaskPresenter的start() 方法-->執(zhí)行了Model的getTask() 方法曲稼。

public void getTasks(@NonNull final LoadTasksCallback callback) {
        // 判空處理
        checkNotNull(callback);

        // 從內(nèi)存緩存中讀取數(shù)據(jù)
        if (mCachedTasks != null && !mCacheIsDirty) {
            callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
            return;
        }

        if (mCacheIsDirty) {
            // 如果緩存是臟數(shù)據(jù),那么從網(wǎng)絡(luò)獲取數(shù)據(jù)
            getTasksFromRemoteDataSource(callback);
        } else {
            // 查詢本地存儲(如果有)湖员。 如果沒有贫悄,查詢網(wǎng)絡(luò)。
            mTasksLocalDataSource.getTasks(new LoadTasksCallback() {
                @Override
                public void onTasksLoaded(List<Task> tasks) {
                    refreshCache(tasks);
                    //在P中通過回調(diào)得到M層查詢到的數(shù)據(jù)
                    callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
                }

                @Override
                public void onDataNotAvailable() {
                    getTasksFromRemoteDataSource(callback);
                }
            });
        }
    }

總結(jié)


回顧整理娘摔,總結(jié)一下:

  • Activity作為管理者窄坦,負(fù)責(zé)創(chuàng)建Fragment(V)和Presenter(P),而Presenter(P)的創(chuàng)建需要用到Fragment(V)和Repository(M)凳寺,F(xiàn)ragment(V)已經(jīng)被Activity創(chuàng)建了鸭津。
  • Contract 作為契約類,能夠更清晰的看到在Presenter層和View層中有哪些功能肠缨,方便我們以后的維護(hù)逆趋。
  • Fragment 作為V,負(fù)責(zé)的就是接收數(shù)據(jù)晒奕,更新界面闻书。
  • Repository 作為M,負(fù)責(zé)的是對數(shù)據(jù)的處理和回調(diào)脑慧,通過依賴注入的形式創(chuàng)建魄眉,并且Repository(M)可以同時操作遠(yuǎn)程數(shù)據(jù)和本地?cái)?shù)據(jù),而且M中沒有V的引用闷袒,而和P的聯(lián)系則是通過callback坑律,可以再看下官方給的圖。
  • Presenter 作為P霜运,V向它請求數(shù)據(jù)脾歇,然后P再向M請求數(shù)據(jù),通過回調(diào)得到數(shù)據(jù)之后在調(diào)用V進(jìn)行界面的更新淘捡。

看完發(fā)現(xiàn)藕各,好像還真的是一大堆的接口啊=钩<た觥!

參考文章
http://www.reibang.com/p/d54f82848bea
http://blog.csdn.net/jjwwmlp456/article/details/54869179
http://blog.csdn.net/u013400743/article/details/52205445
http://www.reibang.com/p/8ca27934c6e6
http://blog.csdn.net/card361401376/article/details/51518605
https://mp.weixin.qq.com/s?__biz=MzA4MjA0MTc4NQ==&mid=404088059&idx=3&sn=78dafacbca09b0d7345344c3eef24aff#rd
http://www.reibang.com/p/0bf5289ee8ce
http://www.reibang.com/p/389c9ae1a82c
http://blog.csdn.net/ljd2038/article/details/51477475

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市乌逐,隨后出現(xiàn)的幾起案子竭讳,更是在濱河造成了極大的恐慌,老刑警劉巖浙踢,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绢慢,死亡現(xiàn)場離奇詭異,居然都是意外死亡洛波,警方通過查閱死者的電腦和手機(jī)胰舆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蹬挤,“玉大人缚窿,你說我怎么就攤上這事⊙姘猓” “怎么了倦零?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吨悍。 經(jīng)常有香客問我扫茅,道長,這世上最難降的妖魔是什么畜份? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任诞帐,我火速辦了婚禮欣尼,結(jié)果婚禮上爆雹,老公的妹妹穿的比我還像新娘。我一直安慰自己愕鼓,他們只是感情好钙态,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著菇晃,像睡著了一般册倒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上磺送,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天驻子,我揣著相機(jī)與錄音,去河邊找鬼估灿。 笑死崇呵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的馅袁。 我是一名探鬼主播域慷,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了犹褒?” 一聲冷哼從身側(cè)響起抵窒,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎叠骑,沒想到半個月后李皇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宙枷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年疙赠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朦拖。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡圃阳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出璧帝,到底是詐尸還是另有隱情捍岳,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布睬隶,位于F島的核電站锣夹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏苏潜。R本人自食惡果不足惜银萍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恤左。 院中可真熱鬧贴唇,春花似錦、人聲如沸飞袋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巧鸭。三九已至瓶您,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纲仍,已是汗流浹背呀袱。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留郑叠,地道東北人夜赵。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像锻拘,于是被迫代替她去往敵國和親油吭。 傳聞我的和親對象是個殘疾皇子击蹲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,072評論 25 707
  • Google官方MVP Sample代碼解讀 關(guān)于Android程序的構(gòu)架, 當(dāng)前(2016.10)最流行的模式即...
    圣騎士wind閱讀 3,964評論 2 62
  • 轉(zhuǎn)載至:http://www.reibang.com/p/9a6845b26856 “Android MVP 詳解...
    SnowDragonYY閱讀 10,322評論 5 241
  • 臨睡前,我領(lǐng)著孩子們在院子里看星星和月亮婉宰。 夜幕幽藍(lán)閃爍歌豺,華美無比。 夜風(fēng)拂在面龐心包,空氣中彌漫著寧靜类咧。 大寶說,媽...
    任真閱讀 1,244評論 49 49
  • 生活不只有茍且蟹腾,還有詩和遠(yuǎn)方痕惋! 現(xiàn)在的你或許還依然徘徊在人生的十字路口,不知道該往哪邊前行娃殖,看著來來往往的人群值戳,你...
    小姐姐很拽閱讀 246評論 0 1