安卓小部件刷新源碼解析一非列表

一横媚、刷新流程

1纠炮、system_process 發(fā)送廣播
2、應(yīng)用widget收到廣播,準(zhǔn)備數(shù)據(jù)構(gòu)建RemoteView灯蝴,并調(diào)用AppWidgetManager的updateAppWidget()方法
3恢口、AppWidgetManager 通過AIDL 通知 system_process更新,system_process收到回調(diào)后做一些列操作
4穷躁、system_process 通過AIDL 回調(diào)Host 更新方法耕肩,Host 解析RemoteView,并生成相應(yīng)的視圖

二、詳細(xì)刷新流程

widget 更新 -1-.png

1问潭、system_process 發(fā)送廣播
更新廣播的action 為android.appwidget.action.APPWIDGET_UPDATE
2猿诸、updateAppWidget()/partiallyUpdateAppWidget 局部更新
調(diào)用AppWidgetManager.updateAppWidget(),在此之前一般都會設(shè)置相應(yīng)的RemoteView
3、updateAppWidgetIds()
這里通過AIDL跨進(jìn)程技術(shù)調(diào)用system_progress進(jìn)程的AppWidgetServiceImpl對象狡忙。
3.1梳虽、enforceCallFromPackage
安全性校驗(yàn),確定請求的包命和uid 是一致的
3.2灾茁、ensureGroupStateLoadedLocked
若已經(jīng)加載過了則return,若沒有加載過則根據(jù)uid 獲取widgetProvider(過濾帶刷新action 的廣播)窜觉,根據(jù)uid 獲取相應(yīng)的配置文件,根據(jù)配置文件設(shè)置widget删顶,并綁定相應(yīng)的host竖螃。放入mWidgets中淑廊。
3.3逗余、lookupWidgetLocked
根據(jù)widgetId在mWidgets 找到對應(yīng)的widget,通過uid驗(yàn)證權(quán)限
3.4、updateAppWidgetInstanceLocked
替換widget 的remoteView(局部刷新是合并兩次的RemoteView)季惩,檢查RemoteView的bitmap 是否超過最大限制录粱。
3.4.1、scheduleNotifyUpdateAppWidgetLocked
mCallbackHandler 發(fā)送更新message
3.5画拾、handleNotifyUpdateAppWidget
host 的 updateAppWidget()方法
4啥繁、通過AIDL 回調(diào)Host 的updateAppWidget( ),send 更新 message
4.1青抛、handler 處理更新message
4.1.1旗闽、updateAppWidgetView
在host的mViews中通過widgetId 找到對應(yīng)的AppWidgetHostView,讓后進(jìn)行更新
4.1.1.1、applyRemoteViews將remoteView 中的action 設(shè)置在相應(yīng)view 上

三蜜另、詳細(xì)刷新流程代碼分析

1适室、system_process 發(fā)送廣播 ,更新廣播的action 為android.appwidget.action.APPWIDGET_UPDATE举瑰。

// AppWidgetProvider 繼承于廣播捣辆,system_process發(fā)送廣播是會回調(diào)onReceive方法
// 如果是更新廣播的話會回調(diào)onUpdate()方法
public class AppWidgetProvider extends BroadcastReceiver {
   ...
   public void onReceive(Context context, Intent intent) {
    // Protect against rogue update broadcasts (not really a security issue,
    // just filter bad broacasts out so subclasses are less likely to crash).
    String action = intent.getAction();
    if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
        Bundle extras = intent.getExtras();
        if (extras != null) {
            int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
            if (appWidgetIds != null && appWidgetIds.length > 0) {
                this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
            }
        }
    }
   ...
}

2、應(yīng)用widget收到廣播,準(zhǔn)備數(shù)據(jù)構(gòu)建RemoteView此迅,并調(diào)用AppWidgetManager的updateAppWidget()方法

public abstract class TestWidgetProvider extends AppWidgetProvider {
    ...
    /**
    *onReceive會回調(diào)該方法
    *
    **/
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds{
    // AppWidgetProvider 這里通常會設(shè)置new RemoteView,并設(shè)置汽畴,可設(shè)置點(diǎn)擊時(shí)間旧巾、文字、圖片等最后調(diào)用
    // appWidgetManager.updateAppWidget()
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        for (int widgetId : appWidgetIds) {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(),         
            R.layout.widget_test);
             ...
            appWidgetManager.updateAppWidget(widgetId, remoteViews);
    }
    }
  ...
}

