softReference+LruCache優(yōu)化Android緩存

近眼看世界

大家好,我叫石頭.

關(guān)于 SoftReference 在緩存中的使用問題,Android 在官方文檔 SoftReference敌呈,明確指出

Avoid Soft References for Caching

Google從Android 2.3+開始宣布說袍辞,他們要從此版本開始鞋仍,讓GC更加頻繁地去回收具有軟引用對象的內(nèi)存,好吧搅吁。威创。。動不動就被GC回收了谎懦,那我們的對象豈不就會經(jīng)常丟失肚豺?對的,這樣的話界拦,SoftReference雖然不會造成OOM吸申,但是我們的數(shù)據(jù)就會丟失,就會變的十分不可靠了


Most applications should use an android.util.LruCache instead of soft references. LruCachehas an effective eviction policy and lets the user tune how much memory is allotted.

為什么Android明確要求開發(fā)者們放棄SoftReference呢,官方給出的原因:

In practice, soft references are inefficient for caching. The runtime doesn't have enough information on which references to clear and which to keep. Most fatally, it doesn't know what to do when given the choice between clearing a soft reference and growing the heap.


在實(shí)踐中,軟引用(soft references)在緩存中是低效的,因?yàn)?code>runtime并沒有足夠的信息來判別應(yīng)該清除或者保留哪個 SoftReference(持有的對象)享甸,更無法判定當(dāng) App 要求更多內(nèi)存的時候截碴,是應(yīng)該清除 SoftReference,還是增大 App 的Heap蛉威。

當(dāng)我們聽到這句話的時候是不是感覺很合理呀,但是按照我們的理解這個根本說不過去啊日丹。

因?yàn)樵谡5?JVM中,只要不會觸發(fā) OOM(達(dá)到系統(tǒng)內(nèi)存上限或者到達(dá) JVM 設(shè)定的內(nèi)存上限)蚯嫌,JVM 就應(yīng)該毫不留情的增大 Heap 來維持應(yīng)用的正常運(yùn)行哲虾。 而沒有必要考慮是先清理 SoftReference,還是增大 Heap 這種無聊的問題择示。

Android RuntimeJVM 不一樣的是:用戶 App 通常沒有權(quán)限來設(shè)定自己的最大可用內(nèi)存妒牙,這個是由系統(tǒng)控制的, 單個 App 使用的最大內(nèi)存容量是固定的:

Runtime.getRuntime().maxMemory()

其他就是跟 JVM 差不多了对妄,Android 在啟動每一個 App 的時候湘今,也并不是一開始就給每個 App 分配固定的上限內(nèi)存,也是按需動態(tài)分配剪菱,所以摩瞎,這應(yīng)該不是技術(shù)問題。
官方也為我們給出了原因:

The lack of information on the value to your application of each reference limits the usefulness of soft references. References that are cleared too early cause unnecessary work; those that are cleared too late waste memory.

讓我們回顧下軟引用

  • 創(chuàng)建軟引用HashMap作為緩存
private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
  • 向緩存中添加新Bitmap
public void addBitmapToCache(String path) {
        // 強(qiáng)引用的Bitmap對象
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        // 軟引用的Bitmap對象
        SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);
        // 添加該對象到Map中使其緩存
        imageCache.put(path, softBitmap);
    }

注意:由于bitmap為局部變量孝常, 當(dāng)方法結(jié)束時旗们,bitmap被銷毀,其指向的內(nèi)存空間依然只有imageCache中的軟引用。

  • 從緩存中讀取Bitmap
public Bitmap getBitmapByPath(String path) {
        // 從緩存中取軟引用的Bitmap對象
        SoftReference<Bitmap> softBitmap = imageCache.get(path);
        // 判斷是否存在軟引用
        if (softBitmap == null) {
            return null;
        }
        // 取出Bitmap對象构灸,如果由于內(nèi)存不足Bitmap被回收上渴,將取得空
        Bitmap bitmap = softBitmap.get();
        if(bitmap==null){
            return null;
        }
       return bitmap;
    }

軟引用釋放資源是被動的, 當(dāng)內(nèi)存不足時, GC會對其主動回收稠氮。
下面開始我們的主菜~~~


