国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android 有效的解決內(nèi)存泄漏的問題實(shí)例詳解

2019-12-12 04:47:56
字體:
供稿:網(wǎng)友

Android 有效的解決內(nèi)存泄漏的問題

Android內(nèi)存泄漏,我想做Android 應(yīng)用的時(shí)候遇到的話很是頭疼,這里是我在網(wǎng)上找的不錯(cuò)的資料,實(shí)例詳解這個(gè)問題的解決方案

前言:最近在研究Handler的知識,其中涉及到一個(gè)問題,如何避免Handler帶來的內(nèi)存溢出問題。在網(wǎng)上找了很多資料,有很多都是互相抄的,沒有實(shí)際的作用。

本文的內(nèi)存泄漏檢測工具是:LeakCanary  github地址:https://github.com/square/leakcanary

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

內(nèi)存泄漏是當(dāng)程序不再使用到的內(nèi)存時(shí),釋放內(nèi)存失敗而產(chǎn)生了無用的內(nèi)存消耗。內(nèi)存泄漏并不是指物理上的內(nèi)存消失,這里的內(nèi)存泄漏是值由程序分配的內(nèi)存但是由于程序邏輯錯(cuò)誤而導(dǎo)致程序失去了對該內(nèi)存的控制,使得內(nèi)存浪費(fèi)。

 怎樣會(huì)導(dǎo)致內(nèi)存泄漏?

資源對象沒關(guān)閉造成的內(nèi)存泄漏,如查詢數(shù)據(jù)庫后沒有關(guān)閉游標(biāo)cursor

構(gòu)造Adapter時(shí),沒有使用 convertView 重用

Bitmap對象不在使用時(shí)調(diào)用recycle()釋放內(nèi)存

對象被生命周期長的對象引用,如activity被靜態(tài)集合引用導(dǎo)致activity不能釋放

 內(nèi)存泄漏有什么危害?

內(nèi)存泄漏對于app沒有直接的危害,即使app有發(fā)生內(nèi)存泄漏的情況,也不一定會(huì)引起app崩潰,但是會(huì)增加app內(nèi)存的占用。內(nèi)存得不到釋放,慢慢的會(huì)造成app內(nèi)存溢出。所以我們解決內(nèi)存泄漏的目的就是防止app發(fā)生內(nèi)存溢出。

1、新建線程引起的Activity內(nèi)存泄漏

例子:

package rxnet.zyj.com.myapplication; import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View; public class Activity6 extends AppCompatActivity {   @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_6);     findViewById( R.id.finish).setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        finish();      }    });     new Thread(new Runnable() {      @Override      public void run() {        try {<br>          //模擬耗時(shí)操作          Thread.sleep( 15000 );        } catch (InterruptedException e) {          e.printStackTrace();        }      }    }).start();   }}

  運(yùn)行上面的代碼后,點(diǎn)擊finish按鈕,過一會(huì)兒發(fā)生了內(nèi)存泄漏的問題。

 為什么Activity6會(huì)發(fā)生內(nèi)存泄漏?

進(jìn)入Activity6 界面,然后點(diǎn)擊finish按鈕,Activity6銷毀,但是Activity6里面的線程還在運(yùn)行,匿名內(nèi)部類Runnable對象引用了Activity6的實(shí)例,導(dǎo)致Activity6所占用的內(nèi)存不能被GC及時(shí)回收。

 如何改進(jìn)?

Runnable改為靜態(tài)非匿名內(nèi)部類即可。

package rxnet.zyj.com.myapplication; import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View; public class Activity6 extends AppCompatActivity {   @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_6);     findViewById( R.id.finish).setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        finish();      }    });     new Thread( new MyRunnable()).start();   }   private static class MyRunnable implements Runnable {     @Override    public void run() {      try {        Thread.sleep( 15000 );      } catch (InterruptedException e) {        e.printStackTrace();      }    }  }   }

 2、Activity添加監(jiān)聽器造成Activity內(nèi)存泄漏

package rxnet.zyj.com.myapplication; import android.app.Activity;import android.os.Bundle; public class LeakActivity extends Activity {  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    NastyManager.getInstance().addListener(this);  }}

  這個(gè)是在開發(fā)中經(jīng)常會(huì)犯的錯(cuò)誤,NastyManager.getInstance() 是一個(gè)單例,當(dāng)我們通過 addListener(this) 將 Activity 作為 Listener 和 NastyManager 綁定起來的時(shí)候,不好的事情就發(fā)生了。

