Android 緩存策略LruCache和DiskLruCache學(xué)習(xí)

學(xué)習(xí)資料:

  • Android 開發(fā)藝術(shù)探索

其實(shí)就是完完全全摘抄,讀書筆記 : )

LruCacheDiskLruCache是采用了LRU(Least Recently Used)近期最少使用算法的兩種緩存纬黎。LruCache內(nèi)存緩存幅骄,DiskLruCache存儲(chǔ)設(shè)備緩存


1.LruCache 內(nèi)存緩存

LruCache是一個(gè)泛型類,內(nèi)部是一個(gè)LinkedHashMap以強(qiáng)引用的方式存儲(chǔ)緩存對(duì)象本今,提供了getput方法進(jìn)行對(duì)緩存對(duì)象的操作拆座。當(dāng)緩存滿時(shí),移除近期最少使用的緩存對(duì)象诈泼,添加新的緩存對(duì)象

  • 強(qiáng)引用:直接的對(duì)象引用
  • 軟引用:當(dāng)一個(gè)對(duì)象只有軟引用存在時(shí)懂拾,系統(tǒng)內(nèi)存不足時(shí),會(huì)被gc回收
  • 弱引用:當(dāng)一個(gè)對(duì)象只有弱引用存在時(shí)铐达,該對(duì)象會(huì)隨時(shí)被gc回收

LruCache是線程安全的


LruCache典型的初始化岖赋,重寫sizeOf()方法

int MaxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);// kB
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String,Bitmap>(cacheSize){
    @Override
    protected int sizeOf(String key,Bitmap bitmap){
        //bitmap.getByteCount() = bitmap.getRowBytes() * bitmap.getHeight(); 
        return bitmap.getRowBytes() * bitmap.getHeight() / 1024;// KB
    } 
}

計(jì)算緩存對(duì)象大小的單位和總?cè)萘康膯挝灰3忠恢?/p>

一些特殊時(shí)候,還需要重寫entryRemoved()方法瓮孙。LruCache移除舊緩存對(duì)象時(shí)會(huì)調(diào)用這個(gè)方法唐断,根據(jù)需求可以在這個(gè)方法中完成一些資源回收工作

獲取一個(gè)緩存對(duì)象,mMemoryCache.get(key)
添加一個(gè)緩存對(duì)象杭抠,mMemoryCache.put(key,bitmap)


2.DiskLruCache 磁盤緩存

DiskLruCache并不是Android SDK中的類脸甘。不明白為啥,官方只進(jìn)行推薦偏灿,為何不加入SDK

2.1創(chuàng)建

private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;// 50MB

File diskCacheDir = new File(mContext,"bitmap");
if(!diskCacheDir.exists()){
   diskCacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE);
  • diskCacheDir 緩存文件夾丹诀,具體指sdcard/Android/data/package_name/cache
  • 1 應(yīng)用版本號(hào),一般寫為1
  • 1 單個(gè)節(jié)點(diǎn)所對(duì)應(yīng)的數(shù)據(jù)的個(gè)數(shù)翁垂,一般寫1
  • DISK_CACHE_SIZE 緩存大小

2.2添加

DishLruCache緩存添加的操作通過(guò)Eidtor完成铆遭,Editor為一個(gè)緩存對(duì)象的編輯對(duì)象。

首先需要獲取圖片的url所對(duì)應(yīng)的key沿猜,根據(jù)key利用edit()來(lái)獲取Editor對(duì)象枚荣。若此時(shí),這個(gè)緩存正在被編輯啼肩,edit()會(huì)返回null橄妆。DiskLruCache不允許同時(shí)編輯同一個(gè)緩存對(duì)象。之所以把url轉(zhuǎn)換成key祈坠,因?yàn)閳D片的url中可能存在特殊字符害碾,會(huì)影響使用,一般將urlmd5值作為key

private String hashKeyFromUrl(String url){
    String cacheKey;
    try {
        final MessageDigest  mDigest = MessageDigest.getInstance("MD5");
        mDigest.update(url.getBytes());
        cacheKey = byteToHexString(mDigest.digest());
    } catch (NoSuchAlgorithmException e) {
        cacheKey = String.valueOf(url.hashCode());
    }
     return  cacheKey;
}

