Content Provider筆記

參考:

一. 用途:

  • 跨程序共享數(shù)據(jù)(為其他應(yīng)用程序提供訪問數(shù)據(jù)的接口)
  • 利用Content Provider對數(shù)據(jù)進(jìn)行封裝高镐,有利于脫離對數(shù)據(jù)庫的依賴性(解耦)鸠真。改變底層數(shù)據(jù)庫野来,而上層數(shù)據(jù)查詢不用改變。

二. 用到的各種類

  1. Content Resolver

用來訪問ContentProvider的類庭砍,提供了和ContentProvider類中方法同名的方法,Content Provider只是用來提供數(shù)據(jù)的矮锈,而訪問就需要用Content Resolver豺旬。

  1. Content Provider

內(nèi)容提供器钠惩,可以用來給其他程序提供數(shù)據(jù),也可以充當(dāng)其數(shù)據(jù)存儲區(qū)和表格形式的數(shù)據(jù)外部顯示之間的抽象層族阅。主要用來提供對外訪問的接口(CRUD)篓跛。

  1. Uri

用來區(qū)分應(yīng)用程序、字段的URI坦刀,與RUL類似愧沟,分為一下三個(gè)部分,要想使用Content Provider提供的數(shù)據(jù)求泰,就必須通過Uri進(jìn)行訪問央渣,而且只能通過在ContentProvider中已經(jīng)添加(調(diào)用UriMatcher.addURI添加的)的Uri訪問,未添加的就不能訪問渴频。

  • schema:協(xié)議部分芽丹,Content Provider默認(rèn)協(xié)議是content://
  • authority:權(quán)限部分,其實(shí)就是用來區(qū)分是哪個(gè)Content Provider卜朗,通常是應(yīng)用的包名
  • path:用來區(qū)分?jǐn)?shù)據(jù)的路徑拔第,一般是表名,有以下形式:
    • 訪問多條數(shù)據(jù)(整個(gè)表)com.example.provider/table
    • 訪問某一行(rowId對應(yīng)的行)com.example.provider/table/rowId
  • PS:Uri的作用有很多场钉,不僅僅是用在這里蚊俺,還有如用瀏覽器打開鏈接的時(shí)候會用到
  1. Uri.Builder
    • 用來連綴構(gòu)建標(biāo)準(zhǔn)Uri的類(調(diào)用各種方法進(jìn)行參數(shù)的連綴),有如下常用方法:(其實(shí)這些方法很多都是重復(fù)的功能)
Uri.Builder scheme(String scheme)//給Uri添加schema參數(shù)
Uri.Builder authority(String authority)//給Uri添加authority參數(shù)
Uri.Builder path(String path)//給Uri添加path參數(shù)
Uri.Builder query(String query)//給Uri添加id參數(shù)(查詢參數(shù))
Uri build()//生成Uri
Uri.Builder appendPath(String newSegment)//添加path參數(shù)
  1. ContentUris
  • 用來給Uri追加id和讀取Uri中的id的幫助類逛万,有如下方法:
static Uri.Builder appendId(Uri.Builder builder, long id)//給Uri.Builder要構(gòu)建的Uri添加id
static long parseId(Uri contentUri)//解析Uri中的id
static Uri withAppendedId(Uri contentUri, long id)//給Uri添加d
  1. UriMatcher
    • Content Provider用來處理(匹配)不同Uri的類泳猬,創(chuàng)建Content Provider的時(shí)候就依靠UriMatcher進(jìn)行匹配從而確定對不同的Uri請求進(jìn)行不同的操作,有以下兩個(gè)方法:
/**
     * 添加一個(gè)要匹配的Uri宇植,可以用通配符得封,其中*用來通配一個(gè)字符,#用來匹配任何數(shù)字指郁。
     *
     * @param authority Uri的authority
     * @param 要匹配的路徑忙上,可以用通配符
     * @param 匹配成功時(shí)返回的數(shù)字,根據(jù)該數(shù)字進(jìn)行判斷和匹配
     */
    public void addURI(String authority, String path, int code)
