5. Jetpack源碼解析---ViewModel基本使用及源碼解析

截止到目前為止蜜托,JetpackNote源碼分析的文章已經(jīng)有四篇文章了,這一系列的文章我的初衷是想仔細研究一下Jetpack霉赡,最終使用Jetpack組件寫一個Demo橄务,上一篇已經(jīng)分析了LiveData,本篇文章將分析ViewModel.

1.背景

Jetpack源碼解析系列文章:

1. Jetpack源碼解析---看完你就知道Navigation是什么了?

2. Jetpack源碼解析---Navigation為什么切換Fragment會重繪穴亏?

3. Jetpack源碼解析---用Lifecycles管理生命周期

4. Jetpack源碼解析—LiveData的使用及工作原理

上篇我們對LiveData進行了分析蜂挪,已清楚了它的主要作用重挑,我們再來溫習一下:

LiveData是一個可以感知Activity、Fragment生命周期的數(shù)據(jù)容器棠涮。其本身是基于觀察者模式設計的谬哀,當LiveData所持有的數(shù)據(jù)發(fā)生改變時,它會通知對應的界面所持有該數(shù)據(jù)的UI進行更新严肪,并且LiveData中持有Lifecycle的引用史煎,所以只會在LifecycleOwner處于Active的狀態(tài)下通知數(shù)據(jù)改變,果數(shù)據(jù)改變發(fā)生在非 active 狀態(tài)诬垂,數(shù)據(jù)會變化劲室,但是不發(fā)送通知,等 owner 回到 active 的狀態(tài)下结窘,再發(fā)送通知.

LiveData配合今天所講的ViewModel使用起來真的是非常方便,接下來我們開始分析:

2.簡介

2.1 是什么充蓝?

簡單來說:ViewModel是以關聯(lián)生命周期的方式來存儲和管理UI相關數(shù)據(jù)的類隧枫,即使configuration發(fā)生改變(例如屏幕旋轉),數(shù)據(jù)仍然可以存在不會銷毀.

舉個例子來說:我們在開發(fā)中經(jīng)常會遇到這種場景,當我們的Activity和Fragment被銷毀重建之后谓苟,它們中的數(shù)據(jù)將會丟失官脓,而我們一般的解決方案就是通過onSaveInstanceState()中保存數(shù)據(jù),然后在onCreate()中恢復過來涝焙,但是當這些數(shù)據(jù)較大時或者數(shù)據(jù)可能又需要重新請求接口卑笨,這時候ViewModel就派上用場了,也就是說當我們把數(shù)據(jù)保存在ViewModel中仑撞,不管Activity和Fragment被銷毀了還是屏幕旋轉導致configuration發(fā)生了變化赤兴,保存在其中的數(shù)據(jù)依然存在。

不僅如此隧哮,ViewModel還可以幫助我們輕易的實現(xiàn)Fragment及Activity之間的數(shù)據(jù)共享和通信桶良,下面會詳細介紹。

2.2 ViewModel生命周期

先來看一下官方給出的圖:

image

圖中展示了當一個Activity經(jīng)過屏幕旋轉后的生命周期狀態(tài)改變沮翔,右側則是ViewModel的生命周期狀態(tài)陨帆。我們可以看到ViewModel的生命周期并不會跟隨著Activity的生命周期變化而變化,雖然ViewModel是由該Activity創(chuàng)建的采蚀。我們會在源碼分析部分再去看ViewModel的生命周期具體是怎樣的疲牵。下面我們看下ViewModel該怎樣使用?

3. 基本使用

3.1 數(shù)據(jù)存儲

我們參考官方Demo實現(xiàn)一個計時器的功能榆鼠,并且演示當屏幕發(fā)生旋轉時纲爸,計時器會不會重新啟動:

DemoViewModel

class DemoViewModel : ViewModel() {

    var time: Long? = null

    var seekbarValue = MutableLiveData<Int>()
}

ViewModelFragment

class ViewModelFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_view_model, container, false)
    }


    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        val vm = ViewModelProviders.of(this).get(DemoViewModel::class.java)
        if (vm.time == null) {
            vm.time = SystemClock.elapsedRealtime()
        }
        chronometer.base = vm.time!!
        chronometer.start()

        btn_fragment_share.setOnClickListener {
            findNavController().navigate(R.id.viewModelShareActivity)
        }
    }

}

代碼很簡單,只是在ViewModel中存儲了一個time值璧眠,在fragment中啟動計時器缩焦,當我們旋轉屏幕的時候你會發(fā)現(xiàn)读虏,計時器的值并沒有變化,仍然按照旋轉之前的數(shù)值進行計數(shù)袁滥。

3.2 Fragment數(shù)據(jù)共享