private String byteToHexString(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < bytes.length; i ++){
          String hex = Integer.toHexString(0xFF & bytes[i]);//得到十六進(jìn)制字符串
          if (hex.length() == 1){
             sb.append('0');
          }
          sb.append(hex);
    }
    return  sb.toString();
}

url轉(zhuǎn)成key赦拘,利用這key值獲取Editor對(duì)象慌随。若這個(gè)keyEditor對(duì)象不存在,edit()方法就創(chuàng)建一個(gè)新的出來(lái)另绩。通過(guò)Editor對(duì)象可以獲取一個(gè)輸出流對(duì)象儒陨。DiskLruCacheopen()方法中,一個(gè)節(jié)點(diǎn)只能有一個(gè)數(shù)據(jù)笋籽,edit.newOutputStream(DISK_CACHE_INDEX)參數(shù)設(shè)置為0

String key = hashKeyFromUrl(url);
DiskLruCache.Editor editor =mDiskLruCache.edit(key);
if (editor != null){
    OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
}

有了這個(gè)文件輸出流蹦漠,從網(wǎng)絡(luò)加載一個(gè)圖片后,通過(guò)這個(gè)OutputStream outputStream寫入文件系統(tǒng)

private boolean downLoadUrlToStream(String urlString, OutputStream outputStream) {
        HttpURLConnection urlConnection = null;
        BufferedOutputStream bos = null;
        BufferedInputStream bis = null;
        try {
            final URL url   = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            bis = new BufferedInputStream(urlConnection.getInputStream(),8 * 1024);
            bos = new BufferedOutputStream(outputStream,8 * 1024);
            int b ;
            while((b = bis.read())!= -1){
                bos.write(b);
            }
            return  true;
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (urlConnection != null){
                urlConnection.disconnect();
            }
            closeIn(bis) ;
            closeOut(bos);
        }
        return   false;
}

上面的代碼并沒(méi)有將圖片寫入文件系統(tǒng)车海,還需要通過(guò)Editor.commit()提交寫入操作笛园,若寫入失敗,調(diào)用abort()方法侍芝,進(jìn)行回退整個(gè)操作

if (downLoadUrlToStream(url,outputStream)){
    editor.commit();//提交
}else {
    editor.abort();//重復(fù)操作
}

這時(shí)研铆,圖片已經(jīng)正確寫入文件系統(tǒng),接下來(lái)的圖片獲取就不需要請(qǐng)求網(wǎng)絡(luò)


2.3 緩存查找

查找過(guò)程州叠,也需要將url轉(zhuǎn)換為key棵红,然后通過(guò)DiskLruCacheget方法得到一個(gè)Snapshot對(duì)象,再通過(guò)Snapshot對(duì)象可得到緩存的文件輸入流咧栗,有了輸入流就可以得到Bitmap對(duì)象

為了避免oom逆甜,會(huì)使用ImageResizer進(jìn)行縮放。若直接對(duì)FileInputStream進(jìn)行操作致板,縮放會(huì)出現(xiàn)問(wèn)題交煞。FileInputStream是有序的文件流,兩次decodeStream調(diào)用會(huì)影響文件流的位置屬性斟或∷卣鳎可以通過(guò)文件流得到其所對(duì)應(yīng)的文件描述符,利用BitmapFactory.decodeFileDescriptor()方法進(jìn)行縮放

Bitmap bitmap = null;
String key = hashKeyFromUrl(url);
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot != null){
    FileInputStream fis = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
    FileDescriptor fileDescriptor = fis.getFD();
    bitmap = imageResizer.decodeBitmapFromFileDescriptor(fileDescriptor,targetWidth,targetHeight);
    if (bitmap != null){
        addBitmapToMemoryCache(key,bitmap);
    }
}

在查找得到Bitmap后萝挤,把key,bitmap添加到內(nèi)存緩存中


3.ImageLoader的實(shí)現(xiàn)