/**
     * 進(jìn)行匹配的方法
     *
     * @param uri      要匹配的Uri
     *
     * @return  返回值闲坎,匹配到時(shí)返回addURI里面的第三個(gè)參數(shù)疫粥,匹配失敳绺(Content Provider未提供該uri)的時(shí)候返回-1
     */
    public int match(Uri uri)
  1. Uri的MIME類型(不知道是干嘛的,原諒我還沒學(xué)計(jì)算機(jī)網(wǎng)絡(luò)QAQ)
    • 構(gòu)成方法:
      • 以vnd開頭
      • 如URI以路徑結(jié)尾梗逮,則接android.cursor.dir项秉,如果以id結(jié)尾,則接android.cursor.item
      • 接著再接/vnd.<authority>.<path>
    • 如:
vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.book//路徑結(jié)尾
vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.book//id結(jié)尾

三. 訪問Content Provider提供的數(shù)據(jù)的步驟

  1. 構(gòu)建要訪問的Uri
    • 調(diào)用Uri.parse()將String轉(zhuǎn)化為Uri
    • 調(diào)用Uri.Builder方法進(jìn)行各種連綴構(gòu)造(其實(shí)可以不用調(diào)用這些方法库糠,直接寫好String然后parse成Uri就可以了伙狐,但是推薦做法是調(diào)用API中的函數(shù)進(jìn)行構(gòu)造)涮毫,一般有如下方式構(gòu)建Uri:(構(gòu)建Uri的方法很多瞬欧,這里只是兩種)
Uri uri = new Uri.Builder().scheme("content").authority("cn.foxnickel.databasedemo.provider").path("table1").build();//用Builder進(jìn)行構(gòu)造
Uri uri1 = Uri.parse("content://cn.foxnickel.databasedemo.provider/table/id");//用String構(gòu)建Uri
  1. 得到Content Resolver并調(diào)用CRUD方法進(jìn)行數(shù)據(jù)的操作
    • 得到Content Resolver(Context提供了getContentResolver方法用來得到Content Resolver)
ContentResolver contentResolver = Context.getContentResolver();
  • 調(diào)用Content Resolver的CRUD方法進(jìn)行數(shù)據(jù)操作,和SQLiteDatabase的CRUD方法一樣罢防。

三. 訪問系統(tǒng)提供的Content Provider

  • 訪問系統(tǒng)提供的Content Provider的時(shí)候就不需要自己構(gòu)建Uri艘虎,因?yàn)橄到y(tǒng)對各種Uri都是封裝好了的,只需要查找官方文檔就可以使用了
  • 常見的內(nèi)置Content Provider
    • 聯(lián)系人提供程序
    • 日歷提供查詢
    • 短信提供程序
    • 用戶字典提供程序
    • 媒體提供程序

四. 創(chuàng)建自己應(yīng)用的Content Provider

  1. 實(shí)現(xiàn)自己的Content Provider類咒吐,繼承自Content Provider基類
  2. 實(shí)現(xiàn)六個(gè)必須的抽象方法(onCreate野建,getType,insert恬叹,delete候生,update,query)
  3. 實(shí)例化UriMatcher绽昼,添加要提供的Uri
  4. 根據(jù)添加的Uri進(jìn)行判斷唯鸭,然后具體實(shí)現(xiàn)六個(gè)抽象方法
/**
 * 該類是ContentProvider的實(shí)現(xiàn)類,實(shí)現(xiàn)了對數(shù)據(jù)庫數(shù)據(jù)的共享
 */
public class DatabaseProvider extends ContentProvider {

    /**
     * 定義支持的Uri的匹配號
     */
    private static final int BOOK_DIR = 0;
    private static final int BOOK_ITEM = 1;
    private static final int CATEGORY_DIR = 2;
    private static final int CATEGORY_ITEM = 3;

    private static final String AUTHORITY = "cn.foxnickel.databasedemo.provider";
    private static UriMatcher mUriMatcher;//靜態(tài)的UriMatcher
    private MyDatabaseHelper mDatabaseHelper;//用來查詢數(shù)據(jù)庫數(shù)據(jù)的Helper
    private final String TAG = getClass().getSimpleName();
    private SQLiteDatabase db;//數(shù)據(jù)庫

    /**
     * 在靜態(tài)代碼塊中進(jìn)行Uri的添加
     */
    static {
        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        mUriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
        mUriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
        mUriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
        mUriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
    }

