Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Activitys, Threads, & Memory Leaks

Activitys, Threads, & Memory Leaks

編輯:關於Android編程

Activitys, Threads, & Memory Leaks     在Android編程中,一個公認的難題是在Activity的生命周期如何協調長期運行的任務和避免有可能出現的內存洩漏問題。考慮下面一段代碼,在Activity創建時啟動了一個線程,在線程中無限循環。  
/** 
 * Example illustrating how threads persist across configuration 
 * changes (which cause the underlying Activity instance to be 
 * destroyed). The Activity context also leaks because the thread 
 * is instantiated as an anonymous class, which holds an implicit 
 * reference to the outer Activity instance, therefore preventing 
 * it from being garbage collected. 
 */  
publicclass MainActivity extends Activity {  
   
  @Override  
  protectedvoid onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    exampleOne();  
  }  
   
  privatevoid exampleOne() {  
    newThread() {  
      @Override  
      publicvoid run() {  
        while(true) {  
          SystemClock.sleep(1000);  
        }  
      }  
    }.start();  
  }  
}  

 

  當手機配置發生改變時(例如手機朝向發生改變),會導致整個Activity銷毀和重新創建。我們很容易認為Andriod會為我們做好一切清理工作,回收與Activity相關聯的內存和正在運行的線程。但是,事情並不和我們想象中的一樣!Activity相關聯的內存和正在運行的內存都不會回收,會發生洩漏,一個直接的結果就是程序的性能越來越差。   Activity是如何洩漏的   如果讀過LZ的前一篇文章會發現一個非靜態的匿名類會持有外部類的一個隱式引用(上述代碼中的MainActivity),只要非靜態的匿名類對象沒有被回收,MainActivity就不會被回收,MainActivity所關聯的資源和視圖都不會被回收,發生比較嚴重的內存洩漏。要解決MainActivity的內存洩漏問題,只需把非靜態的Thread匿名類定義成靜態的內部類就行了(靜態的內部類不會持有外部類的一個隱式引用),如下代碼所示    
/** 
 * This example avoids leaking an Activity context by declaring the 
 * thread as a private static inner class, but the threads still 
 * continue to run even across configuration changes. The DVM has a 
 * reference to all running threads and whether or not these threads 
 * are garbaged collected has nothing to do with the Activity lifecycle. 
 * Active threads will continue to run until the kernel destroys your 
 * application's process. 
 */  
publicclass MainActivity extends Activity {  
   
  @Override  
  protectedvoid onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    exampleTwo();  
  }  
   
  privatevoid exampleTwo() {  
    newMyThread().start();  
  }  
   
  privatestatic class MyThread extends Thread {  
    @Override  
    publicvoid run() {  
      while(true) {  
        SystemClock.sleep(1000);  
      }  
    }  
  }  
}  

 

      現在新創建的Thread不會持有MainActivity的一個隱式引用,當手機朝向發生改變時不會阻止垃圾回收器對舊MainActivity的回收工作~   Thread是如何洩漏的   在提到的第二個問題中,一旦一個新的Activity創建,那麼就有一個Thread永遠得不到清理回收,發生內存洩漏。Threads在Java中是GC roots;意味著Dalvik Virtual Machine (DVM) 會為所有活躍的threads在運行時系統中保持一個硬引用,這會導致threads一直處於運行狀態,垃圾收集器將永遠不可能回收它。出於這個原因,我們應當為threads添加一個結束的邏輯,如下代碼所示  
/** 
 * Same as example two, except for this time we have implemented a 
 * cancellation policy for our thread, ensuring that it is never 
 * leaked! onDestroy() is usually a good place to close your active 
 * threads before exiting the Activity. 
 */  
publicclass MainActivity extends Activity {  
  privateMyThread mThread;  
   
  @Override  
  protectedvoid onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    exampleThree();  
  }  
   
  privatevoid exampleThree() {  
    mThread = new MyThread();  
    mThread.start();  
  }  
   
  /** 
   * Static inner classes don't hold implicit references to their 
   * enclosing class, so the Activity instance won't be leaked across 
   * configuration changes. 
   */  
  privatestatic class MyThread extends Thread {  
    privateboolean mRunning = false;  
   
    @Override  
    publicvoid run() {  
      mRunning = true;  
      while(mRunning) {  
        SystemClock.sleep(1000);  
      }  
    }  
   
    publicvoid close() {  
      mRunning = false;  
    }  
  }  
   
  @Override  
  protectedvoid onDestroy() {  
    super.onDestroy();  
    mThread.close();  
  }  
}  

 

    在上述的代碼中,當Activity結束銷毀時在onDestroy()方法中結束了新創建的線程,保證了thread不會發生洩漏。但是如果你想在手機配置發生改變時保持原有的線程而不重新創建的話,你可以考慮使用fragment來保留原有的線程,以備下一次使用具體做法可以參考我之前的一篇文章http://blog.csdn.net/tu_bingbing/article/details/9274289,關於這方面 APIdemos  中也做了相關的實現。    結論   在Android中,長時間運行的任務和Acyivity生命周期進行協調會有點困難,如果你不加以小心的話會導致內存洩漏。關於如何處理這個棘手的問題,下面有幾個基本的技巧供參考        1、使用靜態內部類/匿名類,不要使用非靜態內部類/匿名類.非靜態內部類/匿名類會隱式的持有外部類的引用,外部類就有可能發生洩漏。而靜態內部類/匿名類不會隱式的持有外部類引用,外部類會以正常的方式回收,如果你想在靜態內部類/匿名類中使用外部類的屬性或方法時,可以顯示的持有一個弱引用。        2、不要以為Java永遠會幫你清理回收正在運行的threads.在上面的代碼中,我們很容易誤以為當Activity結束銷毀時會幫我們把正在運行的thread也結束回收掉,但事情永遠不是這樣的!Java threads會一直存在,只有當線程運行完成或被殺死掉,線程才會被回收。所以我們應該養成為thread設置退出邏輯條件的習慣。        3、適當的考慮下是否應該使用線程.Android應用框架設計了許多的類來簡化執行後台任務,我們可以使用與Activity生命周期相關聯的Loaders來執行簡短的後台查詢任務。如果一個線程不依賴與Activity,我們還可以使用Service來執行後台任務,然後用BroadcastReceiver來向Activity報告結果。另外需要注意的是本文討論的thread同樣使用於AsyncTasks,AsyncTask同樣也是由線程來實現,只不過使用了Java5.0新增並發包中的功能,但同時需要注意的是根據官方文檔所說,AsyncTask適用於執行一些簡短的後台任務  
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved