Android 內(nèi)存優(yōu)化不完全手冊

我的技術(shù)博客:移動開發(fā)小水吧

什么是內(nèi)存泄漏

只要是現(xiàn)代智能電子設(shè)備,不管或大或小厢呵,都會有一個叫做內(nèi)存的硬件纳鼎,在手機中這個硬件的參數(shù)尤為重要,是我們評價一個手機好壞的標準之一扩借。

以Android手機為例椒惨,我們開發(fā)的程序如果想要運行起來,就需要開啟一個獨立的進程潮罪,而這個進程如果想要運行起來康谆,就必須占用一部分的內(nèi)存,這就是我們的應(yīng)用和手機內(nèi)存之間的關(guān)系了嫉到。
說到這里沃暗,我們就可以聊聊內(nèi)存泄漏了(以下內(nèi)容來自百度百科)。

內(nèi)存泄漏(Memory Leak)是指程序中己動態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無法釋放何恶,造成系統(tǒng)內(nèi)存的浪費孽锥,導致程序運行速度減慢甚至系統(tǒng)崩潰等嚴重后果。

內(nèi)存泄漏缺陷具有隱蔽性细层、積累性的特征惜辑,比其他內(nèi)存非法訪問錯誤更難檢測。因為內(nèi)存泄漏的產(chǎn)生原因是內(nèi)存塊未被釋放疫赎,屬于遺漏型缺陷而不是過錯型缺陷盛撑。此外,內(nèi)存泄漏通常不會直接產(chǎn)生可觀察的錯誤癥狀捧搞,而是逐漸積累抵卫,降低系統(tǒng)整體性能,極端的情況下可能使系統(tǒng)崩潰实牡。

百度百科的解釋已經(jīng)很明白了陌僵,具體到程序方面來說,基本上就是該回收的對象由于一些原因無法正炒次耄回收碗短,這樣的對象越來越多導致手機的內(nèi)存占用率居高不下,可用的空閑內(nèi)存越來越少题涨,從而頻繁的觸發(fā)垃圾回收機制偎谁,以至于降低了應(yīng)用的流暢度,嚴重的時候會導致內(nèi)存溢出(OOM Out Of Memory)的問題纲堵,導致程序崩潰巡雨。

至于什么是內(nèi)存溢出,請自行搜索學習席函,該知識點不在本篇教程內(nèi)铐望。

Java的四種引用

聊到Java的四種引用,其實也是一個知識點的加固,因為一些考慮到內(nèi)存泄漏的解決方案中正蛙,需要用到這里的知識點督弓,所以先講解給大家:

  • 強引用:強引用是Java中最普通的引用,隨意創(chuàng)建一個對象然后在其他的地方引用一下乒验,就是強引用愚隧,強引用的對象Java寧愿OOM也不會回收他
  • 軟引用: 軟引用是比強引用弱的引用,在Java gc的時候锻全,如果軟引用所引用的對象被回收狂塘,首次gc失敗的話會繼而回收軟引用的對象,軟引用適合做緩存處理 可以和引用隊列(ReferenceQueue)一起使用鳄厌,當對象被回收之后保存他的軟引用會放入引用隊列
  • 弱引用: 弱引用是比軟引用更加弱的引用荞胡,當Java執(zhí)行g(shù)c的時候,如果弱引用所引用的對象被回收部翘,無論他有沒有用都會回收掉弱引用的對象硝训,不過gc是一個比較低優(yōu)先級的線程,不會那么及時的回收掉你的對象新思。 可以和引用隊列一起使用窖梁,當對象被回收之后保存他的弱引用會放入引用隊列
  • 虛引用: 虛引用和沒有引用是一樣的,他必須和引用隊列一起使用夹囚,當Java回收一個對象的時候纵刘,如果發(fā)現(xiàn)他有虛引用,會在回收對象之前將他的虛引用加入到與之關(guān)聯(lián)的引用隊列中荸哟。可以通過這個特性在一個對象被回收之前采取措施

具體用法可以自己查詢一下假哎,寫幾個例子,這里就不展開講了鞍历。