LruCache

LruCache 是對限定數(shù)量的緩存對象持有強(qiáng)引用的緩存曹阔,每一次緩存對象被訪問,都會被移動到隊(duì)列的頭部隔披。LruCache類包含在android-support-v4包中,使用方法和其他緩存一樣:加載圖片前判斷緩存中是否已經(jīng)存在赃份, 如果不存在就重新從圖片源加載。

我們應(yīng)該注意到了LruCache中的前3個單詞LRU,是不是有點(diǎn)眼熟呢,所謂LRU奢米,即為 Least recently used抓韩,近期最少使用策略,其實(shí)很熟悉啦鬓长,操作系統(tǒng)還是學(xué)過的谒拴,嘿嘿~~~。

與使用SoftReference不同涉波,LruCache內(nèi)部通過一個LinkedHashMap保存資源的強(qiáng)引用彪薛。其控制內(nèi)存的方式是主動的,需要在內(nèi)部記錄當(dāng)前緩存大小怠蹂, 并與初始化時設(shè)置的max值比較,如果超過少态, 就將排序最靠前(即最近最少使用)的資源從LinkedHashMap中移除城侧。這樣, 就沒有任何引用指向資源的內(nèi)存空間了彼妻。該內(nèi)存空間無人認(rèn)領(lǐng)嫌佑, 會在GC時得到釋放。
關(guān)于LinkedHashMap侨歉, 其是HashMap的子類屋摇, 支持兩種排序方式, 第一種是根據(jù)插入順序排序幽邓, 第二種就是根據(jù)訪問進(jìn)行排序炮温。采用哪種排序方式由其構(gòu)造函數(shù)傳入?yún)?shù)決定。在LruCache中牵舵, 初始化LinkedHashMap的代碼如下:

this.map = new LinkedHashMap<K, V>(0, 0.75f, true);

其中最后一個參數(shù)柒啤, 就是是否根據(jù)訪問進(jìn)行排序。

LruCache的具體實(shí)現(xiàn):

private LruCache<String, Bitmap> mMemoryCache;

  @Override

  protected void onCreate(Bundle savedInstanceState) {

  // 獲取到可用內(nèi)存的最大值畸颅,使用內(nèi)存超出這個值會引起  OutOfMemory異常担巩。

  // LruCache通過構(gòu)造函數(shù)傳入緩存值,以KB為單位没炒。

  int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

  // 使用最大可用內(nèi)存值的1/8作為緩存的大小涛癌。

  int cacheSize = maxMemory / 8;

  mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {

    @Override

    protected int sizeOf(String key, Bitmap bitmap) {

    // 必須重寫此方法來衡量每張圖片的大小,默認(rèn)返回圖片數(shù)量。

      return bitmap.getByteCount() / 1024;

    }

  };

}

 

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {

    if (getBitmapFromMemCache(key) == null) {

          mMemoryCache.put(key, bitmap);

    }

}


public Bitmap getBitmapFromMemCache(String key) {

    return mMemoryCache.get(key);

}

/**2種情況:

*1.當(dāng)有條目被擠出時拳话,evicted 為true, key與oldValue

為被擠出的條目的值

*2.有條目值發(fā)生改變時先匪,evicted 為false ,使用put()替換值

* key 為替換條目的key oldValue為條目之前的值

newValue 為條目的新值

* 使用remove()時假颇, key與oldValue

為被移除的條目

*/

@Override

protected void entryRemoved(boolean evicted, String key,

Bitmap oldValue, Bitmap newValue) {

System.out.println("evicted:" + evicted + "key:" + key

+ "oldValue:" + oldValue + "newValue:" + newValue);

}

public Bitmap removeBitmapFromMemCache(String key) {

return mMemoryCache.remove(key);

}

