ContentProvider實(shí)踐

ContentProvider

簡(jiǎn)介

ContentProvider(內(nèi)容提供者)是提供跨程序數(shù)據(jù)訪問(wèn)的Android組件靖避。簡(jiǎn)而言之潭枣,相當(dāng)于一個(gè)數(shù)據(jù)接口,可以將程序內(nèi)部的數(shù)據(jù)向其他應(yīng)用程序公開(kāi)幻捏,讓其他應(yīng)用程序可以增刪查改操作盆犁。
Android原生的應(yīng)用很多都應(yīng)用了ContentProvider例如通訊錄,文件系統(tǒng)等篡九。

存在的原因

  1. 解決應(yīng)用程序的數(shù)據(jù)庫(kù)在私有目錄下谐岁,其他應(yīng)用程序使用常規(guī)方法無(wú)法訪問(wèn)數(shù)據(jù)庫(kù)。
  2. 程序內(nèi)部的數(shù)據(jù)庫(kù)結(jié)構(gòu)復(fù)雜榛臼,即使可以訪問(wèn)伊佃,但是如果不了解表的構(gòu)造也很難訪問(wèn),所以通過(guò)ContentProvider封裝對(duì)數(shù)據(jù)的訪問(wèn)沛善,給外部程序訪問(wèn)數(shù)據(jù)的一個(gè)直觀的接口航揉。
  3. 如果直接提供其他應(yīng)用程序訪問(wèn)數(shù)據(jù)的權(quán)限不加以控制,那么數(shù)據(jù)安全也是一個(gè)問(wèn)題金刁,所以通過(guò)ContentProvider即可以提供數(shù)據(jù)接口并且可以控制訪問(wèn)權(quán)限帅涂。

簡(jiǎn)單的使用

主要步驟:數(shù)據(jù)庫(kù)提供方:繼承ContentProvider實(shí)現(xiàn)有關(guān)數(shù)據(jù)庫(kù)的相關(guān)操作议薪,然后再AndroidMainfest.xml文件中注冊(cè)。 數(shù)據(jù)庫(kù)訪問(wèn)方: 通過(guò)'URI'訪問(wèn)對(duì)應(yīng)的ContentProvider媳友。

PS: 一定要在AndroidMainfest.xml文件中注冊(cè)斯议,每一個(gè)ContentProvider對(duì)應(yīng)一個(gè)authorities,需要注意的是authorities在整個(gè)系統(tǒng)必須是唯一的,否則安裝會(huì)有沖突庆锦。并且要注明是android:exported="true"才能被外部訪問(wèn)。
注冊(cè)例子如下:

 <provider
            android:name="com.doris.support.provider.SettingsProvider"
            android:authorities="com.doris.contact.provider"
            android:exported="true" >
        </provider>

上面只是粗略的總結(jié)了一下用法轧葛,不過(guò)肯定這幾行字搂抒,你們還是不太清楚具體如何使用。接下來(lái)我會(huì)一步一步的帶著大家來(lái)自定義ContentProvider以及使用它尿扯。接下來(lái)要長(zhǎng)篇大論了求晶,做好準(zhǔn)備啦,千萬(wàn)別暈衷笋!

這里我們統(tǒng)一一下芳杏,把提供數(shù)據(jù)的應(yīng)用程序稱之為服務(wù)端,把請(qǐng)求數(shù)據(jù)的應(yīng)用程序稱之為客戶端辟宗。

服務(wù)端

  • 在服務(wù)器自定義一個(gè)ContentProvider提供數(shù)據(jù)訪問(wèn)接口爵赵。

1.自定義ContentProvider

自定義ContentPorvider繼承自ContentPorvider,這里我們自定義一個(gè)ContactProvider用于獲取Contact數(shù)據(jù)表的數(shù)據(jù),Contact表用于存儲(chǔ)聯(lián)系人信息泊脐。先貼一下完整代碼:

package com.doris.provide;

import com.orhanobut.logger.Logger;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;

/**
 * Created by Doris on 2017/2/24.
 */