常見內(nèi)存泄漏

  • 非靜態(tài)內(nèi)部類和匿名內(nèi)部類造成的內(nèi)存泄漏

    這里首先我們了解一下內(nèi)部類和靜態(tài)內(nèi)部類的區(qū)別:

    兩種類對比項 靜態(tài)內(nèi)部類 非靜態(tài)內(nèi)部類
    與外部類的引用關(guān)系 如果沒有傳入?yún)?shù)則無引用關(guān)系 自動獲得強引用關(guān)系
    被調(diào)用時需要外部實例 不需要 需要
    能否使用外部類的非靜態(tài)成員 不能
    生命周期 自主的生命周期 依賴外部類舵抹,甚至更長

    首先我們要考慮一下,在Android里什么時候我們會用到內(nèi)部類呢劣砍?

    最常見的就是Handler惧蛹、AysncTask和Thread這三個類了,所以這就會造成一個內(nèi)存泄漏的隱患刑枝,那就是如果我們正在通過Handler請求網(wǎng)絡(luò)數(shù)據(jù)香嗓,這時我們突然關(guān)閉了Activity,但是網(wǎng)絡(luò)請求并沒有結(jié)束,此時作為內(nèi)部類的Handler還持有外部Activity的強引用關(guān)系装畅,從而導致該Activity的內(nèi)存并沒有被正晨坑椋回收,這就發(fā)生了內(nèi)存泄漏掠兄,而且還有可能造成一些不可預測的空指針的問題像云。

    下面將三個類的內(nèi)存泄漏及解決方案都列出來锌雀,以后遇到類似的問題就可以避免了:

    1. AsyncTask

      public class AsyncTaskOOMActivity extends AppCompatActivity {
          @Override
          protected void onCreate(@Nullable Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              new MyAsyncTask().execute();
          }
          class MyAsyncTask extends AsyncTask<Void,Integer,String>{
              @Override
              protected String doInBackground(Void... voids) {
                  try {
                      Thread.sleep(5000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  return "";
              }
          }
      }
      

      這種寫法的AsyncTask在AndroidStudio中會有警告提示,提示的內(nèi)容是:

      This AsyncTask class should be static or leaks might occur (com.jasoncool.study.oom.asynctask.AsyncTaskOOMActivity.MyAsyncTask) less... (Ctrl+F1)
      A static field will leak contexts. Non-static inner classes have an implicit reference to their outer class. If that outer class is for example a Fragment or Activity, then this reference means that the long-running handler/loader/task will hold a reference to the activity which prevents it from getting garbage collected. Similarly, direct field references to activities and fragments from these longer running instances can cause leaks. ViewModel classes should never point to Views or non-application Contexts.

      說白了其實就是告訴你苫费,非靜態(tài)內(nèi)部類這么用就內(nèi)存泄漏了汤锨,讓你改正過來,我們看一下改后的代碼:

      public class AsyncTaskOOMActivity extends AppCompatActivity {
          @Override
          protected void onCreate(@Nullable Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              new MyAsyncTask().execute();
          }
         static class MyAsyncTask extends AsyncTask<Void,Integer,String>{
              @Override
              protected String doInBackground(Void... voids) {
                  try {
                      Thread.sleep(5000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  return "";
              }
          }
      }
      

      就是這么簡單百框,加一個static關(guān)鍵字就藥到病除了。

      1. Handler

      Handler是我們在開發(fā)過程中比較常見的工具類了牍汹,幾乎是每個Android程序員入門必須掌握的知識點铐维,但是這個類也是經(jīng)常出現(xiàn)內(nèi)存泄漏的元兇,如果使用不好慎菲,會帶來很多意想不到的內(nèi)存泄漏嫁蛇,下面請看代碼:

      public class HandlerOOMActivity extends AppCompatActivity {
      
          private TextView mTextView;
          private Handler mHandler = new Handler(){
              @Override
              public void handleMessage(Message msg) {
                  mTextView.setText("內(nèi)存溢出了");
              }
          };
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
              mTextView = (TextView) findViewById(R.id.text);//模擬內(nèi)存泄露
              loadData();
              this.finish();
          }
      
          private void loadData(){
              mHandler.postDelayed(new Runnable() {
                  @Override
                  public void run() {
                      //什么都不干 就延遲180秒
                  }
              }, 3 * 60 * 1000);
      
          }
      
          @Override
          protected void onDestroy() {
              super.onDestroy();
          }
      }
      

      這個例子其實和上一個AsyncTask非常類似,只不過這里多了一個概念就是在內(nèi)部類中我們引用了外部類的TextView,如果簡單的將Handler改成靜態(tài)內(nèi)部類的話露该,我們還記得 靜態(tài)內(nèi)部類有一個特點就是我們無法似乎用外部類的非靜態(tài)資源睬棚,那這個TextView就無法在Handler中進行使用了,那么該如何解決呢解幼?其實我們可以使用傳入外部Activity的Context的方式來解決該問題抑党,為了保險起見,還可以將該Context設(shè)置成弱引用的方式使用撵摆,并且我們再onDestroy的時候底靠,還可以使用 mHandler.removeCallbacksAndMessages(null);方法來中端handler的請求和回調(diào),并將handler置空特铝,具體看下面的代碼:

      public class HandlerOOMActivity extends AppCompatActivity {
      
          private TextView mTextView;
          private SoftHandler mHandler = new SoftHandler(this);
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
              mTextView = (TextView) findViewById(R.id.text);//模擬內(nèi)存泄露
              loadData();
              this.finish();
          }
      
          public static class SoftHandler extends Handler{
              WeakReference<Activity> weakContext;
               public SoftHandler(Activity context) {
                  weakContext = new WeakReference<>(context);
              }
              @Override
              public void handleMessage(Message msg) {
                  HandlerOOMActivity weakActivity= (HandlerOOMActivity)weakContext.get();
                  weakActivity.mTextView.setText("內(nèi)存不溢出了");
              }
          };
      
          private void loadData(){
              mHandler.postDelayed(new Runnable() {
                  @Override
                  public void run() {
                      //什么都不干 就延遲180秒
                  }
              }, 3 * 60 * 1000);
      
          }
          
          @Override
          protected void onDestroy() {
              super.onDestroy();
              mHandler.removeCallbacksAndMessages(null);
              mHandler=null;
          }
      }
      

      可以看到暑中,雖然寫法復雜了一些,但是也非常好理解鲫剿,這樣基本上就可以避免Handler的內(nèi)存溢出了鳄逾。

      3.Thread

      我們在開發(fā)過程中,有時候會新建一個線程執(zhí)行一些耗時操作灵莲,但是寫耗時操作時我們建立的線程一般都是匿名內(nèi)部類的方式創(chuàng)建的雕凹,匿名內(nèi)部類也會持有外部類的強引用,一樣會遇到內(nèi)存泄漏的問題笆呆,請看代碼:

      public class ThreadOOMActivity extends AppCompatActivity {
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      try {
                          Thread.sleep(50000);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }).start();
          }
      }
      

      這個new Thread就是一個匿名內(nèi)部類了请琳,如果在線程休眠的這段時間內(nèi)我關(guān)閉了該Activity的話,一樣會造成內(nèi)存泄漏的問題赠幕,建議的寫法應(yīng)當是:

      public class ThreadOOMActivity extends AppCompatActivity {
          SoftThread softThread;
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
             softThread=new SoftThread();
             softThread.start();
          }
      
          static class SoftThread  extends  Thread{
              @Override
              public void run() {
                  //自己的擴展...
                  try {
                      Thread.sleep(50000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      
  • 單例模式引起的內(nèi)存泄漏

    單例模式是開發(fā)人員最經(jīng)常接觸的一個設(shè)計模式了俄精,其主要目的是為了讓應(yīng)用中只生成一個對象在應(yīng)用中使用,因為創(chuàng)建對象也要耗費一定的資源榕堰,頻繁創(chuàng)建對象肯定不如長期使用一個對象要合理竖慧,所以單例模式的應(yīng)用非常廣泛嫌套,一般的代碼如下:

    public class SingleUtils {
        private static volatile SingleUtils mInstance = null;
        private Context context;
        private SingleUtils (Context context){
            this.context = context;
        }
     
        public static SingleUtils getInstance(Context context){
            if(mInstance == null){
                synchronized (Singleton.class) {
                    if(mInstance == null)
                mInstance = new SingleUtils (context);
                }
                }
            }
            return mInstance;
        }
     
        public Object getObject(){//根據(jù)業(yè)務(wù)邏輯傳入?yún)?shù)
            //返回業(yè)務(wù)邏輯結(jié)果,這里需要用到context
        }
    }
    

    一般只要不出現(xiàn)重大的程序問題圾旨,單例模式的生命周期都是跟應(yīng)用的生命周期一致的踱讨,如果我們把一個Activity的Context傳給了單例模式的邏輯中,那么這個單例模式的靜態(tài)變量就會引用了我們的Activity的Context,在默認的情況下就會造成即便關(guān)閉了該Activity砍的,也無法在內(nèi)從中回收該Activity痹筛,從而導致了內(nèi)存泄漏。

    解決方案:

    將該Activity的Context換成生命周期較長的ApplicationContext廓鞠,ApplicationContext是跟著應(yīng)用的生命周期而建立和銷毀的帚稠,這樣就和單例模式的一致了,也避免了內(nèi)存泄漏問題的發(fā)生床佳。

  • 資源不及時回收引起的內(nèi)存泄露

    圖片加載是手機端占用內(nèi)存的大戶滋早,處理不好圖片很容易造成內(nèi)存泄漏,甚至導致內(nèi)存溢出砌们,不過處理圖片需要講解的東西太多了杆麸,我在這里主要說一下Bitmap的內(nèi)存泄漏,至于如何優(yōu)化圖片的技術(shù)浪感,如:圖片質(zhì)量壓縮昔头、圖片尺寸裁剪、改變圖片顏色模式等知識點請自行搜索學習篮撑,我就不展開討論了减细。

    說到Bitmap內(nèi)存泄漏,其實最大的問題就是沒有及時的回收資源赢笨,只要在圖片使用完后調(diào)用recylce()方法未蝌,并將Bitmap對象賦值為null就可以了。

    很多地方其實都適用這種處理方式茧妒,比如 Sqlite數(shù)據(jù)庫在查詢結(jié)束后要記得關(guān)閉Cursor.close(),又或者我們在處理完文件流后要記得InputStream.close(),以及EventBus在使用完成后也要記得unregister()萧吠,等等,這些知識點要在平時開發(fā)的過程中很注意桐筏,不然就會為未來的項目維護埋下各種深坑纸型,謹記。

  • WebView引起的內(nèi)存泄漏

    WebView主要是用來加載網(wǎng)頁的梅忌,在H5大行其道的今天狰腌,WebView的使用場景非常常見,合理的釋放WebView占用的資源也是處理內(nèi)存泄漏的關(guān)鍵牧氮,下面我們來看一下WebView如何釋放資源琼腔,代碼如下:

    private void destroyWebView() {
            if (mWebView != null) {
                mWebView.pauseTimers();
                mWebView.removeAllViews();
                mWebView.destroy();
                mWebView = null;
            }
        }
    
  • 屬性動畫引起的內(nèi)存泄漏

    屬性動畫中有一種動畫的執(zhí)行時間模式是ValueAnimator.INFINITE,這種方式是無限循環(huán)的踱葛。如果頁面邏輯比較復雜丹莲,屬性動畫可能會和Handler配合使用光坝,在這種情況下,就有可能造成屬性動畫內(nèi)存泄漏了甥材,解決問題的方法也比較簡單盯另,在關(guān)閉該頁面前,在onDestory方法中調(diào)用屬性動畫的cancel()方法洲赵,并將該動畫的對象置為Null即可鸳惯。

  • Service引起的內(nèi)存泄漏

    Service停止失敗,也會導致內(nèi)存泄漏板鬓,因為系統(tǒng)會將這個Service所在的進程進行保留悲敷,如果該進程耗內(nèi)存很大,便會造成內(nèi)存泄漏俭令,容易莫名的引發(fā)GC。

    所以在使用Service方面部宿,可以盡量嘗試使用IntentService,IntentService的本質(zhì)其實就是Service+HandlerThread,它有很多優(yōu)點抄腔,其中一個就是IntentService在執(zhí)行完指定的任務(wù)后會自行關(guān)閉,不用用戶手動關(guān)閉理张,避免了內(nèi)存泄漏的發(fā)生,下面來看一下IntentService的源碼:

    package android.app;
    
    import android.annotation.WorkerThread;
    import android.annotation.Nullable;
    import android.content.Intent;
    import android.os.Handler;
    import android.os.HandlerThread;
    import android.os.IBinder;
    import android.os.Looper;
    import android.os.Message;
    
    public abstract class IntentService extends Service {
        private volatile Looper mServiceLooper;
        private volatile ServiceHandler mServiceHandler;
        private String mName;
        private boolean mRedelivery;
    
        private final class ServiceHandler extends Handler {
            public ServiceHandler(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
                onHandleIntent((Intent)msg.obj);
                stopSelf(msg.arg1);
            }
        }
    
        /**
         * Creates an IntentService.  Invoked by your subclass's constructor.
         */
        public IntentService(String name) {
            super();
            mName = name;
        }
    
        /**
         * Sets intent redelivery preferences.  Usually called from the constructor
         */
        public void setIntentRedelivery(boolean enabled) {
            mRedelivery = enabled;
        }
    
        @Override
        public void onCreate() {
            // TODO: It would be nice to have an option to hold a partial wakelock
            // during processing, and to have a static startService(Context, Intent)
            // method that would launch the service & hand off a wakelock.
    
            super.onCreate();
            HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
            thread.start();
    
            mServiceLooper = thread.getLooper();
            mServiceHandler = new ServiceHandler(mServiceLooper);
        }
    
        @Override
        public void onStart(@Nullable Intent intent, int startId) {
            Message msg = mServiceHandler.obtainMessage();
            msg.arg1 = startId;
            msg.obj = intent;
            mServiceHandler.sendMessage(msg);
        }
    
        /**
         * You should not override this method for your IntentService. Instead,
         */
        @Override
        public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
            onStart(intent, startId);
            return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
        }
    
        @Override
        public void onDestroy() {
            mServiceLooper.quit();
        }
    
        /**
         * Unless you provide binding for your service, you don't need to implement this
         */
        @Override
        @Nullable
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        /**
         */
        @WorkerThread
        protected abstract void onHandleIntent(@Nullable Intent intent);
    }
    

    源碼并不多赫蛇,大家應(yīng)該要通讀一下,其中關(guān)鍵一行代碼是26行:stopSelf(msg.arg1);

    這里stopSelf(msg.arg1),會先看隊列中是否有消息待處理雾叭,如果有則繼續(xù)處理后面的消息悟耘,沒有才會將Service銷毀。

  • Static引起的內(nèi)存泄漏

    在非必要的情況下织狐,不要輕易的使用Static來修飾成員變量暂幼,這會導致其生命周期和app進程的生命周期一樣,從而在靜態(tài)變量大量存在的情況下會導致應(yīng)用內(nèi)存占用量大的問題移迫,且更容易被系統(tǒng)回收旺嬉,解決方案就是盡量用懶加載的方式來定義成員變量。Static引起的內(nèi)存泄漏

  • 用緩存避免內(nèi)存泄漏

    很常見的一個例子就是圖片的三級緩存結(jié)構(gòu)厨埋,分別為網(wǎng)絡(luò)緩存邪媳,本地緩存以及內(nèi)存緩存。在內(nèi)存緩存邏輯類中荡陷,通常會定義這樣的集合類雨效。

    private HashMap<String, Bitmap> mMemoryCache = new HashMap<String, Bitmap>();//String類為該圖片對應(yīng)url
    

    三級緩存結(jié)構(gòu)過程介紹:

    在用戶切換到展示圖片的界面時,當然是優(yōu)先判斷內(nèi)存緩存是否為Null废赞,不為空直接展示圖片徽龟,若為空,同樣的邏輯去判斷本地緩存(不為空便設(shè)置內(nèi)存緩存并展示圖片)蛹头,本地緩存再為空才會根據(jù)該圖片的url用網(wǎng)絡(luò)下載類去下載該圖片并展示圖片(當然了顿肺,下載到圖片后會有設(shè)置本地緩存以及內(nèi)存緩存的操作)戏溺。

    內(nèi)存泄漏的問題就出現(xiàn)在內(nèi)存緩存中:只要HashMap對象實例被引用,而Bitmap對象又都是強引用屠尊,Bitmap中圖片越來越多旷祸,即便是內(nèi)存溢出了,垃圾回收器也不會處理讼昆。

解決方案:

(1)我們可以選擇使用軟引用托享,從而在內(nèi)存不足時,垃圾回收器更容易回收Bitmap垃圾浸赫。

private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new HashMap<String, SoftReference<Bitmap>>();

(2)Android2.3以后闰围,SoftReference不再可靠。垃圾回收期更容易回收它既峡,不再是內(nèi)存不足時才回收軟引用羡榴。那么緩存機制便失去了意義。

Google官方建議使用LruCache作為緩存的集合類运敢。其實內(nèi)部封裝了LinkedHashMap校仑。內(nèi)部原理是一直判斷集合大小是否超出給定的最大值,超出就把最早最少使用的對象踢出集合传惠。

private LruCache<String, Bitmap> mMemoryCache = new LruCache<String, Bitmap>
((int)(Runtime.getRuntime().maxMemory()/8)){ 
//用最大內(nèi)存的1/8分配給這個集合使用
//讓這個集合知道每個圖片的大小
@Override
protected int sizeOf(String key, Bitmap value){
int byteCount = value.getRowBytes() * value.getHeight();//計算圖片大小迄沫,每行字節(jié)數(shù)*高度
return byteCount;
  }
}; 
  • 集合類導致的內(nèi)存泄漏

    集合類添加元素后,仍引用著集合元素對象卦方,導致該集合中的元素對象無法被回收羊瘩,從而導致內(nèi)存泄露,舉個例子:

     static List<Object> objectList = new ArrayList<>();
       for (int i = 0; i < 10; i++) {
           Object obj = new Object();
           objectList.add(obj);
           obj = null;
        }
    

    在這個例子中盼砍,循環(huán)多次將 new 出來的對象放入一個靜態(tài)的集合中尘吗,因為靜態(tài)變量的生命周期和應(yīng)用程序一致,而且他們所引用的對象 Object 也不能釋放衬廷,這樣便造成了內(nèi)存泄露摇予。

    解決方法:在集合元素使用之后從集合中刪除,等所有元素都使用完之后吗跋,將集合置空侧戴。

     objectList.clear();
     objectList = null;
    

內(nèi)存泄漏總結(jié)

說了這么多,其實這些知識點很多朋友在其他技術(shù)文章中也都看到過了跌宛,我這里也只是做了歸納和總結(jié)酗宋,其中比較關(guān)鍵的就是在開發(fā)的過程中一定要心細,不要怕麻煩疆拘,對一些可能存在的內(nèi)存泄漏一定要及時的處理蜕猫,養(yǎng)成良好的編程習慣和責任心。

平時在開發(fā)的過程中哎迄,除了知道這些內(nèi)存泄漏的知識點外還應(yīng)當學習和利用一些工具來幫助自己分析應(yīng)用的內(nèi)存泄漏問題回右,這里我先推薦兩個:一個是Android端的LeakCanary,還一個是AndroidStudio3.0出來的Profiler分析器隆圆,至于怎么用,大家可以自行查詢相關(guān)文檔學習翔烁,我這里就只是拋個磚渺氧。

謝謝你看完!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蹬屹,一起剝皮案震驚了整個濱河市侣背,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌慨默,老刑警劉巖贩耐,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異厦取,居然都是意外死亡潮太,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門虾攻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來消别,“玉大人,你說我怎么就攤上這事台谢。” “怎么了岁经?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵朋沮,是天一觀的道長。 經(jīng)常有香客問我缀壤,道長樊拓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任塘慕,我火速辦了婚禮筋夏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘图呢。我一直安慰自己条篷,他們只是感情好,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布蛤织。 她就那樣靜靜地躺著赴叹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪指蚜。 梳的紋絲不亂的頭發(fā)上乞巧,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音摊鸡,去河邊找鬼绽媒。 笑死蚕冬,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的是辕。 我是一名探鬼主播囤热,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼免糕!你這毒婦竟也來了赢乓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤石窑,失蹤者是張志新(化名)和其女友劉穎牌芋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體松逊,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡躺屁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了经宏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片犀暑。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖烁兰,靈堂內(nèi)的尸體忽然破棺而出耐亏,到底是詐尸還是另有隱情,我是刑警寧澤沪斟,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布广辰,位于F島的核電站,受9級特大地震影響主之,放射性物質(zhì)發(fā)生泄漏择吊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一槽奕、第九天 我趴在偏房一處隱蔽的房頂上張望几睛。 院中可真熱鬧,春花似錦粤攒、人聲如沸所森。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽必峰。三九已至,卻和暖如春钻蹬,著一層夾襖步出監(jiān)牢的瞬間吼蚁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肝匆,地道東北人粒蜈。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像旗国,于是被迫代替她去往敵國和親枯怖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

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