Android入門(mén)(9)| 四大組件之內(nèi)容提供器

內(nèi)容提供器(Content Provider)主要用于在不同的應(yīng)用程序之間實(shí)現(xiàn)數(shù)據(jù)數(shù)據(jù)共享的功能往衷,同時(shí)這也是Android實(shí)現(xiàn)跨程序共享數(shù)據(jù)的標(biāo)準(zhǔn)方式肢藐。盡管之前學(xué)習(xí)的SharedPreference的數(shù)據(jù)存儲(chǔ)也能夠?qū)崿F(xiàn)讀寫(xiě)操作,但是內(nèi)容提供器可以選擇對(duì)哪一部分的數(shù)據(jù)進(jìn)行共享,因此內(nèi)容提供器有著更好的保護(hù)隱私的作用。

使用系統(tǒng)內(nèi)容提供器

如果一個(gè)應(yīng)用程序通過(guò)內(nèi)容提供器對(duì)其數(shù)據(jù)提供了外部訪問(wèn)借接口雪位,那么其他的程序就能夠通過(guò)該接口來(lái)對(duì)這些數(shù)據(jù)進(jìn)行訪問(wèn)。Android系統(tǒng)中自帶的電話簿梨撞、相冊(cè)雹洗、短信等應(yīng)用都設(shè)置內(nèi)容提供器香罐,所以我們的程序都可以來(lái)利用這些數(shù)據(jù)。

1.內(nèi)容URI的組成

由于是其他的程序在訪問(wèn)內(nèi)容提供器中的數(shù)據(jù)时肿,所以需要用有一定格式規(guī)范的內(nèi)容URI來(lái)代替庇茫,我們先來(lái)了解一下什么是URI吧。

內(nèi)容URI給內(nèi)容提供器中的數(shù)據(jù)建立了唯一的表示符螃成,它主要是由兩部分組成:authority和path旦签。authority是用于對(duì)不同的應(yīng)用程序做區(qū)分的,而path則是用于對(duì)不同的表做區(qū)分的寸宏。舉例來(lái)說(shuō)假設(shè)某個(gè)程序中有兩張表table 1和table 2宁炫,該程序的包為com.example.app,那該程序的兩個(gè)內(nèi)容URI的標(biāo)準(zhǔn)寫(xiě)法就是:

content://com.example.app.provider/table 1
content://cmom.example.app.provider/table 2

在得到了內(nèi)容URI之后氮凝,我們還需要將其解析為Uri對(duì)象才可以作為參數(shù)來(lái)進(jìn)行傳入羔巢,具體的解析方法是:

Uri uri1 = Uri.parse("content://com.example.app.provider/table 1");
Uri uri2 = Uri.parse("content://com.example.app.provider/table 2");

我們只用調(diào)用Uri中的parse()方法即可將其解析為Uri對(duì)象,之后我們就可以對(duì)該對(duì)象進(jìn)行操作了罩阵。

2.ContentResolve類

如果想要訪問(wèn)內(nèi)容提供器中的內(nèi)容竿秆,我們就需要使用ContentResolve類,使用該類的getContentResolve()方法就會(huì)返回一個(gè)該類的實(shí)例永脓。有了該類的實(shí)例我們就可以對(duì)其進(jìn)行CRUD操作袍辞,具體的方法仍然是查詢query()鞋仍、添加insert()常摧、更新update()和刪除delete()。但是這里的CRUD操作所使用的參數(shù)和之前的SQLite有所不同威创,我們這里不再用單純的表名指明被操作的表落午,而是使用內(nèi)容URI來(lái)進(jìn)行來(lái)當(dāng)參數(shù)進(jìn)行操作了。具體操作的方法和SQLite中的很相似肚豺,就用一張圖來(lái)概括:

方法操作

3.實(shí)踐:讀取系統(tǒng)聯(lián)系人