public class ContactProvider extends ContentProvider {
    public static final int     CODE_CONTACT            = 0X108;
    private static final String TAG                         = ContactProvider.class.getSimpleName();
    private static final String AUTHORITYS = "com.doris.contact.provider";
    private static final String TABLE_NAME = "Contact";
    //通過(guò)UriMatcher匹配Uri從而執(zhí)行對(duì)應(yīng)的操作空幻。
    private static UriMatcher   mUriMatcher                 = new UriMatcher(UriMatcher.NO_MATCH);
    static {
        mUriMatcher.addURI(AUTHORITYS, TABLE_NAME, CODE_CONTACT);
    }
    @Override
    public boolean onCreate() {
        Logger.d(TAG, "onCreate");
        return true;
    }
    
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Cursor cursor = null;
        switch (mUriMatcher.match(uri)) {
            case CODE_CONTACT :
                cursor = DBHelper.getInstance().query(TABLE_NAME, selection, selectionArgs, sortOrder);
                break;
        }
        return cursor;
    }
    
    @Override
    public String getType(Uri uri) {
        return uri.toString();
    }
    
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        switch (mUriMatcher.match(uri)) {
            case static {
        mUriMatcher.addURI(AUTHORITYS, TABLE_NAME, CODE_CONTACT);
    } : {
                long rowId2 = DBHelper.getInstance().insert(TABLE_NAME, values);
                if (rowId2 > 0) {
                    Uri noteUri = ContentUris.withAppendedId(uri, rowId2);
                    getContext().getContentResolver().notifyChange(noteUri, null);
                    return noteUri;
                }
            }
                break;
        }
        
        return null;
    }
    
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int count = 0;
        switch (mUriMatcher.match(uri)) {
            case CODE_CONTACT :
                count = DBHelper.getInstance().delete(TABLE_NAME, selection, selectionArgs);
                break;
        }
        return count;
    }
    
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        int count = 0;
        switch (mUriMatcher.match(uri)) {
            case CODE_CONTACT :
                count = DBHelper.getInstance().update(CPosterManager.TABLE_NAME, values, selection, selectionArgs);
                break;
        }
        return count;
    }
}

在上面這段代碼中,我們自定義了一個(gè)ContactProvider提供了對(duì)Contact表的增刪查改操作容客,而增刪查改動(dòng)作又是通過(guò)DBHelper類來(lái)實(shí)現(xiàn)的秕铛,DBHelper類封裝了對(duì)數(shù)據(jù)庫(kù)的操作。
我們來(lái)解析一下代碼缩挑。以上代碼的關(guān)鍵就是UriMatch如其名但两,用來(lái)匹配Uri來(lái)解讀對(duì)應(yīng)的操作CODE,從而對(duì)應(yīng)到某個(gè)操作上供置。因?yàn)槠渌绦蚨际峭ㄟ^(guò)uri來(lái)訪問(wèn)ContactProvider的所以需要匹配區(qū)分谨湘,訪問(wèn)同一個(gè)Provider的時(shí)候分別是要請(qǐng)求什么數(shù)據(jù)。比如:content:\\com.doris.contact.provider\contact,通過(guò)UriMathc匹配則是匹配到CODE_CONTACT.
ContactProvider里面只有一個(gè)CODE就是CODE_CONTACT芥丧,通過(guò)這個(gè)來(lái)標(biāo)示操作Contact表悲关。

static {
      //在這句代碼里面就是通過(guò)addURI來(lái)添加UriMatcher的一個(gè)匹配規(guī)則。
      mUriMatcher.addURI(AUTHORITYS, TABLE_NAME, CODE_CONTACT);
  }
  
  //addURI的方法如下:
  
  /**
   * Add a URI to match, and the code to return when this URI is
   * matched. URI nodes may be exact match string, the token "*"
   * that matches any text, or the token "#" that matches only
   * numbers.
   * <p>
   * Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
   * this method will accept a leading slash in the path.
   *
   * @param authority the authority to match
   * @param path the path to match. * may be used as a wild card for
   * any text, and # may be used as a wild card for numbers.
   * @param code the code that is returned when a URI is matched
   * against the given components. Must be positive.
   */
  public void addURI(String authority, String path, int code){
      .......
  }
  
  //從上面的注解可以知道娄柳,addURI就是用于添加一個(gè)匹配的URi給URIMatch寓辱,第三個(gè)參數(shù)code就是當(dāng)uri匹配的時(shí)候返回的CODE。然后開(kāi)發(fā)人員通過(guò)CODE 來(lái)做相應(yīng)的處理赤拒。