主要思路:

  1. 拿到圖片請(qǐng)求地址url后御毅,先把url變作對(duì)應(yīng)的key
  2. 利用key在內(nèi)存緩存中查找平斩,查找到了就進(jìn)行加載顯示圖片亚享;若沒(méi)有查到就進(jìn)行3
  3. 在磁盤緩存中查找,在若查到了绘面,就加載到內(nèi)存緩存欺税,后加載顯示圖片;若沒(méi)有查找到揭璃,進(jìn)行4
  4. 進(jìn)行網(wǎng)絡(luò)求晚凿,拿到數(shù)據(jù)圖片,把圖片寫進(jìn)磁盤緩存成功后瘦馍,再加入到內(nèi)存緩存中歼秽,并根據(jù)實(shí)際所需顯示大小進(jìn)行合理縮放顯示

類比較長(zhǎng),查看順序:構(gòu)造方法->bindBitmap(),之后順著方法內(nèi)的方法情组,結(jié)合4個(gè)步驟燥筷,并不難理解

public class ImageLoader {
    private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;// 50MB
    private Context mContext;
    private LruCache<String, Bitmap> mMemoryCache;
    private DiskLruCache mDiskLruCache;
    private boolean mIsDiskLruCacheCreated = false;//用來(lái)標(biāo)記mDiskLruCache是否創(chuàng)建成功
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT+ 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final long KEEP_ALIVE = 10;
    private final int DISK_CACHE_INDEX = 0;

    private static final int MESSAGE_POST_RESULT = 101;

    private ImageResizer imageResizer = new ImageResizer();