關(guān)于recycle()調(diào)用
其實(shí)最早在使用LruCache或者軟引用的時候胚鸯, 我產(chǎn)生了這樣的疑問:GC可以釋放沒有強(qiáng)引用指向的內(nèi)存,但Bitmap的圖片資源(像素數(shù)據(jù))笨鸡, 不是保存在native層姜钳, 需要顯示調(diào)用recycle方法進(jìn)行內(nèi)存釋放嗎。而在一些人關(guān)于LruCache的博客中形耗, 看到博主回復(fù)類似問題哥桥,說該操作由LruCache幫助完成了。然而我看遍了LruCache 的源碼激涤, 也沒有看到哪里有釋放底層資源的操作拟糕,這反而更加深了我的疑惑。 后來在網(wǎng)上看到了這樣的說明倦踢, 即在Android 3.0(Level 11)及其以后送滞, Bitmap的像素數(shù)據(jù)與Bitmap的對象一起保存在Java堆中, 如此辱挥, 系統(tǒng)GC時犁嗅, 也可以一起將像素資源回收了。 要注意的是晤碘, 在使用LruCache時褂微, 千萬不要畫蛇添足, 在LruCache的entryRemoved回調(diào)中實(shí)現(xiàn)對釋放資源的手動recycle园爷。 因?yàn)殡m然該Bitmap從LinkedHashMap中被移除了宠蚂, 但我們無法得知外部是否還有對當(dāng)前Bitmap的引用。如果還有ImageView正顯示著該圖片童社, 那必然會導(dǎo)致崩潰求厕。

LruCache源碼

發(fā)現(xiàn)一堆int類型的變量,還有一個最重要的LinkedHashMap<K,V> 這個隊(duì)列扰楼,通俗的講LinkedHashMap<K,V>就是一個雙向鏈表存儲結(jié)構(gòu)甘改。

各個變量的意思為:
size - LruCache中已經(jīng)存儲的大小
maxSize - 我們定義的LruCache緩存最大的空間
putCount- put的次數(shù)(為LruCache添加緩存對象的次數(shù))
createCount - create的次數(shù)
evictionCount - 回收的次數(shù)
hitCount - 命中的次數(shù)
missCount - 丟失的次數(shù)

結(jié)合SoftReference和LruCache的二級緩存結(jié)構(gòu)

整個思路是:使用了系統(tǒng)提供的LruCache類做一級緩存, 大小為運(yùn)行內(nèi)存的1/8,當(dāng)LruCache容量要滿的時候,會自動將系統(tǒng)移除的圖片放到二級緩存中,但為了避免OOM的問題,這里將SoftReference軟引用加入來,當(dāng)系統(tǒng)快要OOM的時候會自動清除里面的圖片內(nèi)存,當(dāng)然內(nèi)存充足時就會繼續(xù)保存這些二級緩存的圖片.強(qiáng)調(diào)一點(diǎn),不要用SoftReference去做一級緩存,現(xiàn)在的java中垃圾回收加強(qiáng)了對SoftReference軟引用的回收機(jī)制,它只適合臨時的保存一些數(shù)據(jù)緩存,并不適合長期的(相對臨時而言,并不是真正的長期).

package com.shi.quan.lurcache;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by liaoshiquan on 2017/2/7.
 */

public class ImageLoadManager {
    public enum IMAGE_LOAD_TYPE
    {
        FILE_PATH,FILE_URL,FILE_RESOURCE_ID
    }

    private String TAG = "ImageLoadManager...";

    private Context context;
    private Set<ImageLoadTask> taskCollection;
    /** 最大內(nèi)存 **/
    final static int maxCacheSize = (int)(Runtime.getRuntime().maxMemory() / 8);
    /** 建立線程安全,支持高并發(fā)的容器 **/
    private static ConcurrentHashMap<String, SoftReference<Bitmap>> currentHashmap
            = new ConcurrentHashMap<String, SoftReference<Bitmap>>();

    public ImageLoadManager(Context context)
    {
        super();
        this.context = context;
        taskCollection = new HashSet<ImageLoadTask>();
    }