    /**
     * delete方法:刪除數(shù)據(jù)庫中的數(shù)據(jù)
     * @param uri 指定表或者行的Uri
     * @param selection 刪除的條件
     * @param selectionArgs selection中占位符的值
     * @return 返回刪除的某行的行號
     */
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.i(TAG, "content provider delete:" + uri);
        int deletedRows = 0;
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                deletedRows = db.delete("book",selection,selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deletedRows = db.delete("book","id = ?",new String[]{bookId});
                break;
            case CATEGORY_DIR:
                deletedRows = db.delete("category",selection,selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                deletedRows = db.delete("category","id = ?",new String[]{categoryId});
                break;
            default:
                break;
        }
        return deletedRows;
    }

    /**
     * 返回傳入U(xiǎn)ri的MIME類型
     * @param uri ContentResolver傳來的Uri
     * @return 該Uri對應(yīng)的MIME類型
     */
    @Override
    public String getType(Uri uri) {
        Log.i(TAG, "content provider getType:" + uri);
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.category";
            default:
                break;
        }
        return null;
    }

    /**
     * 插入數(shù)據(jù)
     * @param uri 指定表或者行的Uri
     * @param values 包裝了要插入數(shù)據(jù)的ContentValues
     * @return 用于表示新紀(jì)錄的Uri
     */
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.i(TAG, "content provider insert:" + uri);
        Uri uriReturn = null;
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
            case BOOK_ITEM:
                long newBookId = db.insert("book",null,values);
                uriReturn = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
                break;
            case CATEGORY_DIR:
            case CATEGORY_ITEM:
                long newCategoryId = db.insert("category",null,values);
                uriReturn = Uri.parse("content://"+AUTHORITY+"/category/"+newCategoryId);
                break;
            default:
                break;
        }
        return uriReturn;
    }

    /**
     * 當(dāng)ContentProvider第一次被調(diào)用的時(shí)候會調(diào)用onCreate方法硅确,一般在這里進(jìn)行數(shù)據(jù)庫的初始化
     * @return 是否初始化成功
     */
    @Override
    public boolean onCreate() {
        Log.i(TAG, "content provider onCreate: ");
        mDatabaseHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
        db = mDatabaseHelper.getWritableDatabase();
        return true;
    }

    /**
     * 查詢數(shù)據(jù)
     * @param uri 指定要查詢的表/列的Uri
     * @param projection 要查詢的列
     * @param selection where條件
     * @param selectionArgs selection條件中占位符的值
     * @param sortOrder 排序方式目溉,默認(rèn)為升序
     * @return 查詢到的游標(biāo)
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        Cursor cursor = null;
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                Log.i(TAG, "content provider query:" + uri);
                cursor = db.query("book", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                cursor = db.query("book", projection, "id = ?", new String[]{bookId}, null, null, sortOrder);
                break;
            case CATEGORY_DIR:
                cursor = db.query("category", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                cursor = db.query("category", projection, "id = ?", new String[]{categoryId}, null, null, sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }

    /**
     * 更新數(shù)據(jù)
     * @param uri 指定要更新的表/列的Uri
     * @param values 包裝了要更新數(shù)據(jù)的ContentValues
     * @param selection where條件
     * @param selectionArgs selection條件中占位符的值
     * @return 更新的行號
     */
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        Log.i(TAG, "content provider update:" + uri);
        int updatedRows = 0;
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                updatedRows = db.update("book",values,selection,selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                updatedRows = db.update("book",values,"is = ?",new String[]{bookId});
                break;
            case CATEGORY_DIR:
                updatedRows = db.update("category",values,selection,selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updatedRows = db.update("book",values,"is = ?",new String[]{categoryId});
                break;
            default:
                break;
        }
        return updatedRows;
    }
}
  1. manifest進(jìn)行聲明
<!--
            name:Content Provider的名字
            authorities:權(quán)限(包名)
            enable:是否啟用
            exported:是否能被其他軟件使用
        -->
        <provider
            android:name=".data.DatabaseProvider"
            android:authorities="cn.foxnickel.databasedemo.provider"
            android:enabled="true"
            android:exported="true">
        </provider>
  1. 創(chuàng)建完成之后就可以通過三中的調(diào)用方法進(jìn)行調(diào)用了