所以如果你想要ContentProvider能夠接受訪問(wèn)某個(gè)表的請(qǐng)求是就要用addURI添加匹配規(guī)則秫筏,這樣就其他程序就可以通過(guò)這個(gè)ContentProvider來(lái)訪問(wèn)該表的數(shù)據(jù)了诱鞠。Are you get it?

在上面的代碼中的query,delete, insert,update方法中都是利用UriMatch來(lái)匹配第一個(gè)uri參數(shù)这敬,然后根據(jù)返回的Code來(lái)匹配的對(duì)應(yīng)的操作航夺。這里當(dāng)我們匹配到是CODE_CONTACT則通過(guò)DBHelper來(lái)操作Contact表,從這里也可以看出來(lái)一個(gè)ContentProvider可以對(duì)應(yīng)多個(gè)表崔涂,多個(gè)數(shù)據(jù)阳掐。

  • 在服務(wù)端AndroidMainfest.xml文件里面注冊(cè)自定義的ContentProvider

    ContentProvider也是Android組件之一,所以也需要在AndroidMainfest.xml文件中聲明才能使用冷蚂。聲明語(yǔ)句如下:

<provider
          android:name="com.doris.support.provider.ContactProvider"
          android:authorities="com.doris.contact.provider"
          android:exported="true" >
      </provider>

此處的authorities是類似于ContactProvider的訪問(wèn)鏈接或者是可以理解為一個(gè)指針當(dāng)你在uri組合這個(gè)authorities來(lái)使用的時(shí)候就指向了ContactProvider缭保。android:exported="true"這句也是很關(guān)鍵的一句,它表明你同意將這個(gè)Provider被外部程序訪問(wèn)蝙茶,如果這個(gè)是false則其他應(yīng)用就不能訪問(wèn)數(shù)據(jù)艺骂,所以一定要是true

經(jīng)過(guò)上面兩步隆夯,你就可以在其他程序中訪問(wèn)ContactProvider了钳恕。至于DBHelper其實(shí)就是一個(gè)訪問(wèn)數(shù)據(jù)庫(kù)的幫助類。

客戶端

其實(shí)客戶端訪問(wèn)ContentProvder是通過(guò)ContentResolver來(lái)訪問(wèn)的蹄衷,這個(gè)類就是通過(guò)URI來(lái)跨進(jìn)程連接ContentProvider的忧额。所以客戶端要訪問(wèn)需要通過(guò)ContentResolver來(lái)請(qǐng)求數(shù)據(jù)±⒖冢客戶端示例程序如下:

// 考慮到程序的拓展性宙址,多態(tài)等特性我們先寫一個(gè)抽象的類里面對(duì)ContentResolver訪問(wèn)數(shù)據(jù)進(jìn)行了封裝。
public  abstract class BaseContentHelper {
    
    protected Context mContext;
    
    public BaseContentHelper(Context context) {
        this.mContext = context.getApplicationContext();
    }
    
    protected abstract Uri getUri();
    
    public Cursor query(String[] projection, String whereSql, String[] whereValue, String orderBy) {
        ContentResolver contentResolver = mContext.getContentResolver();
        return contentResolver.query(getUri(), projection, whereSql, whereValue, orderBy);
        
    }

    public Uri insert(ContentValues cv) {
        ContentResolver contentResolver = mContext.getContentResolver();
        return contentResolver.insert(getUri(), cv);
    }
    
    public int update(ContentValues cv, String where, String[] selectionArgs) {
        ContentResolver contentResolver = mContext.getContentResolver();
        return contentResolver.update(getUri(), cv, where, selectionArgs);
    }
    
    public int delete(String whereSQL, String[] whereValue) {
        ContentResolver contentResolver = mContext.getContentResolver();
        return contentResolver.delete(getUri(), whereSQL, whereValue);
    }
    
    /**
     * clear all record
     * 
     * @return
     */
    public int delete() {
        ContentResolver contentResolver = mContext.getContentResolver();
        return contentResolver.delete(getUri(), null, null);
    }
}

從上面的代碼可以看出调卑,增刪查改都是通過(guò)ContentResolver以及getUri()傳遞相關(guān)的參數(shù)來(lái)完成的抡砂。
其中getUri()是有子類實(shí)現(xiàn)用來(lái)提供,子類對(duì)應(yīng)的ContentProvider的訪問(wèn)Uri的恬涧。