如何改進(jìn)?

想要修復(fù)這樣的 Bug,其實(shí)相當(dāng)簡單,就是在你的 Acitivity 被銷毀的時(shí)候,將他和 NastyManager 取消掉綁定就好了。

package rxnet.zyj.com.myapplication; import android.app.Activity;import android.os.Bundle; public class LeakActivity extends Activity {  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    NastyManager.getInstance().addListener(this);  }   @Override  protected void onDestroy() {    super.onDestroy();    NastyManager.getInstance().removeListener(this);  }}

  3、Handler 匿名內(nèi)部類造成內(nèi)存溢出?

先看著一段代碼

package rxnet.zyj.com.myapplication; import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View; public class HandlerActivity extends AppCompatActivity {   private final static int MESSAGECODE = 1 ;   private final Handler handler = new Handler(){    @Override    public void handleMessage(Message msg) {      super.handleMessage(msg);      Log.d("mmmmmmmm" , "handler " + msg.what ) ;    }  };   @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_handler);     findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        finish();      }    });     new Thread(new Runnable() {      @Override      public void run() {        handler.sendEmptyMessage( MESSAGECODE ) ;        try {          Thread.sleep( 8000 );        } catch (InterruptedException e) {          e.printStackTrace();        }        handler.sendEmptyMessage( MESSAGECODE ) ;      }    }).start() ;   }}

  這段代碼運(yùn)行起來后,立即點(diǎn)擊 finish 按鈕,通過檢測,發(fā)現(xiàn) HandlerActivity 出現(xiàn)了內(nèi)存泄漏。當(dāng)Activity finish后,延時(shí)消息會(huì)繼續(xù)存在主線程消息隊(duì)列中8秒鐘,然后處理消息。而該消息引用了Activity的Handler對象,然后這個(gè)Handler又引用了這個(gè)Activity。這些引用對象會(huì)保持到該消息被處理完,這樣就導(dǎo)致該Activity對象無法被回收,從而導(dǎo)致了上面說的 Activity泄露。Handler 是個(gè)很常用也很有用的類,異步,線程安全等等。如果有下面這樣的代碼,會(huì)發(fā)生什么呢? handler.postDeslayed ,假設(shè) delay 時(shí)間是幾個(gè)小時(shí)… 這意味著什么?意味著只要 handler 的消息還沒有被處理結(jié)束,它就一直存活著,包含它的 Activity 就跟著活著。我們來想辦法修復(fù)它,修復(fù)的方案是 WeakReference ,也就是所謂的弱引用。垃圾回收器在回收的時(shí)候,是會(huì)忽視掉弱引用的,所以包含它的 Activity 會(huì)被正常清理掉。

如何避免

使用靜態(tài)內(nèi)部類

使用弱引用

修改后代碼是這樣的。

package rxnet.zyj.com.myapplication; import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View; import java.lang.ref.WeakReference; public class HandlerActivity extends AppCompatActivity {   private final static int MESSAGECODE = 1 ;  private static Handler handler ;   @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_handler);     findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        finish();      }    });     handler = new MyHandler( this ) ;     new Thread(new Runnable() {      @Override      public void run() {        handler.sendEmptyMessage( MESSAGECODE ) ;        try {          Thread.sleep( 8000 );        } catch (InterruptedException e) {          e.printStackTrace();        }        handler.sendEmptyMessage( MESSAGECODE ) ;      }    }).start() ;   }   private static class MyHandler extends Handler {    WeakReference<HandlerActivity> weakReference ;     public MyHandler(HandlerActivity activity ){      weakReference = new WeakReference<HandlerActivity>( activity) ;    }     @Override    public void handleMessage(Message msg) {      super.handleMessage(msg);      if ( weakReference.get() != null ){        // update android ui        Log.d("mmmmmmmm" , "handler " + msg.what ) ;      }    }  }}

  這個(gè)Handler已經(jīng)使用了靜態(tài)內(nèi)部類,并且使用了弱引用。但是這個(gè)并沒有完全解決 HandlerActivity 內(nèi)存泄漏的問題,罪魁禍?zhǔn)资蔷€程創(chuàng)建的方式出了問題,就像本文的第一個(gè)例子一樣。改進(jìn)的方式,是把Runnable類寫成靜態(tài)內(nèi)部類。

最終完整的代碼如下:

package rxnet.zyj.com.myapplication; import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View; import java.lang.ref.WeakReference; public class HandlerActivity extends AppCompatActivity {   private final static int MESSAGECODE = 1 ;  private static Handler handler ;   @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_handler);     findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        finish();      }    });     //創(chuàng)建Handler    handler = new MyHandler( this ) ;     //創(chuàng)建線程并且啟動(dòng)線程    new Thread( new MyRunnable() ).start();  }   private static class MyHandler extends Handler {    WeakReference<HandlerActivity> weakReference ;     public MyHandler(HandlerActivity activity ){      weakReference = new WeakReference<HandlerActivity>( activity) ;    }     @Override    public void handleMessage(Message msg) {      super.handleMessage(msg);      if ( weakReference.get() != null ){        // update android ui        Log.d("mmmmmmmm" , "handler " + msg.what ) ;      }    }  }   private static class MyRunnable implements Runnable {     @Override    public void run() {      handler.sendEmptyMessage( MESSAGECODE ) ;      try {        Thread.sleep( 8000 );      } catch (InterruptedException e) {        e.printStackTrace();      }      handler.sendEmptyMessage( MESSAGECODE ) ;    }  }}

  等等,還沒完呢?