五. 源碼

Provider類

/**
 * 該類是ContentProvider的實(shí)現(xiàn)類,實(shí)現(xiàn)了對數(shù)據(jù)庫數(shù)據(jù)的共享
 */
public class DatabaseProvider extends ContentProvider {

    /**
     * 定義支持的Uri的匹配號
     */
    private static final int BOOK_DIR = 0;
    private static final int BOOK_ITEM = 1;
    private static final int CATEGORY_DIR = 2;
    private static final int CATEGORY_ITEM = 3;

    private static final String AUTHORITY = "cn.foxnickel.databasedemo.provider";
    private static UriMatcher mUriMatcher;//靜態(tài)的UriMatcher
    private MyDatabaseHelper mDatabaseHelper;//用來查詢數(shù)據(jù)庫數(shù)據(jù)的Helper
    private final String TAG = getClass().getSimpleName();
    private SQLiteDatabase db;//數(shù)據(jù)庫

    /**
     * 在靜態(tài)代碼塊中進(jìn)行Uri的添加
     */
    static {
        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        mUriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
        mUriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
        mUriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
        mUriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
    }

    /**
     * delete方法:刪除數(shù)據(jù)庫中的數(shù)據(jù)
     * @param uri 指定表或者行的Uri
     * @param selection 刪除的條件
     * @param selectionArgs selection中占位符的值
     * @return 返回刪除的某行的行號
     */
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.i(TAG, "content provider delete:" + uri);
        int deletedRows = 0;
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                deletedRows = db.delete("book",selection,selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deletedRows = db.delete("book","id = ?",new String[]{bookId});
                break;
            case CATEGORY_DIR:
                deletedRows = db.delete("category",selection,selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                deletedRows = db.delete("category","id = ?",new String[]{categoryId});
                break;
            default:
                break;
        }
        return deletedRows;
    }

    /**
     * 返回傳入U(xiǎn)ri的MIME類型
     * @param uri ContentResolver傳來的Uri
     * @return 該Uri對應(yīng)的MIME類型
     */
    @Override
    public String getType(Uri uri) {
        Log.i(TAG, "content provider getType:" + uri);
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.category";
            default:
                break;
        }
        return null;
    }

    /**
     * 插入數(shù)據(jù)
     * @param uri 指定表或者行的Uri
     * @param values 包裝了要插入數(shù)據(jù)的ContentValues
     * @return 用于表示新紀(jì)錄的Uri
     */
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.i(TAG, "content provider insert:" + uri);
        Uri uriReturn = null;
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
            case BOOK_ITEM:
                long newBookId = db.insert("book",null,values);
                uriReturn = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
                break;
            case CATEGORY_DIR:
            case CATEGORY_ITEM:
                long newCategoryId = db.insert("category",null,values);
                uriReturn = Uri.parse("content://"+AUTHORITY+"/category/"+newCategoryId);
                break;
            default:
                break;
        }
        return uriReturn;
    }

    /**
     * 當(dāng)ContentProvider第一次被調(diào)用的時(shí)候會調(diào)用onCreate方法菱农,一般在這里進(jìn)行數(shù)據(jù)庫的初始化
     * @return 是否初始化成功
     */
    @Override
    public boolean onCreate() {
        Log.i(TAG, "content provider onCreate: ");
        mDatabaseHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
        db = mDatabaseHelper.getWritableDatabase();
        return true;
    }

    /**
     * 查詢數(shù)據(jù)
     * @param uri 指定要查詢的表/列的Uri
     * @param projection 要查詢的列
     * @param selection where條件
     * @param selectionArgs selection條件中占位符的值
     * @param sortOrder 排序方式缭付,默認(rèn)為升序
     * @return 查詢到的游標(biāo)
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        Cursor cursor = null;
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                Log.i(TAG, "content provider query:" + uri);
                cursor = db.query("book", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                cursor = db.query("book", projection, "id = ?", new String[]{bookId}, null, null, sortOrder);
                break;
            case CATEGORY_DIR:
                cursor = db.query("category", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                cursor = db.query("category", projection, "id = ?", new String[]{categoryId}, null, null, sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }

    /**
     * 更新數(shù)據(jù)
     * @param uri 指定要更新的表/列的Uri
     * @param values 包裝了要更新數(shù)據(jù)的ContentValues
     * @param selection where條件
     * @param selectionArgs selection條件中占位符的值
     * @return 更新的行號
     */
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        Log.i(TAG, "content provider update:" + uri);
        int updatedRows = 0;
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                updatedRows = db.update("book",values,selection,selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                updatedRows = db.update("book",values,"is = ?",new String[]{bookId});
                break;
            case CATEGORY_DIR:
                updatedRows = db.update("category",values,selection,selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updatedRows = db.update("book",values,"is = ?",new String[]{categoryId});
                break;
            default:
                break;
        }
        return updatedRows;
    }
}