    private static final ThreadFactory mThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r,"ImageLoader#"+mCount.getAndIncrement());
        }
    };
    /**
     * 創(chuàng)建線程池
     */
    public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
            CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE, TimeUnit.SECONDS,
            new LinkedBlockingDeque<Runnable>(),mThreadFactory
    );

    /**
     * 創(chuàng)建Handler
     */
    private Handler mHandler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == MESSAGE_POST_RESULT){
                LoaderResult loadResult = (LoaderResult) msg.obj;
                ImageView iv = loadResult.iv;
                String url = (String) iv.getTag();
                if (url.equals(loadResult.uri)){//防止加載列表形式時(shí)箩祥,滑動(dòng)復(fù)用的錯(cuò)位
                    iv.setImageBitmap(loadResult.bitmap);
                }
            }
        }
    };

    private ImageLoader(Context mContext) {
        this.mContext = mContext.getApplicationContext();
        init();
    }
   /**
     * 創(chuàng)建一個(gè)ImageLoader
     */
    public static ImageLoader build(Context context) {
        return new ImageLoader(context);
    }

    
   /**
     * 初始化
     * LruCache<String,Bitmap> mMemoryCache
     * DiskLruCache mDiskLruCache
     */
    private void init() {
        // LruCache<String,Bitmap> mMemoryCache
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                 //return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
                return bitmap.getByteCount() / 1024;
            }
        };
        // DiskLruCache mDiskLruCache
        File diskCacheDir = getDiskCacheDir(mContext, "bitmap");
        if (!diskCacheDir.exists()) {
            diskCacheDir.mkdirs();
        }
        if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) {
            try {
                mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
                mIsDiskLruCacheCreated = true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

   
    /**
     *  加載原始大小的圖
     */
    public  void bindBitmap(String uri,ImageView iv){
        bindBitmap(uri,iv,0,0);
    }

    /**
     * 異步加載網(wǎng)絡(luò)圖片 指定大小
     */
    public void bindBitmap(final String uri, final ImageView iv, final int targetWidth, final int targetHeight){
         iv.setTag(uri);
         Bitmap bitmap = loadBitmapFormMemCache(uri);
        if (bitmap != null){
            iv.setImageBitmap(bitmap);
            return;
        }
        Runnable loadBitmapTask = new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = loadBitmap(uri,targetWidth,targetHeight);
                if (bitmap != null){
                    LoaderResult result = new LoaderResult(iv,uri,bitmap);
                    Message message = mHandler.obtainMessage();
                    message.obj = result;
                    message.what = MESSAGE_POST_RESULT;
                    mHandler.sendMessage(message);
                }
            }
        };
        THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
    }

    /**
     * 同步加載網(wǎng)絡(luò)圖片
     */
    private Bitmap loadBitmap(String url, int targetWidth, int targetHeight) {
        Bitmap bitmap = loadBitmapFormMemCache(url);
        if (bitmap != null) {
            return bitmap;
        }
        try {
            bitmap = loadBitmapFromDiskCache(url, targetWidth, targetHeight);
            if (bitmap != null) {
                return bitmap;
            }
            bitmap = loadBitmapFromHttp(url, targetWidth, targetHeight);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (bitmap == null && !mIsDiskLruCacheCreated) {//緩存文件夾創(chuàng)建失敗
            bitmap = downLoadFromUrl(url);
        }
        return bitmap;
    }
    
    /**
     * 向緩存中添加Bitmap
     */
    private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemoryCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }

    /**
     * 通過(guò)key拿到bitmap
     */
    private Bitmap getBitmapFromMemoryCache(String key) {
        return mMemoryCache.get(key);
    }

    private Bitmap loadBitmapFormMemCache(String url) {
        final String key = hashKeyFromUrl(url);
        return getBitmapFromMemoryCache(key);
    }

    /**
     * 從網(wǎng)絡(luò)進(jìn)行請(qǐng)求
     */
    private Bitmap loadBitmapFromHttp(String url, int targetWidth, int targetHeight) throws IOException {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            throw new RuntimeException("UI 線程不能進(jìn)行網(wǎng)絡(luò)訪問(wèn)");
        }
        if (mDiskLruCache == null) {
            return null;
        }
        String key = hashKeyFromUrl(url);
        DiskLruCache.Editor editor = mDiskLruCache.edit(key);
        if (editor != null) {
            OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
            if (downLoadUrlToStream(url, outputStream)) {
                editor.commit();
            } else {
                editor.abort();//重復(fù)操作
            }
            mDiskLruCache.flush();//刷新
        }
        return loadBitmapFromDiskCache(url, targetWidth, targetHeight);
    }

    /**
     * 從硬盤緩存中讀取Bitmap
     */
    private Bitmap loadBitmapFromDiskCache(String url, int targetWidth, int targetHeight) throws IOException {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            throw new RuntimeException("硬盤讀取Bitmap在UI線程,UI 線程不進(jìn)行耗時(shí)操作");
        }
        if (mDiskLruCache == null) {
            return null;
        }
        Bitmap bitmap = null;
        String key = hashKeyFromUrl(url);
        DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
        if (snapshot != null) {
            FileInputStream fis = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
            FileDescriptor fileDescriptor = fis.getFD();
            bitmap = imageResizer.decodeBitmapFromFileDescriptor(fileDescriptor, targetWidth, targetHeight);
            if (bitmap != null) {
                addBitmapToMemoryCache(key, bitmap);
            }
        }
        return bitmap;
    }

    /**
     * 將數(shù)據(jù)請(qǐng)求到流之中
     */
    private boolean downLoadUrlToStream(String urlString, OutputStream outputStream) {
        HttpURLConnection urlConnection = null;
        BufferedOutputStream bos = null;
        BufferedInputStream bis = null;
        try {
            final URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            bis = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
            bos = new BufferedOutputStream(outputStream, 8 * 1024);
            int b;
            while ((b = bis.read()) != -1) {
                bos.write(b);
            }
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            closeIn(bis);
            closeOut(bos);
        }
        return false;
    }

    /**
     * 直接通過(guò)網(wǎng)絡(luò)請(qǐng)求圖片 也不做任何的縮放處理
     */
    private Bitmap downLoadFromUrl(String urlString) {
        Bitmap bitmap = null;
        HttpURLConnection urlConnection = null;
        BufferedInputStream bis = null;
        try {
            final URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            bis = new BufferedInputStream(urlConnection.getInputStream());
            bitmap = BitmapFactory.decodeStream(bis);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            closeIn(bis);
        }
        return bitmap;
    }


    /**
     * 得到MD5值key
     */
    private String hashKeyFromUrl(String url) {
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(url.getBytes());
            cacheKey = byteToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(url.hashCode());
        }
        return cacheKey;
    }

    /**
     * 將byte轉(zhuǎn)換成16進(jìn)制字符串
     */
    private String byteToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);//得到十六進(jìn)制字符串
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }


    /**
     * 得到緩存文件夾
     */
    private File getDiskCacheDir(Context mContext, String uniqueName) {
        //判斷儲(chǔ)存卡是否可以用
        boolean externalStorageAvailable = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
        final String cachePath;
        if (externalStorageAvailable) {
            cachePath = mContext.getExternalCacheDir().getPath();//儲(chǔ)存卡
        } else {
            cachePath = mContext.getCacheDir().getPath();//手機(jī)自身內(nèi)存
        }
        return new File(cachePath + File.separator + uniqueName);
    }

    /**
     * 得到可用空間大小
     */
    private long getUsableSpace(File file) {
        return file.getUsableSpace();
    }

    /**
     * 關(guān)閉輸入流
     */
    private void closeIn(BufferedInputStream in) {
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                in = null;
            }
        }
    }

    /**
     * 關(guān)閉輸輸出流
     */
    private void closeOut(BufferedOutputStream out) {
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                out = null;
            }
        }
    }

    private static class LoaderResult {
        public ImageView iv ;
        public String uri;
        public Bitmap bitmap;

        public LoaderResult(ImageView iv, String uri, Bitmap bitmap) {
            this.iv = iv;
            this.uri = uri;
            this.bitmap = bitmap;
        }
    }
}