3忍些、updateAppWidgetIds()方法鲁猩,這里通過AIDL跨進(jìn)程技術(shù)調(diào)用system_progress進(jìn)程的AppWidgetServiceImpl對象。

public class AppWidgetManager {
    ...
    public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
        mService 為 AppWidgetServiceImpl
        if (mService == null) {
        return;
        }
        try {
            // 通過AIDL 調(diào)用 AppWidgetServiceImpl 的 mService.updateAppWidgetIds 方法
            mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
   ...
 
}
 // partially 代表是否部分更新
 private void updateAppWidgetIds(String callingPackage, int[] appWidgetIds,
        RemoteViews views, boolean partially) {
    final int userId = UserHandle.getCallingUserId();
 
    if (appWidgetIds == null || appWidgetIds.length == 0) {
        return;
    }
 
 
    // Make sure the package runs under the caller uid.
    // AppWidgetServiceImpl 運(yùn)行在system_process 罢坝,包名為字符串傳入绳匀,
    // 安全性校驗(yàn),確定請求的包命和uid 是一致的
    mSecurityPolicy.enforceCallFromPackage(callingPackage);
    synchronized (mLock) {
         // 是否解鎖狀態(tài)炸客,處于解鎖狀態(tài)疾棵,若第一次加載則構(gòu)建widget,后面會詳細(xì)解析
        ensureGroupStateLoadedLocked(userId);
 
        final int N = appWidgetIds.length;
        for (int i = 0; i < N; i++) {
            final int appWidgetId = appWidgetIds[i];
 
            // NOTE: The lookup is enforcing security across users by making
            // sure the caller can only access widgets it hosts or provides.
            // 根據(jù)widgetId在mWidgets 找到對應(yīng)的widget,通過uid驗(yàn)證權(quán)限,后面會詳細(xì)解析
            Widget widget = lookupWidgetLocked(appWidgetId,
                    Binder.getCallingUid(), callingPackage);
 
 
            if (widget != null) {
                // 更新widget
                updateAppWidgetInstanceLocked(widget, views, partially);
            }
        }
    }
}

3.1痹仙、enforceCallFromPackage()方法是尔,安全性校驗(yàn),確定請求的包命和uid 是一致的开仰。

public void enforceCallFromPackage(String packageName) {
    mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
}
@Deprecated
public void checkPackage(int uid, @NonNull String packageName) {
    try {
        // 檢查請求的 uid 和 packageName  是否一致
        if (mService.checkPackage(uid, packageName) != MODE_ALLOWED) {
            throw new SecurityException(
                    "Package " + packageName + " does not belong to " + uid);
        }
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

3.2拟枚、ensureGroupStateLoadedLocked()方法,若已經(jīng)加載過了則return,若沒有加載過則根據(jù)uid 獲取widgetProvider(過濾帶刷新action 的廣播)众弓,根據(jù)uid 獲取相應(yīng)的配置文件恩溅,根據(jù)配置文件設(shè)置widget,并綁定相應(yīng)的host谓娃。放入mWidgets中脚乡。

private void ensureGroupStateLoadedLocked(int userId, boolean enforceUserUnlockingOrUnlocked) {
    // 判斷該應(yīng)用是否處在解鎖狀態(tài),設(shè)備鎖
    if (enforceUserUnlockingOrUnlocked && !isUserRunningAndUnlocked(userId)) {
        throw new IllegalStateException(
                "User " + userId + " must be unlocked for widgets to be available");
    }
    // 判斷該應(yīng)用文件配置是否處在解鎖狀態(tài)
    if (enforceUserUnlockingOrUnlocked && isProfileWithLockedParent(userId)) {
        throw new IllegalStateException(
                "Profile " + userId + " must have unlocked parent");
    }
    // 獲取能用的配置配置id
    final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
 
 
    // 查看是否有未加載的user http://www.reibang.com/p/3ad2163f7d34
    //https://blog.csdn.net/qq_14978113/article/details/94654401
    // Careful lad, we may have already loaded the state for some
    // group members, so check before loading and read only the
    // state for the new member(s).
    int newMemberCount = 0;
    final int profileIdCount = profileIds.length;
    for (int i = 0; i < profileIdCount; i++) {
        final int profileId = profileIds[i];
        // >=0代表已經(jīng)加載過,標(biāo)記數(shù)組
        if (mLoadedUserIds.indexOfKey(profileId) >= 0) {
            profileIds[i] = LOADED_PROFILE_ID;
        } else {
            newMemberCount++;
        }
    }
    // 沒有新加的 便會return
    if (newMemberCount <= 0) {
        return;
    }
     // 構(gòu)建新增加的ProfileId 數(shù)組,后續(xù)通常在第一次加載的時(shí)候執(zhí)行
    int newMemberIndex = 0;
    final int[] newProfileIds = new int[newMemberCount];
    for (int i = 0; i < profileIdCount; i++) {
        final int profileId = profileIds[i];
        if (profileId != LOADED_PROFILE_ID) {
            mLoadedUserIds.put(profileId, profileId);
            newProfileIds[newMemberIndex] = profileId;
            newMemberIndex++;
        }
    }
    // 清除provider 和 host 的tag 設(shè)置為 TAG_UNDEFINED
    clearProvidersAndHostsTagsLocked();
     // 根據(jù)加載ProfileId 獲取系統(tǒng) ResolveInfo 列表滨达, 根據(jù)ResolveInfo 構(gòu)建
     provider;
    loadGroupWidgetProvidersLocked(newProfileIds);
    // 從系統(tǒng)配置文件/data/system/users/0/appwidgets.xml 加載狀態(tài)奶稠、
    loadGroupStateLocked(newProfileIds);
}

3.3、lookupWidgetLocked()方法捡遍,根據(jù)widgetId在mWidgets 找到對應(yīng)的widget,通過uid驗(yàn)證權(quán)限锌订。

private Widget lookupWidgetLocked(int appWidgetId, int uid, String packageName) {
    final int N = mWidgets.size();
    for (int i = 0; i < N; i++) {
        Widget widget = mWidgets.get(i);
        if (widget.appWidgetId == appWidgetId
                && mSecurityPolicy.canAccessAppWidget(widget, uid, packageName)) {
            return widget;
        }
    }
    return null;
}

3.4、updateAppWidgetInstanceLocked()方法画株,替換widget 的remoteView(局部刷新是合并兩次的RemoteView)辆飘,檢查RemoteView的bitmap 是否超過最大限制。

private void updateAppWidgetInstanceLocked(Widget widget, RemoteViews views,
        boolean isPartialUpdate) {
    if (widget != null && widget.provider != null
            && !widget.provider.zombie && !widget.host.zombie) {
 
 
        // 如果是局部更新谓传,合并remote view
        if (isPartialUpdate && widget.views != null) {
            // For a partial update, we merge the new RemoteViews with the old.
            widget.views.mergeRemoteViews(views);
        } else {
            // For a full update we replace the RemoteViews completely.
            widget.views = views;
        }
        // 如果非系統(tǒng)應(yīng)用 bitmap 的大小超過規(guī)定大小蜈项,則拋出異常
        int memoryUsage;
        if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) &&
                (widget.views != null) &&
                ((memoryUsage = widget.views.estimateMemoryUsage()) > mMaxWidgetBitmapMemory)) {
            widget.views = null;
            throw new IllegalArgumentException("RemoteViews for widget update exceeds"
                    + " maximum bitmap memory usage (used: " + memoryUsage
                    + ", max: " + mMaxWidgetBitmapMemory + ")");
        }
        scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
    }
}
 
/***
**合并action 
**/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void mergeRemoteViews(RemoteViews newRv) {
    if (newRv == null) return;
    // We first copy the new RemoteViews, as the process of merging modifies the way the actions
    // reference the bitmap cache. We don't want to modify the object as it may need to
    // be merged and applied multiple times.
    RemoteViews copy = new RemoteViews(newRv);
 
 
    HashMap<String, Action> map = new HashMap<String, Action>();
    if (mActions == null) {
        mActions = new ArrayList<Action>();
    }
 
    // 將老的action 放入map
    int count = mActions.size();
    for (int i = 0; i < count; i++) {
        Action a = mActions.get(i);
        map.put(a.getUniqueKey(), a);
    }
 
 
    ArrayList<Action> newActions = copy.mActions;
    if (newActions == null) return;
    count = newActions.size();
    for (int i = 0; i < count; i++) {
        Action a = newActions.get(i);
        //由viewId和action tag 組成
        String key = newActions.get(i).getUniqueKey();
        // R開始支持
        int mergeBehavior = newActions.get(i).mergeBehavior();
        // 相同則把舊的action 移除
        if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
            mActions.remove(map.get(key));
            map.remove(key);
        }
 
 
        // If the merge behavior is ignore, we don't bother keeping the extra action
        if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
            mActions.add(a);
        }
    }
 
    存儲設(shè)置的bitmap
    // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
    mBitmapCache = new BitmapCache();
    setBitmapCache(mBitmapCache);
}