調(diào)用Provider的類

public void onClick(View v) {
        ContentResolver contentResolver = getContentResolver();
        switch (v.getId()) {
            case R.id.add_data:
                ContentValues values = new ContentValues();
                values.put("name","A Clash of Kings");
                values.put("author","George Martin");
                values.put("pages",1040);
                values.put("price",22.85);
                Uri newUri = contentResolver.insert(Uri.parse(AUTHORITY+"/book"),values);
                newId = newUri.getPathSegments().get(1);
                break;
            case R.id.query_data:
                Cursor cursor = null;
                cursor = contentResolver.query(Uri.parse(AUTHORITY+"/book"),null,null,null,null);
                if(cursor!=null){
                    while (cursor.moveToNext()){
                        Log.i(TAG, "onClick: name: "+cursor.getString(cursor.getColumnIndex("name")));
                        Log.i(TAG, "onClick: author: "+cursor.getString(cursor.getColumnIndex("author")));
                        Log.i(TAG, "onClick: price: "+cursor.getFloat(cursor.getColumnIndex("price")));
                        Log.i(TAG, "onClick: pages: "+cursor.getString(cursor.getColumnIndex("pages")));
                    }
                    cursor.close();
                }
                break;
            case R.id.update_data:
                ContentValues newValues = new ContentValues();
                newValues.put("name","A Storm of sWords");
                contentResolver.update(Uri.parse(AUTHORITY+"/book"),newValues,"pages = ?",new String[]{"1040"});
                break;
            case R.id.delete_data:
                contentResolver.delete(Uri.parse(AUTHORITY+"/book/"+1),null,null);
                break;
            default:
                break;
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市循未,隨后出現(xiàn)的幾起案子陷猫,更是在濱河造成了極大的恐慌,老刑警劉巖的妖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绣檬,死亡現(xiàn)場離奇詭異,居然都是意外死亡羔味,警方通過查閱死者的電腦和手機(jī)河咽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赋元,“玉大人忘蟹,你說我怎么就攤上這事飒房。” “怎么了媚值?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵狠毯,是天一觀的道長。 經(jīng)常有香客問我褥芒,道長嚼松,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任锰扶,我火速辦了婚禮献酗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘坷牛。我一直安慰自己罕偎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布京闰。 她就那樣靜靜地躺著颜及,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蹂楣。 梳的紋絲不亂的頭發(fā)上俏站,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音痊土,去河邊找鬼肄扎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛施戴,可吹牛的內(nèi)容都是我干的反浓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼赞哗,長吁一口氣:“原來是場噩夢啊……” “哼雷则!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肪笋,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤月劈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后藤乙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猜揪,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年坛梁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了而姐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡划咐,死狀恐怖拴念,靈堂內(nèi)的尸體忽然破棺而出钧萍,到底是詐尸還是另有隱情,我是刑警寧澤政鼠,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布风瘦,位于F島的核電站,受9級特大地震影響公般,放射性物質(zhì)發(fā)生泄漏万搔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一官帘、第九天 我趴在偏房一處隱蔽的房頂上張望瞬雹。 院中可真熱鬧,春花似錦遏佣、人聲如沸挖炬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至馅巷,卻和暖如春膛虫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钓猬。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工稍刀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人敞曹。 一個(gè)月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓账月,卻偏偏與公主長得像,于是被迫代替她去往敵國和親澳迫。 傳聞我的和親對象是個(gè)殘疾皇子局齿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

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