知道了使用方法溃斋,下面就來(lái)具體的實(shí)踐一下吧。我們這里是用我們的程序來(lái)調(diào)用系統(tǒng)的電話簿中的聯(lián)系人并將他們的姓名和電話號(hào)碼顯示在我們的程序當(dāng)中吸申。先創(chuàng)建一個(gè)空項(xiàng)目梗劫,然后為布局加上一個(gè)ListView,然后修改主代碼:

package com.example.yzbkaka.contasttest;

import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    List<String> contactList = new ArrayList<String>();
    ArrayAdapter<String> adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ListView contaseView = (ListView)findViewById(R.id.list_view);
        adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,contactList);
        contaseView.setAdapter(adapter);

        if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this,new String[] {Manifest.permission.READ_CONTACTS},1);
        }
        else{
            readContast();
        }
    }

    private void readContast(){
        Cursor cursor = null;
        try{
            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
            if(cursor != null){
                while(cursor.moveToNext()){
                    String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                    String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                    contactList.add(displayName+"\n"+number);
                }
                adapter.notifyDataSetChanged();
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            if(cursor != null){
                cursor.close();
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,String[] permission,int[] grantResults){
        switch(requestCode){
            case 1:
                if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    readContast();
                }
                else{
                    Toast.makeText(this, "you denied the permission", Toast.LENGTH_SHORT).show();
                }
        }
    }
}

這個(gè)代碼看起來(lái)很長(zhǎng)截碴,但是其中的onRequestPermissionsResult()方法和中間申請(qǐng)權(quán)限的代碼都在技能總結(jié)篇(2)中見(jiàn)到過(guò)梳侨。這里主要是看一下readContast()中的操作,我們先是使用getContentResolver()方法返回一個(gè)Cursor對(duì)象日丹,接著是使用query()方法來(lái)進(jìn)行查詢走哺,這個(gè)方法中我們傳入的ContactsContract.Common DataKinds.Phone.CONTENT_URI類是已經(jīng)幫我們封裝好的Uri對(duì)象。接著我們就對(duì)cursor對(duì)象進(jìn)行便利哲虾,將其中的聯(lián)系人的姓名和號(hào)碼讀取出來(lái)丙躏,在使用getColumnIndex()方法時(shí)我們傳入的參數(shù)仍然是系統(tǒng)為我們封裝好的常量择示,我們直接使用即可。最后就是將這些信息傳入到List中展示出來(lái)晒旅。

最后要記住危險(xiǎn)權(quán)限的聲明一定要在AndroidManifest.xml中進(jìn)行注冊(cè):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yzbkaka.contasttest">
    
    <uses-permission android:name="android.permission.READ_CONTACTS"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

創(chuàng)建自己的內(nèi)容提供器

創(chuàng)建自己的內(nèi)容提供器可以讓我們的程序中的數(shù)據(jù)共享給其他的程序栅盲,我們這里使用的數(shù)據(jù)庫(kù)是上一節(jié)中使用的Book.dp,我們的創(chuàng)建將會(huì)以它為基礎(chǔ)來(lái)進(jìn)行废恋。打開(kāi)那個(gè)項(xiàng)目剪菱,然后新建一個(gè)DatabaseProvider類,并讓它繼承自ContentProvider類拴签,接著我們?cè)陬惖拈_(kāi)始新建幾個(gè)變量:

    public static final int BOOK_DIR = 0;
    public static final int BOOK_ITEM = 1;
    public static final int CATEGORY_DIR = 2;
    public static final int CATEGORY_ITEM = 3;
    public static final String AUTHORITY = "com.example.databasetest.provider";
    public static UriMatcher uriMatcher;
    MyDatabaseHelper dbHelper;
    
    static{
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY,"book",BOOK_DIR);
        uriMatcher.addURI(AUTHORITY,"book/#",BOOK_ITEM);
        uriMatcher.addURI(AUTHORITY,"category",CATEGORY_DIR);
        uriMatcher.addURI(AUTHORITY,"category/#",CATEGORY_ITEM);
    }