//我們定義一個(gè)ContactManager來(lái)獲取服務(wù)端的數(shù)據(jù)

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

import com.doris.db.BaseContentHelper;
import com.orhanobut.logger.Logger;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

public class ContactManager extends BaseContentHelper {
    //定義ContactProvider的authoritys常量用于拼接URi
    public static final String      AUTHORITYS          = "com.doris.support.provider.ContactProvider";
    public static final String      URI_BASE            = "content://" + AUTHORITYS + "/";
    public static final String      TABLE_NAME          = "contact";
    public static final String      PERSON_NAME         = "name";
    public static final String      PERSON_TELE         = "telephone";
    public static final String      PERSON_DESCRIPTION  = "description";
    private static String           TAG                 = ContactManager.class.getSimpleName();
    private static Uri              mUri                = Uri.parse(URI_BASE + "contact"); //最終拼接出來(lái)的URI
    private static ContactManager   mInstance;
    
    private ContactManager(Context ctx) {
        super(ctx);
    }
    
    /**
     * singelten
     * 
     * @return
     */
    public static synchronized ContactManager obtain(Context ctx) {
        
        if (mInstance == null)
            mInstance = new ContactManager(ctx);
        return mInstance;
    }
    
    public List<String> getAllPersonName() {
        List<String> allName = new ArrayList<>();
        Cursor cursor = query(null, null, null, null);
        if (cursor != null) {
            while (cursor.moveToNext()) {
                Persion vo = parserPersonVoFromCursor(cursor);
                allName.add(vo.getName());
            }
            cursor.close();
        }
        return allName;
    }
    public List<Person> getAllPerson() {
        List<Person> allList = new ArrayList<Person>();
        Cursor cursor = query(null, null, null, null);
        if (cursor != null) {
            while (cursor.moveToNext()) {
                Person vo = parserPersonVoFromCursor(cursor);
                allList.add(vo);
            }
            cursor.close();
        }
        return allList;
    }
    
    /**
     * CN:根據(jù)name獲取person列表
    
     * 
     * @param name
     *            eg:doris
     * @return
     */
    public List<Person> getPersonList(String name) {
        List<Person> allList = new ArrayList<Person>();
        Cursor cursor = query(null, PERSON_NAME + "=?", new String[]{name}, null);
        if (cursor != null) {
            while (cursor.moveToNext()) {
                Person vo = parserPersonVoFromCursor(cursor);
                allList.add(vo);
            }
            cursor.close();
        }
        return allList;
    }
    
 
    
    private Person parserPersonVoFromCursor(Cursor cursor) {
        Person vo = new Person();
        vo.setName(cursor.getString(cursor.getColumnIndex(PERSON_NAME)));
        vo.setTelephone(cursor.getString(cursor.getColumnIndex(PERSON_TELE)));
        vo.setDescription(cursor.getString(cursor.getColumnIndex(PERSON_DESCRIPTION)));
        return vo;
    }
    
      
   /**
   重寫父類的getUri 返回uri供ContentResolver使用注益。
   **/
    
    @Override
    protected Uri getUri() {
        return mUri;
    }
}

上面的BaseContentHelper封裝了增刪查改,ContactManager通過(guò)繼承BaseContentHelper封裝了一些數(shù)據(jù)操作的接口溯捆,比如通過(guò)名字獲得Person對(duì)象,獲取所有的Person列表丑搔。

其實(shí)上面就把客戶端如何請(qǐng)求數(shù)據(jù)以及服務(wù)端如何提供ContentProvider給講完了,只要你開(kāi)始用起來(lái)提揍,你就會(huì)發(fā)現(xiàn)其實(shí)很簡(jiǎn)單啤月,只要自定義一個(gè)ContentProvider并且在AndroidMainfest.xml文件里注冊(cè)一下就可以被跨程序訪問(wèn)了。 客戶端也只需結(jié)合ContentResolver以及Uri便可以訪問(wèn)數(shù)據(jù)了劳跃。 是不是炒雞簡(jiǎn)單谎仲。一開(kāi)始我也搞不懂,但是用了幾次之后刨仑,也知道是怎么回事了郑诺。又一次驗(yàn)證了重在實(shí)踐夹姥,所以還沒(méi)用的快用起來(lái)吧。寫這篇文章有兩個(gè)目的: 1.讓還不熟悉ContentProvider的你能了解如何使用辙诞。 2.給自己留下一個(gè)涉獵的足跡辙售,畢竟好記性不如爛筆頭,以后有疑惑可以翻出來(lái)再看看飞涂。