ViewModelShareActivity中展示了ViewModel中的數(shù)據(jù)進行Fragment數(shù)據(jù)共享的功能盖桥。

在之前的DemoViewModel中我們存儲了seekbar的值,然后我們看Fragment中是怎么實現(xiàn)的题翻?

class DataShareFragment : Fragment() {

    private lateinit var mViewModel: DemoViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        mViewModel = ViewModelProviders.of(activity!!).get(DemoViewModel::class.java)
        return inflater.inflate(R.layout.fragment_data_share, container, false)
    }


    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        subscribeSeekBar()
    }

    private fun subscribeSeekBar() {
        // 監(jiān)聽SeekBar改變ViewModel的中的值
        seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
                if (fromUser) {
                    mViewModel.seekbarValue.value = progress
                }
            }

            override fun onStartTrackingTouch(seekBar: SeekBar) {}

            override fun onStopTrackingTouch(seekBar: SeekBar) {}
        })
        // 當ViewModel中的值發(fā)生變化時揩徊,更新SeekBar
        activity?.let {
            mViewModel.seekbarValue.observe(it, Observer<Int> { value ->
                if (value != null) {
                    seekBar.progress = value
                }
            })
        }
    }
}

看代碼其實挺簡單的,只做了兩個操作:

  1. 監(jiān)聽SeekBar改變ViewModel的中的值
  2. 當ViewModel中的值發(fā)生變化時嵌赠,更新SeekBar

同樣塑荒,當旋轉屏幕之后,SeekBar的值也不會改變姜挺。到這里ViewModel的基本使用方式我們已經(jīng)了解了齿税,接下來我們來分析一下它具體是怎么實現(xiàn)的?

沒錯炊豪,又到了源碼分析的部分了雳锋,相信很多童鞋也都比較不喜歡源碼的部分傍衡,包括我也是史飞,之前很不愿意去看源碼喧务,但是當你嘗試看了一些源碼之后,你會發(fā)現(xiàn)很有意思的缺虐,因為有些東西你是有必要去分析源碼實現(xiàn)的芜壁,這是作為一個開發(fā)者必備的基本的素質,而且當你使用一個組件的時候高氮,一步一步的跟著代碼走慧妄,慢慢的分析了整個的組件設計方式,最后站在開發(fā)這個組件的角度纫溃,去看他的設計思想和一些模式的時候腰涧,對自己本身也是一個很大的提高,所以我真的建議有興趣的可以跟著自己的思路一步一步的看下源碼紊浩。好了廢話不多說窖铡。

4. 源碼分析

ViewModelProviders

在使用VM的時候一般都通過val vm = ViewModelProviders.of(this).get(DemoViewModel::class.java)去創(chuàng)建,具體來看一下of方法:

@NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        Application application = checkApplication(checkActivity(fragment));
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(fragment.getViewModelStore(), factory);
    }

of方法通過ViewModelProvider中的一個單例AndroidViewModelFactory創(chuàng)建了Factory幫助我們去創(chuàng)建VM坊谁,并且返回了一個ViewModelProvider费彼。大家注意一下第一個參數(shù)fragment.getViewModelStore(),這個ViewModelStore是什么呢口芍?接著看


public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();
        //存儲VM
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

其實就是一個存儲VM的容器箍铲,里面通過HashMap進行存和取。

下面我們看創(chuàng)建VM時的get()方法

@NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
            //在ViewModelStore中拿到VM實例
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        //工廠創(chuàng)建VM鬓椭,并保存VM
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

get()方法就是獲取VM實例的過程颠猴,如果VM不存在关划,則通過工廠去創(chuàng)建實例。具體工廠創(chuàng)建實現(xiàn):

?

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;

        /**
         * Retrieve a singleton instance of AndroidViewModelFactory.
         *
         * @param application an application to pass in {@link AndroidViewModel}
         * @return A valid {@link AndroidViewModelFactory}
         */
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;

        /**
         * Creates a {@code AndroidViewModelFactory}
         *
         * @param application an application to pass in {@link AndroidViewModel}
         */
        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } 
              .......
            }
            return super.create(modelClass);
        }
    }

這個工廠就很簡單了翘瓮,就是通過反射來創(chuàng)建VM實例贮折。

到這里VM的創(chuàng)建過程就差不多了,而我們發(fā)現(xiàn)他并沒有和生命周期有什么相關的東西资盅,或者說VM是怎樣保證的的數(shù)據(jù)不被銷毀的呢调榄?看了網(wǎng)上的一些文章,很多都說在of方法的時候傳入了Fragment,并且通過HolderFragment持有ViewModelStore對象等等……比如:

image

但是在我這里發(fā)現(xiàn)跟他們的都不一樣呵扛,我搜了一下ViewModelStores每庆,發(fā)現(xiàn)它已經(jīng)‘退役’了。