我們首先定義了4個(gè)常量孝常,BOOK_DIR表示訪問(wèn)Book表中的所有數(shù)據(jù),BOOK_ITEM表示訪問(wèn)Book表中的單條數(shù)據(jù)蚓哩,CATEGORY_DIR表示訪問(wèn)Category中的全部數(shù)據(jù)构灸,CATEGORY_ITEM表示訪問(wèn)Category中的單條數(shù)據(jù)。接著我們?cè)陟o態(tài)代碼塊中定義了一個(gè)UriMatcher實(shí)例岸梨,利用它的addURI()方法我們可以將期望匹配的幾種URI格式添加了進(jìn)去喜颁,這個(gè)方法需要傳入三個(gè)參數(shù),可以分別把a(bǔ)uthority曹阔、path和一個(gè)自定義的代碼傳進(jìn)去半开。這樣當(dāng)調(diào)用UriMatcher的match()方法時(shí)就可以將一個(gè)Uri對(duì)象傳入,而返回值就是一個(gè)之前自定義的代碼赃份,利用這個(gè)代碼我們就可以判斷出調(diào)用方需要的是那一項(xiàng)數(shù)據(jù)了寂拆。

接著我們來(lái)分別實(shí)現(xiàn)繼承過(guò)來(lái)的方法,首先是onCreate()方法:

 @Override
    public boolean onCreate() {
        dbHelper = new MyDatabaseHelper(getContext(),"BookStore.db",null,2);
        return true;
    }

在這一步中我們是定義了dbHelper抓韩,并且讓它創(chuàng)建了一個(gè)BookStore.db的數(shù)據(jù)庫(kù)纠永,然后是返回true表示內(nèi)容提供器初始化成功。接著來(lái)寫(xiě)query()方法:

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Cursor cursor = null;
        switch(uriMatcher.match(uri)){
            case BOOK_DIR:
                cursor = db.query("Book",projection,selection,selectionArgs,null,null,sortOrder);
                break;
            case BOOK_ITEM:
                cursor = db.query("Book",projection,selection,selectionArgs,null,null,sortOrder);
                break;
            case CATEGORY_DIR:
                cursor = db.query("Category",projection,selection,selectionArgs,null,null,sortOrder);
                break;
            case CATEGORY_ITEM:
                cursor = db.query("Category",projection,selection,selectionArgs,null,null,sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }

在這個(gè)方法中需要我們傳入5個(gè)參數(shù)谒拴,第一個(gè)是Uri對(duì)象尝江,第二個(gè)是查詢的列,第三個(gè)和第四個(gè)是用于約束查詢哪些行英上,第五個(gè)是對(duì)結(jié)果的排序方式炭序。我們?cè)谶@個(gè)方法里面首先是得到了一個(gè)SQLiteDatabase對(duì)象,并定義了一個(gè)Cursor苍日,之后我們?cè)龠M(jìn)行匹配惭聂,當(dāng)匹配成功時(shí),我們就使用SQLiteDatabase中的query()方法來(lái)進(jìn)行查詢易遣,該方法會(huì)返回一個(gè)Cursor對(duì)象彼妻。在最后我們會(huì)返回一個(gè)Cursor對(duì)象。

接著來(lái)看insert()方法:

 @Override
    public Uri insert(Uri uri, ContentValues contentValues) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Uri uriReturn = null;
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
            case BOOK_ITEM:
                long newBookId = db.insert("Book",null,contentValues);
                uriReturn = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
                break;
            case CATEGORY_DIR:
            case CATEGORY_ITEM:
                long newCategoryId = db.insert("Category",null,contentValues);
                uriReturn = Uri.parse("context://"+AUTHORITY+"/category/"+newCategoryId);
                break;
            default:
                break;
        }
        return uriReturn;
    }