    private static LruCache<String, Bitmap> BitmapMemoryCache = new LruCache<String, Bitmap>(maxCacheSize)
    {
        @Override
        protected int sizeOf(String key, Bitmap value)
        {
            if(value != null)
            {
                return value.getByteCount();
                //return value.getRowBytes() * value.getHeight(); //舊版本的方法
            }
            else
            {
                return 0;
            }
        }
        //這個方法當(dāng)LruCache的內(nèi)存容量滿的時候會調(diào)用,將oldValue的元素移除出來騰出空間給新的元素加入
        @Override
        protected void entryRemoved(boolean evicted, String key,Bitmap oldValue, Bitmap newValue)
        {
            if(oldValue != null)
            {
                // 當(dāng)硬引用緩存容量已滿時,會使用LRU算法將最近沒有被使用的圖片轉(zhuǎn)入軟引用緩存
                currentHashmap.put(key, new SoftReference<Bitmap>(oldValue));
            }
        }

    };

    /**
     * 針對提供圖片資源ID來顯示圖片的方法
     * @param loadType 圖片加載類型
     * @param imageResourceID 圖片資源id
     * @param imageView 顯示圖片的ImageView
     */
    public void setImageView(IMAGE_LOAD_TYPE loadType, int imageResourceID, ImageView imageView)
    {
        if(loadType == IMAGE_LOAD_TYPE.FILE_RESOURCE_ID)
        {
//   if(ifResourceIdExist(imageResourceID))
//   {
//    imageView.setImageResource(imageResourceID);
//
//   }else{ //映射無法獲取該圖片,則顯示默認(rèn)圖片
//    imageView.setImageResource(R.drawable.pic_default);
//   }
            try
            {
                imageView.setImageResource(imageResourceID);
                return;
            } catch (Exception e) {
                Log.e(TAG, "Can find the imageID of "+imageResourceID);
                e.printStackTrace();
            }
            //默認(rèn)圖片
            imageView.setImageResource(R.mipmap.ic_launcher);
        }
    }

    /**
     * 針對提供圖片文件鏈接或下載鏈接來顯示圖片的方法
     * @param loadType  圖片加載類型
     * @param imageFilePath 圖片文件的本地文件地址或網(wǎng)絡(luò)URL的下載鏈接
     * @param imageView 顯示圖片的ImageView
     */
    public void setImageView(IMAGE_LOAD_TYPE loadType, String imageFilePath, ImageView imageView)
    {
        if(imageFilePath == null || imageFilePath.trim().equals(""))
        {
            imageView.setImageResource(R.mipmap.ic_launcher);

        }else{
            Bitmap bitmap = getBitmapFromMemoryCache(imageFilePath);
            if(bitmap != null)
            {
                imageView.setImageBitmap(bitmap);
            }
            else
            {
                imageView.setImageResource(R.mipmap.ic_launcher);
                ImageLoadTask task = new ImageLoadTask(loadType, imageView);
                taskCollection.add(task);
                task.execute(imageFilePath);
            }
        }
    }