image

并且它的注釋也告訴了我們它的繼承者:

image

也就是我們在of()方法中的:

image

也就是說谷歌已經(jīng)將ViewModelStore集成到了Fragment中今穿,一起去看一下吧:

Fragment.java


        @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        return mFragmentManager.getViewModelStore(this);
    }
FragmentManagerImpl.java

        private FragmentManagerViewModel mNonConfig;
        ......
    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        return mNonConfig.getViewModelStore(f);
    }

我們可以看到代碼到了我們熟悉的FragmentManagerImpl缤灵,它的里面持有了一個FragmentManagerViewModel實例,進去看看這個是個什么東西:

FragmentManagerViewModel.java

class FragmentManagerViewModel extends ViewModel {

    //創(chuàng)建FragmentVM的工廠
    private static final ViewModelProvider.Factory FACTORY = new ViewModelProvider.Factory() {
        @NonNull
        @Override
        @SuppressWarnings("unchecked")
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            FragmentManagerViewModel viewModel = new FragmentManagerViewModel(true);
            return (T) viewModel;
        }
    };

    //從viewModelProvider中獲取FragmentVM實例
    @NonNull
    static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
        ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
                FACTORY);
        return viewModelProvider.get(FragmentManagerViewModel.class);
    }
    //非中斷的Fragment集合
    private final HashSet<Fragment> mRetainedFragments = new HashSet<>();
    private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>();
    private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();

    private final boolean mStateAutomaticallySaved;
    // Only used when mStateAutomaticallySaved is true
    private boolean mHasBeenCleared = false;
    // Only used when mStateAutomaticallySaved is false
    private boolean mHasSavedSnapshot = false;

 
    FragmentManagerViewModel(boolean stateAutomaticallySaved) {
        mStateAutomaticallySaved = stateAutomaticallySaved;
    }

    
    //添加非中斷Fragment
    boolean addRetainedFragment(@NonNull Fragment fragment) {
        return mRetainedFragments.add(fragment);
    }

    @NonNull
    Collection<Fragment> getRetainedFragments() {
        return mRetainedFragments;
    }
    //是否改銷毀
    boolean shouldDestroy(@NonNull Fragment fragment) {
        if (!mRetainedFragments.contains(fragment)) {
            // Always destroy non-retained Fragments
            return true;
        }
        if (mStateAutomaticallySaved) {
            // If we automatically save our state, then only
            // destroy a retained Fragment when we've been cleared
            return mHasBeenCleared;
        } else {
            // Else, only destroy retained Fragments if they've
            // been reaped before the state has been saved
            return !mHasSavedSnapshot;
        }
    }

    boolean removeRetainedFragment(@NonNull Fragment fragment) {
        return mRetainedFragments.remove(fragment);
    }

        //獲取VMStore
    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore == null) {
            viewModelStore = new ViewModelStore();
            mViewModelStores.put(f.mWho, viewModelStore);
        }
        return viewModelStore;
    }
    //銷毀并清空VM
    void clearNonConfigState(@NonNull Fragment f) {
        if (FragmentManagerImpl.DEBUG) {
            Log.d(FragmentManagerImpl.TAG, "Clearing non-config state for " + f);
        }
        // Clear and remove the Fragment's child non config state
        FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
        if (childNonConfig != null) {
            childNonConfig.onCleared();
            mChildNonConfigs.remove(f.mWho);
        }
        // Clear and remove the Fragment's ViewModelStore
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore != null) {
            viewModelStore.clear();
            mViewModelStores.remove(f.mWho);
        }
    }

看代碼我們發(fā)現(xiàn)它繼承了VM荣赶,并且里面保存了VMStore,也就是說保存了VM凤价,同時清空的操作也在這里面:clearNonConfigState()

ViewModelStore.java