在這個(gè)方法中也是先獲取SQLiteDatabase對(duì)象,然后再來(lái)根據(jù)uri進(jìn)行匹配侨歉。但是要注意的是這個(gè)方法要求返回一個(gè)Uri的對(duì)象屋摇,因此我們需要使用parse()方法來(lái)進(jìn)行轉(zhuǎn)換,不過(guò)這個(gè)內(nèi)容的URI是以新增數(shù)據(jù)的id結(jié)尾的幽邓。

接下來(lái)就是udata()方法:

@Override
    public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int updateDows = 0;
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                updateDows = db.update("Book",contentValues,selection,selectionArgs);
                break;
            case BOOK_ITEM:
                String newBookId = uri.getPathSegments().get(1);
                updateDows = db.update("Book",contentValues,"id=?",new String[] {newBookId});
                break;
            case CATEGORY_DIR:
                updateDows = db.update("Book",contentValues,selection,selectionArgs);
                break;
            case CATEGORY_ITEM:
                String newCategoryId = uri.getPathSegments().get(1);
                updateDows = db.update("Book",contentValues,"id=?",new String[]{newCategoryId});
                break;
            default:
                break;
        }
        return updateDows;
    }

updata()方法也是和前面幾個(gè)相似炮温,先是獲取SQLiteDatabase對(duì)象,然后再來(lái)根據(jù)uri進(jìn)行匹配牵舵。這個(gè)方法需要返回一個(gè)整數(shù)代表的事更新的行數(shù)柒啤,所以在匹配的時(shí)候我們使用了一個(gè)getPathSegments()的方法,該方法可以把URI中的的權(quán)限之后的部分以“/”分割畸颅,并把分割后的結(jié)果放在一個(gè)字符串列表中担巩,所以這個(gè)列表第0個(gè)位置存放的就是路徑,第1個(gè)位置存放的就是id了没炒。

然后是delete()方法了:

@Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int deleteRows = 0;
        switch(uriMatcher.match(uri)){
            case BOOK_DIR:
                deleteRows = db.delete("Book",selection,selectionArgs);
                break;
            case  BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deleteRows = db.delete("Book","id = ?",new String[] {bookId});
                break;
            case CATEGORY_DIR:
                deleteRows = db.delete("Category",selection,selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                deleteRows = db.delete("Category","id = ?",new String[]{categoryId});
                break;
            default:
                break;
        }
        return deleteRows;
    }

這個(gè)方法也很簡(jiǎn)單涛癌,就不多說(shuō)了。

最后就是getType()方法了:

 @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vand.com.example.databasetest.provider.book";
            case BOOK_ITEM:
                return "vnd.android.cursor.dir/vand.com.example.databasetest.provider.book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vand.com.example.databasetest.provider.CATEGORY";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.dir/vand.com.example.databasetest.provider.CATEGORY";
                
        }
        return null;
    }

這個(gè)方法是用于獲取Uri對(duì)象所對(duì)應(yīng)的MIME類型送火,一個(gè)內(nèi)容Uri所對(duì)應(yīng)的MIME字符串主要是由三部分構(gòu)成:Android對(duì)這三個(gè)部分做了以下格式規(guī)定:必須以vnd開(kāi)頭拳话;如果內(nèi)容URI以路徑結(jié)尾,則后接android.cursor.dir/种吸,如果內(nèi)容URI以id結(jié)尾弃衍,則后接android.cursor.item/;最后接上vnd.< authority>.< path>坚俗。所以我們把這四個(gè)添加進(jìn)去镜盯。

最后內(nèi)容提供器一定要在AndroidManifst.xml中進(jìn)行注冊(cè):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yzbkaka.databasetest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <provider
            android:authorities="com.example.databasttest.provider"
            android:name=".DatabaseProvider"
            android:exported="true"
            android:enabled="true">
        </provider>

    </application>

</manifest>

我們?cè)?lt;application>標(biāo)簽中添加了<provider>,其中authorities就是指它的authority坦冠,name指定了DatabaseProvider的類名形耗,exported表示是否運(yùn)行外部程序訪問(wèn)我們的內(nèi)容提供器哥桥,enable表示是否啟用該內(nèi)容提供器辙浑。