    /**
     * 從LruCache中獲取一張圖片灭抑,如果不存在就返回null
     * @param key  鍵值可以是圖片文件的filePath,可以是圖片URL地址
     * @return Bitmap對象,或者null
     */
    public Bitmap getBitmapFromMemoryCache(String key)
    {
        try
        {
            if(BitmapMemoryCache.get(key) == null)
            {
                if(currentHashmap.get(key) != null)
                {
                    return currentHashmap.get(key).get();
                }
            }
            return BitmapMemoryCache.get(key);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return BitmapMemoryCache.get(key);
    }

    /**
     * 將圖片放入緩存
     * @param key
     * @param bitmap
     */
    private void addBitmapToCache(String key, Bitmap bitmap)
    {
        BitmapMemoryCache.put(key, bitmap);
    }



    /**
     * 圖片異步加載
     * @author Mr.Et
     *
     */
    private class ImageLoadTask extends AsyncTask<String, Void, Bitmap>
    {
        private String imagePath;
        private ImageView imageView;
        private IMAGE_LOAD_TYPE loadType;

        public ImageLoadTask(IMAGE_LOAD_TYPE loadType , ImageView imageView)
        {
            this.loadType = loadType;
            this.imageView = imageView;
        }

        @Override
        protected Bitmap doInBackground(String...params)
        {
            imagePath = params[0];
            try
            {
                if(loadType == IMAGE_LOAD_TYPE.FILE_PATH)
                {
                    if(new File(imagePath).exists())
                    { //從本地FILE讀取圖片
                        BitmapFactory.Options opts = new BitmapFactory.Options();
                        opts.inSampleSize = 2;
                        Bitmap bitmap = BitmapFactory.decodeFile(imagePath, opts);
                        //將獲取的新圖片放入緩存
                        addBitmapToCache(imagePath, bitmap);
                        return bitmap;
                    }
                    return null;
                }
                else if(loadType == IMAGE_LOAD_TYPE.FILE_URL)
                { //從網(wǎng)絡(luò)下載圖片
                    byte[] datas = getBytesOfBitMap(imagePath);
                    if(datas != null)
                    {
//      BitmapFactory.Options opts = new BitmapFactory.Options();
//      opts.inSampleSize = 2;
//      Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length, opts);
                        Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length);
                        addBitmapToCache(imagePath, bitmap);
                        return bitmap;
                    }
                    return null;
                }

            } catch (Exception e) {
                e.printStackTrace();
//                FileUtils.saveExceptionLog(e);
                //可自定義其他操作
            }
            return null;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap)
        {
            try
            {
                if(imageView != null)
                {
                    if(bitmap != null)
                    {
                        imageView.setImageBitmap(bitmap);
                    }
                    else
                    {
                        Log.e(TAG, "The bitmap result is null...");
                    }
                }
                else
                {
                    Log.e(TAG, "The imageView is null...");
                    //獲取圖片失敗時顯示默認(rèn)圖片
                    imageView.setImageResource(R.mipmap.ic_launcher);
                }

            } catch (Exception e) {
                e.printStackTrace();
//                FileUtils.saveExceptionLog(e);
            }
        }


    }

    /**
     * InputStream轉(zhuǎn)byte[]
     * @param inStream
     * @return
     * @throws Exception
     */
    private byte[] readStream(InputStream inStream) throws Exception{
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[2048];
        int len = 0;
        while( (len=inStream.read(buffer)) != -1){
            outStream.write(buffer, 0, len);
        }
        outStream.close();
        inStream.close();
        return outStream.toByteArray();
    }

    /**
     * 獲取下載圖片并轉(zhuǎn)為byte[]
     * @param imgUrl
     * @return
     */
    private byte[] getBytesOfBitMap(String imgUrl){
        try {
            URL url = new URL(imgUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(10 * 1000);  //10s
            conn.setReadTimeout(20 * 1000);
            conn.setRequestMethod("GET");
            conn.connect();
            InputStream in = conn.getInputStream();
            return readStream(in);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 該資源ID是否有效
     * @param resourceId 資源ID
     * @return
     */
    private boolean ifResourceIdExist(int resourceId)
    {
        try
        {
            Field field = R.drawable.class.getField(String.valueOf(resourceId));
            Integer.parseInt(field.get(null).toString());
            return true;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 取消所有任務(wù)
     */
    public void cancelAllTask()
    {
        if(taskCollection != null){
            for(ImageLoadTask task : taskCollection)
            {
                task.cancel(false);
            }
        }
    }
}

這里為了舉例使用了AsyncTask,但是在實(shí)際項(xiàng)目中,我們不是很推薦使用AsyncTask,因?yàn)樗泻芏酀撛诘膯栴},這里我們推薦"泡在網(wǎng)上的日子"的一篇關(guān)于AsyncTaskAsyncTaskLoader的替代品使用RxJava.Observable取代AsyncTask和AsyncTaskLoader來替代代碼中的AsyncTask網(wǎng)絡(luò)部分.


每日箴言

只有登上山頂十艾,才能看到那邊的風(fēng)光。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末腾节,一起剝皮案震驚了整個濱河市忘嫉,隨后出現(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ī)與錄音,去河邊找鬼弥咪。 笑死过蹂,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的聚至。 我是一名探鬼主播酷勺,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼扳躬!你這毒婦竟也來了脆诉?” 一聲冷哼從身側(cè)響起甚亭,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎击胜,沒想到半個月后亏狰,有當(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
  • 正文 我和宋清朗相戀三年暇唾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辰斋。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡策州,死狀恐怖,靈堂內(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. 我叫王不留饮六,地道東北人其垄。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像卤橄,于是被迫代替她去往敵國和親绿满。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

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