3.4.1、scheduleNotifyUpdateAppWidgetLocked()方法良拼,mCallbackHandler 發(fā)送更新message战得。

private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) {
    long requestId = UPDATE_COUNTER.incrementAndGet();
    if (widget != null) {
        widget.updateSequenceNos.put(ID_VIEWS_UPDATE, requestId);
    }
    if (widget == null || widget.provider == null || widget.provider.zombie
            || widget.host.callbacks == null || widget.host.zombie) {
        return;
    }
 
    SomeArgs args = SomeArgs.obtain();
    args.arg1 = widget.host;
    args.arg2 = widget.host.callbacks;
    args.arg3 = (updateViews != null) ? updateViews.clone() : null;
    args.arg4 = requestId;
    args.argi1 = widget.appWidgetId;
 
 
    mCallbackHandler.obtainMessage(
            CallbackHandler.MSG_NOTIFY_UPDATE_APP_WIDGET,
            args).sendToTarget();
}

3.5、handleNotifyUpdateAppWidget()方法庸推,host 的 updateAppWidget()方法常侦,通過通過AIDL 回調(diào)到host 中浇冰。

private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks,
        int appWidgetId, RemoteViews views, long requestId) {
    try {
        callbacks.updateAppWidget(appWidgetId, views);
        host.lastWidgetUpdateSequenceNo = requestId;
    } catch (RemoteException re) {
        synchronized (mLock) {
            Slog.e(TAG, "Widget host dead: " + host.id, re);
            host.callbacks = null;
        }
    }
}