/**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }

那么到底是什么時候來清空VM的呢?也就是說clear()是什么時候調用的呢拔创?查看源碼我們發(fā)現(xiàn)有兩處:

image
  1. 也就是上面提到的FragmentViewModel.java里面的clearNonConfigState()方法,而這個方法只在一個地方被調用了:
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
boolean beingRemoved = f.mRemoving && !f.isInBackStack();
if (beingRemoved || mNonConfig.shouldDestroy(f)) {
    boolean shouldClear;
    if (mHost instanceof ViewModelStoreOwner) {
        shouldClear = mNonConfig.isCleared();
    } else if (mHost.getContext() instanceof Activity) {
        Activity activity = (Activity) mHost.getContext();
        shouldClear = !activity.isChangingConfigurations();
    } else {
        shouldClear = true;
    }
    //Fragment正在被移除或者應該清空的狀態(tài)下
    if (beingRemoved || shouldClear) {
        mNonConfig.clearNonConfigState(f);
    }
    f.performDestroy();
    dispatchOnFragmentDestroyed(f, false);
} else {
    f.mState = Fragment.INITIALIZING;
}

這個方法是在FragmentManagerImpl.java中的moveToState方法里面的富蓄,這個方法是跟隨著Fragment的生命周期的剩燥,當這個方法被調用時,判斷兩個狀態(tài)beingRemovedshoudClear然后調用clear()方法立倍。關于moveToState()可以查看這篇文章:Fragment之底層關鍵操作函數(shù)moveToState

  1. 第二個調用的地方就是ComponentActivity.java
getLifecycle().addObserver(new GenericLifecycleObserver() {
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
            if (event == Lifecycle.Event.ON_DESTROY) {
                if (!isChangingConfigurations()) {//不是改變狀態(tài)配置的
                    getViewModelStore().clear();
                }
            }
        }
});

關于ComponentActivity灭红,如果有看過之前我分析過的關于Lifecycles的應該對它有所了解3. Jetpack源碼解析---用Lifecycles管理生命周期FragmentActivity繼承了它口注,而ComponentActivity里面通過Lifecycles觀察生命周期变擒,當接受到ON_DESTROY的事件時,清空VM寝志。

5. 總結

分析完源碼之后娇斑,我們來總結一下整個流程:

  1. 通過ViewModelProviders.of(this).get(DemoViewModel::class.java)創(chuàng)建VM
  2. of()方法中傳入 fragment.getViewModelStore()并且返回VMProviders
  3. 查看FragmentManagerImplgetViewModelStore(),持有FragmentManagerViewModel對象
  4. FragmentManagerViewModel中清空VM操作 clearNonConfigState()材部,同時在ViewModelStoreclear()了ViewModel的value值
  5. 最后我們發(fā)現(xiàn)只有在ComponentActivity中觀察到接收到ON_DESTROY的事件時同時并不是由于configuration發(fā)生變化時才會執(zhí)行clear()操作毫缆;另外一處是在moveToState()方法中,滿足beingRemovedshouldClear狀態(tài)也會清空VM

好了 整個流程就是這樣了乐导,并沒有特別深入的去分析苦丁,但是基本的原理我們已經(jīng)清楚了,Demo中也只是簡單的使用了VM物臂。

JetPack源碼分析系列文章到這里應該就結束了旺拉,后面還有Paging产上、WorkManager、Room蛾狗,以及Camera2和ViewPager2晋涣,這些目前暫時不分析了,但是也會出基本的使用和簡單的分析淘太,一共5篇源碼分析姻僧,文章中的Demo我寫了一個APP—JetpackNote,里面有基本的Demo例子蒲牧,和文章的分析撇贺;一直也沒有提到是因為功能還不完善,我會盡快完善它的冰抢,也希望有什么意見的小伙伴可以和我溝通交流松嘶。

Github源碼地址在:

JetpackNote

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市挎扰,隨后出現(xiàn)的幾起案子翠订,更是在濱河造成了極大的恐慌,老刑警劉巖遵倦,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尽超,死亡現(xiàn)場離奇詭異,居然都是意外死亡梧躺,警方通過查閱死者的電腦和手機似谁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掠哥,“玉大人巩踏,你說我怎么就攤上這事⌒螅” “怎么了塞琼?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長禁舷。 經(jīng)常有香客問我彪杉,道長,這世上最難降的妖魔是什么榛了? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任在讶,我火速辦了婚禮,結果婚禮上霜大,老公的妹妹穿的比我還像新娘构哺。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布曙强。 她就那樣靜靜地躺著残拐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碟嘴。 梳的紋絲不亂的頭發(fā)上溪食,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機與錄音娜扇,去河邊找鬼错沃。 笑死,一個胖子當著我的面吹牛雀瓢,可吹牛的內容都是我干的枢析。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼刃麸,長吁一口氣:“原來是場噩夢啊……” “哼醒叁!你這毒婦竟也來了?” 一聲冷哼從身側響起泊业,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤把沼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吁伺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饮睬,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年篮奄,在試婚紗的時候發(fā)現(xiàn)自己被綠了续捂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡宦搬,死狀恐怖,靈堂內的尸體忽然破棺而出劫拗,到底是詐尸還是另有隱情间校,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布页慷,位于F島的核電站憔足,受9級特大地震影響,放射性物質發(fā)生泄漏酒繁。R本人自食惡果不足惜滓彰,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望州袒。 院中可真熱鬧揭绑,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至邦蜜,卻和暖如春依鸥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悼沈。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工贱迟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人絮供。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓衣吠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杯缺。 傳聞我的和親對象是個殘疾皇子蒸播,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內容