疑問(wèn)點(diǎn)

  • ContentProvider 何時(shí)創(chuàng)建旦部?

    在應(yīng)用一開(kāi)始的時(shí)候,ContentProvider就由系統(tǒng)創(chuàng)建较店。在應(yīng)用中士八,無(wú)需直接為它創(chuàng)建一個(gè)實(shí)例。

  • 其他程序是如何訪問(wèn)到ContentProvider的泽西?

    通過(guò)ContentResolver結(jié)合uri來(lái)訪問(wèn)曹铃,當(dāng)ContentResolver訪問(wèn)某個(gè)uri時(shí)缰趋,系統(tǒng)會(huì)識(shí)別uri解析出authoritys來(lái)匹配對(duì)應(yīng)ContentProvider. 然后傳遞請(qǐng)求與參數(shù)捧杉。

  • 注意,同一個(gè)安卓系統(tǒng)中秘血,ContentProviderauthoritys必須是唯一的味抖,其實(shí)你很容易就可以知道是不是唯一的,如果你運(yùn)行一個(gè)應(yīng)用灰粮,運(yùn)行不上的時(shí)候仔涩,就很有可能是因?yàn)?code>authoritys沖突了,不過(guò)首先你得看看提示信息粘舟,再做判斷熔脂。

  • 其實(shí)ContentProvider不僅可以訪問(wèn)數(shù)據(jù)庫(kù)中的數(shù)據(jù),可以是調(diào)用其他程序中的某個(gè)方法然后獲取返回值柑肴。示例如下:


 //在ContactProvider里面重寫 call方法  
        private static final String FUNC_GET_MESSAGE    = "func_get_messge";
        private static final String MESSAGE = "message";
    @Override
    public Bundle call(String method, String arg, Bundle extras) {
        Bundle ret = null;
        if (method.equals(FUNC_GET_MESSAGE)) {
            String message = getMessage();
            ret = new Bundle();
            ret.putString(MESSAGE, message);
        }
        return ret;
    }
    

客戶端調(diào)用:

//在ContactManager里面添加getMessage方法
    private static final String FUNC_GET_MESSAGE    = "func_get_messge";
        private static final String MESSAGE = "message";
public String getMessage() {
        Bundle ret = mContext.getContentResolver().call(getUri(), FUNC_GET_MESSAGE, null, null);
        ret.setClassLoader(getClass().getClassLoader());
        String list = ret.getString(MESSAGE);
        return list;
    }
    //通過(guò)這種方式可以調(diào)用另一個(gè)應(yīng)用的某個(gè)方法來(lái)獲取某些數(shù)據(jù)霞揉,不過(guò)本質(zhì)上都是獲取一些數(shù)據(jù)。
  • 合理使用ContentProvider晰骑,暴露應(yīng)該暴露的接口适秩,敏感接口不要暴露給用戶。

  • 可以通過(guò)notifyChange()來(lái)通知數(shù)據(jù)的改變硕舆,這個(gè)很有用的秽荞,當(dāng)你通過(guò)ContentProvider來(lái)訪問(wèn)某個(gè)數(shù)據(jù)時(shí),但是又希望在數(shù)據(jù)改變的時(shí)候監(jiān)聽(tīng)到抚官,那么就可以通過(guò)設(shè)置resolver.registerContentObserver來(lái)監(jiān)聽(tīng)數(shù)據(jù)的改變.

//方法聲明如下:第一個(gè)是uri是用來(lái)匹配要監(jiān)聽(tīng)的數(shù)據(jù)的扬跋,第二個(gè)參數(shù)是一個(gè)ContentObserver對(duì)象通過(guò)繼承ContentObserver,重寫onChange方法來(lái)再監(jiān)聽(tīng)到數(shù)據(jù)改變的時(shí)候做相應(yīng)的操作凌节。
/**
     * Register an observer class that gets callbacks when data identified by a
     * given content URI changes.
     *
     * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI
     * for a whole class of content.
     * @param notifyForDescendents When false, the observer will be notified whenever a
     * change occurs to the exact URI specified by <code>uri</code> or to one of the
     * URI's ancestors in the path hierarchy.  When true, the observer will also be notified
     * whenever a change occurs to the URI's descendants in the path hierarchy.
     * @param observer The object that receives callbacks when changes occur.
     * @see #unregisterContentObserver
     */