4、通過AIDL 回調(diào)Host 的updateAppWidget( )聋亡,send 更新 message

public class AppWidgetHost {
    public void updateAppWidget(int appWidgetId, RemoteViews views) {
        if (isLocalBinder() && views != null) {
            views = views.clone();
        }
        Handler handler = mWeakHandler.get();
        if (handler == null) {
            return;
        }
        Message msg = handler.obtainMessage(HANDLE_UPDATE, appWidgetId, 0, views);
        msg.sendToTarget();
    }
}

4.1肘习、handler 處理更新message。

public void handleMessage(Message msg) {
    switch (msg.what) {
        case HANDLE_UPDATE: {
            updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj);
            break;
        }
    }
}

4.1.1坡倔、updateAppWidgetView()方法漂佩,在host的mViews中通過widgetId 找到對應(yīng)的AppWidgetHostView,讓后進(jìn)行更新。

void updateAppWidgetView(int appWidgetId, RemoteViews views) {
    AppWidgetHostView v;
    synchronized (mViews) {
        v = mViews.get(appWidgetId);
    }
    if (v != null) {
        v.updateAppWidget(views);
    }
}

4.1.1.1罪塔、applyRemoteViews()方法投蝉,將remoteView 中的action 設(shè)置在相應(yīng)view 上。