上面這個(gè)代碼已經(jīng)有效的解決了Handler,Runnable 引用Activity實(shí)例從而導(dǎo)致內(nèi)存泄漏的問題,但是這不夠。因?yàn)閮?nèi)存泄漏的核心原因就是這個(gè)某個(gè)對象應(yīng)該被系統(tǒng)回收內(nèi)存的時(shí)候,卻被其他對象引用,造成該內(nèi)存無法回收。所以我們在寫代碼的時(shí)候,要始終繃著這個(gè)弦。再回到上面這個(gè)問題,當(dāng)當(dāng)前Activity調(diào)用finish銷毀的時(shí)候,在這個(gè)Activity里面所有線程是不是應(yīng)該在OnDestory()方法里,取消線程。當(dāng)然是否取消異步任務(wù),要看項(xiàng)目具體的需求,比如在Activity銷毀的時(shí)候,啟動(dòng)一個(gè)線程,異步寫log日志到本地磁盤,針對這個(gè)需求卻需要在OnDestory()方法里開啟線程。所以根據(jù)當(dāng)前環(huán)境做出選擇才是正解。

所以我們還可以修改代碼為:在onDestroy() 里面移除所有的callback 和 Message 。

package rxnet.zyj.com.myapplication; import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View; import java.lang.ref.WeakReference; public class HandlerActivity extends AppCompatActivity {   private final static int MESSAGECODE = 1 ;  private static Handler handler ;   @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_handler);     findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        finish();      }    });     //創(chuàng)建Handler    handler = new MyHandler( this ) ;     //創(chuàng)建線程并且啟動(dòng)線程    new Thread( new MyRunnable() ).start();  }   private static class MyHandler extends Handler {    WeakReference<HandlerActivity> weakReference ;     public MyHandler(HandlerActivity activity ){      weakReference = new WeakReference<HandlerActivity>( activity) ;    }     @Override    public void handleMessage(Message msg) {      super.handleMessage(msg);      if ( weakReference.get() != null ){        // update android ui        Log.d("mmmmmmmm" , "handler " + msg.what ) ;      }    }  }   private static class MyRunnable implements Runnable {     @Override    public void run() {      handler.sendEmptyMessage( MESSAGECODE ) ;      try {        Thread.sleep( 8000 );      } catch (InterruptedException e) {        e.printStackTrace();      }      handler.sendEmptyMessage( MESSAGECODE ) ;    }  }   @Override  protected void onDestroy() {    super.onDestroy();     //如果參數(shù)為null的話,會(huì)將所有的Callbacks和Messages全部清除掉。    handler.removeCallbacksAndMessages( null );  }}

 

 4、AsyncTask造成內(nèi)存泄漏

package rxnet.zyj.com.myapplication; import android.os.AsyncTask;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View; public class Activity2 extends AppCompatActivity {   @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_2);     findViewById( R.id.finish2).setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        finish();      }    });      new AsyncTask<String,Integer,String>(){       @Override      protected String doInBackground(String... params) {        try {          Thread.sleep( 6000 );        } catch (InterruptedException e) {        }        return "ssss";      }       @Override      protected void onPostExecute(String s) {        super.onPostExecute(s);        Log.d( "mmmmmm activity2 " , "" + s ) ;      }     }.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR , "" ) ;       }}

  為什么?