最后我們運(yùn)行程序,然后退出程序拟糕,再寫(xiě)一個(gè)調(diào)用該程序的測(cè)試程序判呕,先在布局文件里面添加4個(gè)按鈕分別表示CRUE,然后修改主代碼:

import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private String newId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button addData = (Button) findViewById(R.id.add_data);
        addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 添加數(shù)據(jù)
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
                ContentValues values = new ContentValues();
                values.put("name", "A Clash of Kings");
                values.put("author", "George Martin");
                values.put("pages", 1040);
                values.put("price", 55.55);
                Uri newUri = getContentResolver().insert(uri, values);
                newId = newUri.getPathSegments().get(1);
            }
        });
        Button queryData = (Button) findViewById(R.id.query_data);
        queryData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 查詢數(shù)據(jù)
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
                Cursor cursor = getContentResolver().query(uri, null, null, null, null);
                if (cursor != null) {
                    while (cursor.moveToNext()) {
                        String name = cursor.getString(cursor. getColumnIndex("name"));
                        String author = cursor.getString(cursor. getColumnIndex("author"));
                        int pages = cursor.getInt(cursor.getColumnIndex ("pages"));
                        double price = cursor.getDouble(cursor. getColumnIndex("price"));
                        Log.d("MainActivity", "book name is " + name);
                        Log.d("MainActivity", "book author is " + author);
                        Log.d("MainActivity", "book pages is " + pages);
                        Log.d("MainActivity", "book price is " + price);
                    }
                    cursor.close();
                }
            }
        });
        Button updateData = (Button) findViewById(R.id.update_data);
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 更新數(shù)據(jù)
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
                ContentValues values = new ContentValues();
                values.put("name", "A Storm of Swords");
                values.put("pages", 1216);
                values.put("price", 24.05);
                getContentResolver().update(uri, values, null, null);
            }
        });
        Button deleteData = (Button) findViewById(R.id.delete_data);
        deleteData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 刪除數(shù)據(jù)
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
                getContentResolver().delete(uri, null, null);
            }
        });
    }

}

最后運(yùn)行程序送滞,調(diào)用數(shù)據(jù)成功O啦荨!犁嗅!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末边涕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌功蜓,老刑警劉巖园爷,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異式撼,居然都是意外死亡童社,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)著隆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扰楼,“玉大人,你說(shuō)我怎么就攤上這事美浦∠依担” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵浦辨,是天一觀的道長(zhǎng)腾节。 經(jīng)常有香客問(wèn)我,道長(zhǎng)荤牍,這世上最難降的妖魔是什么案腺? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮康吵,結(jié)果婚禮上劈榨,老公的妹妹穿的比我還像新娘。我一直安慰自己晦嵌,他們只是感情好同辣,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著惭载,像睡著了一般旱函。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上描滔,一...
    開(kāi)封第一講書(shū)人閱讀 52,736評(píng)論 1 312
  • 那天棒妨,我揣著相機(jī)與錄音,去河邊找鬼含长。 笑死券腔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拘泞。 我是一名探鬼主播纷纫,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼陪腌!你這毒婦竟也來(lái)了辱魁?” 一聲冷哼從身側(cè)響起烟瞧,我...
    開(kāi)封第一講書(shū)人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎染簇,沒(méi)想到半個(gè)月后燕刻,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剖笙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年卵洗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弥咪。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡过蹂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出聚至,到底是詐尸還是另有隱情酷勺,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布扳躬,位于F島的核電站脆诉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏贷币。R本人自食惡果不足惜击胜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望役纹。 院中可真熱鬧偶摔,春花似錦、人聲如沸促脉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瘸味。三九已至宫仗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旁仿,已是汗流浹背藕夫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丁逝,地道東北人汁胆。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像霜幼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子誉尖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

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