protected void applyRemoteViews(@Nullable RemoteViews remoteViews, boolean useAsyncIfPossible) {
    boolean recycled = false;
    View content = null;
    Exception exception = null;
 
    // Block state restore until the end of the apply.
    mLastInflatedRemoteViewsId = -1;
 
    if (mLastExecutionSignal != null) {
        mLastExecutionSignal.cancel();
        mLastExecutionSignal = null;
    }
    // 如果remoteViews 為null 加載默認(rèn)視圖
    if (remoteViews == null) {
        if (mViewMode == VIEW_MODE_DEFAULT) {
            // We've already done this -- nothing to do.
            return;
        }
        content = getDefaultView();
        mLayoutId = -1;
        mViewMode = VIEW_MODE_DEFAULT;
    } else {
        // Select the remote view we are actually going to apply.
        // 查找適合的remoteView 找不到時(shí)返回最小的
        RemoteViews rvToApply = remoteViews.getRemoteViewsToApply(mContext, mCurrentSize);
        // 是否 設(shè)置了
        /if (mOnLightBackground) {
            // 如果深色淺色模式變了征堪,使用相應(yīng)的 layout id ,new remoteview
            rvToApply = rvToApply.getDarkTextViews();
        }
 
        if (mAsyncExecutor != null && useAsyncIfPossible) {
            // 異步加載
            inflateAsync(rvToApply);
            return;
        }
        int layoutId = rvToApply.getLayoutId();
        if (rvToApply.canRecycleView(mView)) {
            try {
                rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
                        mColorResources);
                content = mView;
                mLastInflatedRemoteViewsId = rvToApply.computeUniqueId(remoteViews);
                recycled = true;
                if (LOGD) Log.d(TAG, "was able to recycle existing layout");
            } catch (RuntimeException e) {
                exception = e;
            }
        }
 
 
        // Try normal RemoteView inflation
        if (content == null) {
            try {
                content = rvToApply.apply(mContext, this, mInteractionHandler,
                        mCurrentSize, mColorResources);
                mLastInflatedRemoteViewsId = rvToApply.computeUniqueId(remoteViews);
                if (LOGD) Log.d(TAG, "had to inflate new layout");
            } catch (RuntimeException e) {
                exception = e;
            }
        }
 
        mLayoutId = layoutId;
        mViewMode = VIEW_MODE_CONTENT;
    }
 
    applyContent(content, recycled, exception);
}
 
 
/**
**將action 轉(zhuǎn)成相應(yīng)的設(shè)置
**/
private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
        ColorResources colorResources, boolean topLevel) {
 
 
    RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
 
    // In the case that a view has this RemoteViews applied in one orientation or size, is
    // persisted across change, and has the RemoteViews re-applied in a different situation
    // (orientation or size), we throw an exception, since the layouts may be completely
    // unrelated.
    if (hasMultipleLayouts()) {
        if (!rvToApply.canRecycleView(v)) {
            throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
                    " that does not share the same root layout id.");
        }
    }
    // 將action 轉(zhuǎn)成相應(yīng)行為
    rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
    
    // If the parent of the view is has is a root, resolve the recycling.
    if (topLevel && v instanceof ViewGroup) {
        finalizeViewRecycling((ViewGroup) v);
    }
}
 
/**
**將生成的view 添加到host 中
**/
private void applyContent(View content, boolean recycled, Exception exception) {
    if (content == null) {
        if (mViewMode == VIEW_MODE_ERROR) {
            // We've already done this -- nothing to do.
            return ;
        }
        if (exception != null) {
            Log.w(TAG, "Error inflating RemoteViews", exception);
        }
        content = getErrorView();
        mViewMode = VIEW_MODE_ERROR;
    }
    // 復(fù)用的場景直接在view上進(jìn)行了設(shè)置瘩缆,而view 之前已經(jīng)添加到host,所以復(fù)用的case 這里就不用處理佃蚜。
    if (!recycled) {
        prepareView(content);
        addView(content);
    }
 
 
    if (mView != content) {
        removeView(mView);
        mView = content;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庸娱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子谐算,更是在濱河造成了極大的恐慌熟尉,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洲脂,死亡現(xiàn)場離奇詭異斤儿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)腮考,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門雇毫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人踩蔚,你說我怎么就攤上這事∶墩常” “怎么了馅闽?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長馍迄。 經(jīng)常有香客問我福也,道長,這世上最難降的妖魔是什么攀圈? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任暴凑,我火速辦了婚禮,結(jié)果婚禮上赘来,老公的妹妹穿的比我還像新娘现喳。我一直安慰自己凯傲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布嗦篱。 她就那樣靜靜地躺著冰单,像睡著了一般。 火紅的嫁衣襯著肌膚如雪灸促。 梳的紋絲不亂的頭發(fā)上诫欠,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機(jī)與錄音浴栽,去河邊找鬼荒叼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛典鸡,可吹牛的內(nèi)容都是我干的甩挫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼椿每,長吁一口氣:“原來是場噩夢啊……” “哼伊者!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起间护,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤亦渗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后汁尺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體法精,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年痴突,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搂蜓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辽装,死狀恐怖帮碰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拾积,我是刑警寧澤殉挽,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站拓巧,受9級特大地震影響斯碌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肛度,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一傻唾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧承耿,春花似錦冠骄、人聲如沸伪煤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽带族。三九已至,卻和暖如春蟀给,著一層夾襖步出監(jiān)牢的瞬間蝙砌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工跋理, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留择克,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓前普,卻偏偏與公主長得像肚邢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子拭卿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

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