registerContentObserver(@NonNull Uri uri, boolean notifyForDescendents,
            @NonNull ContentObserver observer) ;

實(shí)例:

 private class SettingsObserver extends ContentObserver {
        public SettingsObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            String option = uri.getLastPathSegment();
            Slog.d(TAG, "onChange, option = " + option);
            switch (option) {
            ...........
                }
        }
    }

在安卓的一些自帶的應(yīng)用中胁住,其實(shí)很多監(jiān)聽(tīng)系統(tǒng)屬性的變化的時(shí)候趁猴,都是通過(guò)ContentProvider以及監(jiān)聽(tīng)來(lái)做相應(yīng)的處理的。所以你可以運(yùn)用到你的應(yīng)用中彪见。讓你的應(yīng)用更加完美儡司。

許久未更新博客,總覺(jué)得自己知道的不夠高深余指,但是想想每一個(gè)記錄都是為別人為自己積累知識(shí)捕犬,不管簡(jiǎn)單還是復(fù)雜。所以再一次碼起了文字酵镜。希望大家多多指教碉碉,有不對(duì)的提出來(lái),一起學(xué)習(xí)成長(zhǎng)淮韭。O(∩_∩)O哈哈~垢粮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市靠粪,隨后出現(xiàn)的幾起案子蜡吧,更是在濱河造成了極大的恐慌,老刑警劉巖占键,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昔善,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡畔乙,警方通過(guò)查閱死者的電腦和手機(jī)君仆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)牲距,“玉大人返咱,你說(shuō)我怎么就攤上這事‰咕希” “怎么了咖摹?”我有些...
    開(kāi)封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)皮服。 經(jīng)常有香客問(wèn)我楞艾,道長(zhǎng),這世上最難降的妖魔是什么龄广? 我笑而不...
    開(kāi)封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任硫眯,我火速辦了婚禮,結(jié)果婚禮上择同,老公的妹妹穿的比我還像新娘两入。我一直安慰自己,他們只是感情好敲才,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布裹纳。 她就那樣靜靜地躺著择葡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剃氧。 梳的紋絲不亂的頭發(fā)上敏储,一...
    開(kāi)封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音朋鞍,去河邊找鬼已添。 笑死,一個(gè)胖子當(dāng)著我的面吹牛滥酥,可吹牛的內(nèi)容都是我干的更舞。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼坎吻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼缆蝉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起瘦真,我...
    開(kāi)封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤刊头,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后吗氏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體芽偏,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雷逆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年弦讽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膀哲。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡往产,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出某宪,到底是詐尸還是另有隱情仿村,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布兴喂,位于F島的核電站蔼囊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏衣迷。R本人自食惡果不足惜畏鼓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望壶谒。 院中可真熱鬧云矫,春花似錦、人聲如沸汗菜。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至巡揍,卻和暖如春痛阻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腮敌。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工录平, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缀皱。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓斗这,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親啤斗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子表箭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

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

  • 這篇主要介紹下ContentProvider如何實(shí)現(xiàn)共享數(shù)據(jù)、及ContentResolver如何訪問(wèn)其他進(jìn)程等數(shù)...
    朋永閱讀 2,496評(píng)論 0 5
  • 魏彤彤下樓來(lái)钮莲,看到林子軒筆挺地站在一棵樹(shù)下免钻。他背對(duì)著她,穿著一件白色體恤崔拥,一條洗白的藍(lán)色牛仔褲极舔。身影薄削,但線條硬...
    愛(ài)看書的田小鳥(niǎo)閱讀 214評(píng)論 0 0
  • 我玩部落沖突已有八個(gè)月之久链瓦,現(xiàn)在九本初期拆魏。雖然不比一些大神有經(jīng)驗(yàn)技巧,但也是摸爬滾打找出了一條自己的路慈俯,跟大家分享...
    魚小婧閱讀 4,892評(píng)論 19 17