經(jīng)過(guò)測(cè)試肆氓,是可以正常加載出圖片的,緩存文件也正常袍祖,主要是學(xué)習(xí)過(guò)程思路


3.1補(bǔ)充 Closeable接口

ImageLoader中,代碼最后寫了closeIn(),closeOut()方法谢揪,完全沒(méi)必要這樣寫蕉陋。在jdk1.6之后,所有的流都實(shí)現(xiàn)了Closeable接口拨扶,可以用這個(gè)接口來(lái)關(guān)閉所有的流

/**
 * 關(guān)閉流
 */
 private void closeStream(Closeable closeable){
    if (closeable != null){
        try {
            closeable.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

downLoadFromUrl()downLoadUrlToStream()方法中需要關(guān)閉流凳鬓,就可以直接closeStream(bis),closeStream(bos)來(lái)進(jìn)行關(guān)閉流操作


4.最后

這個(gè)ImgageLoader還非常簡(jiǎn)陋,和GlidePicasso根本無(wú)法相比患民。并不會(huì)在實(shí)際工作開發(fā)中使用缩举,還是使用Glide或者Picasso。主要是學(xué)習(xí)基礎(chǔ)實(shí)現(xiàn)原理酒奶,學(xué)習(xí)了下Android中緩存的部分知識(shí)蚁孔。之前面試時(shí),被問(wèn)到過(guò)ImageLoader原理惋嚎。

最近的學(xué)習(xí)杠氢,感覺(jué)不會(huì)的東西太多了,得理一理學(xué)習(xí)的順序另伍。

注意多鍛煉身體鼻百。 共勉:)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市摆尝,隨后出現(xiàn)的幾起案子温艇,更是在濱河造成了極大的恐慌,老刑警劉巖堕汞,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勺爱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡讯检,警方通過(guò)查閱死者的電腦和手機(jī)琐鲁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)人灼,“玉大人围段,你說(shuō)我怎么就攤上這事⊥斗牛” “怎么了奈泪?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我涝桅,道長(zhǎng)拜姿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任冯遂,我火速辦了婚禮砾隅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘债蜜。我一直安慰自己,他們只是感情好究反,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布寻定。 她就那樣靜靜地躺著,像睡著了一般精耐。 火紅的嫁衣襯著肌膚如雪狼速。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天卦停,我揣著相機(jī)與錄音向胡,去河邊找鬼。 笑死惊完,一個(gè)胖子當(dāng)著我的面吹牛僵芹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播小槐,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼拇派,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了凿跳?” 一聲冷哼從身側(cè)響起件豌,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎控嗜,沒(méi)想到半個(gè)月后茧彤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疆栏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年曾掂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片承边。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡遭殉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出博助,到底是詐尸還是另有隱情险污,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站蛔糯,受9級(jí)特大地震影響拯腮,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蚁飒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一动壤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧淮逻,春花似錦琼懊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至筛严,卻和暖如春醉旦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背桨啃。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工车胡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人照瘾。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓匈棘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親析命。 傳聞我的和親對(duì)象是個(gè)殘疾皇子羹饰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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