上面代碼在activity中創(chuàng)建了一個(gè)匿名類AsyncTask,匿名類和非靜態(tài)內(nèi)部類相同,會(huì)持有外部類對象,這里也就是activity,因此如果你在Activity里聲明且實(shí)例化一個(gè)匿名的AsyncTask對象,則可能會(huì)發(fā)生內(nèi)存泄漏,如果這個(gè)線程在Activity銷毀后還一直在后臺執(zhí)行,那這個(gè)線程會(huì)繼續(xù)持有這個(gè)Activity的引用從而不會(huì)被GC回收,直到線程執(zhí)行完成。

   怎么解決?

 自定義靜態(tài)AsyncTask類A

syncTask的周期和Activity周期保持一致。也就是在Activity生命周期結(jié)束時(shí)要將AsyncTask cancel掉。

package rxnet.zyj.com.myapplication; import android.os.AsyncTask;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View; public class AsyncTaskActivity extends AppCompatActivity {   private static MyTask myTask ;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_asynctask);     findViewById( R.id.finish).setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        finish();      }    });     myTask = new MyTask() ;    myTask.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR , "") ;   }   private static class MyTask extends AsyncTask{     @Override    protected Object doInBackground(Object[] params) {      try {        //模擬耗時(shí)操作        Thread.sleep( 15000 );      } catch (InterruptedException e) {        e.printStackTrace();      }      return "";    }  }   @Override  protected void onDestroy() {    super.onDestroy();     //取消異步任務(wù)    if ( myTask != null ){      myTask.cancel(true ) ;    }  }}

5、Timer Tasks 造成內(nèi)存泄漏

package rxnet.zyj.com.myapplication; import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View; import java.util.Timer;import java.util.TimerTask; public class TimerActivity extends AppCompatActivity {   @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_2);     findViewById( R.id.finish2).setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        finish();      }    });     //開始定時(shí)任務(wù)    timer();  }   void timer(){    new Timer().schedule(new TimerTask() {      @Override      public void run() {        while(true);      }    },1000 ); // 1秒后啟動(dòng)一個(gè)任務(wù)  }}

  為什么? 

這里內(nèi)存泄漏在于Timer和TimerTask沒有進(jìn)行Cancel,從而導(dǎo)致Timer和TimerTask一直引用外部類Activity。

  怎么解決? 

在適當(dāng)?shù)臅r(shí)機(jī)進(jìn)行Cancel。

TimerTask用靜態(tài)內(nèi)部類

   注意:在網(wǎng)上看到一些資料說,解決TimerTask內(nèi)存泄漏可以使用在適當(dāng)?shù)臅r(shí)機(jī)進(jìn)行Cancel。經(jīng)過測試,證明單單使用在適當(dāng)?shù)臅r(shí)機(jī)進(jìn)行Cancel , 還是有內(nèi)存泄漏的問題。所以一定要用靜態(tài)內(nèi)部類配合使用。

package rxnet.zyj.com.myapplication; import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View; import java.util.Timer;import java.util.TimerTask; public class TimerActivity extends AppCompatActivity {   private TimerTask timerTask ;   @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_2);     findViewById( R.id.finish2).setOnClickListener(new View.OnClickListener() {      @Override      public void onClick(View v) {        finish();      }    });     //開始定時(shí)任務(wù)    timer();  }   void timer(){    timerTask = new MyTimerTask() ;    new Timer().schedule( timerTask ,1000 ); // 1秒后啟動(dòng)一個(gè)任務(wù)  }   private static class MyTimerTask extends TimerTask{     @Override    public void run() {      while(true){        Log.d( "ttttttttt" , "timerTask" ) ;      }    }  }   @Override  protected void onDestroy() {    super.onDestroy();     //取消定時(shí)任務(wù)    if ( timerTask != null ){      timerTask.cancel() ;    }  }}

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 钦州市| 大余县| 灵石县| 木兰县| 大竹县| 瑞金市| 德昌县| 扎鲁特旗| 黄平县| 通化市| 麻江县| 霍城县| 城步| 泊头市| 凤庆县| 普兰县| 昭觉县| 葫芦岛市| 普宁市| 永定县| 合水县| 阳信县| 望谟县| 马龙县| 正安县| 喜德县| 重庆市| 全南县| 三门县| 中阳县| 女性| 昌宁县| 浮梁县| 汶上县| 定州市| 大田县| 阳新县| 渝北区| 宕昌县| 皮山县| 惠来县|