Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android應用開發入門(四十)多線程之進程與線程

Android應用開發入門(四十)多線程之進程與線程

編輯:Android開發實例

前言

  對於Android程序中,使用多線程的技術是必不可少的,就拿之前最簡單的例子來說明,對於Android4.0+的應用而言,訪問網絡必須另起線程才可以訪問。本文介紹Android下進程和線程,以及它們的特點及應用,並通過幾個Demo來展示Android中簡單的線程中操作UI線程的組件。

進程與線程

  一般來說,Android中為一個應用程序開啟一個進程進行執行,在這個應用程序中的所有組件,通過單獨的線程進行執行,而其中所有的線程,共享該應用程序進程的資源。當一個應用程序啟動的時候,Android系統啟動一個新的Linux應用程序的進程和一個執行線程。默認情況下,一個應用程序運行中的所有組件運行在相同的進程和線程中,這裡的線程一般稱為主線程。如果一個應用程序的組件開始的時候,已經存在一個進程,那麼應用程序會在與它相同的執行線程中開始這個組件。

進程

  默認情況下,同一應用程序下的所有組件運行在同一進程中,大多數應用程序不應該改變這個。然而,如果需要控制那個進程屬於那個組件,可以在AndroidManifest.xml文件中進行配置。一般來說,組件元素:<activity>、<service>、<receiver>、<provider>均支持一個android:process屬性,可以設置這個屬性讓不同的組件單獨運行在自己的進程中,也可以使用這個屬性使不同的應用程序組件運行在相同的進程中,並共享相同的Linux用戶ID和賦予同樣的證書。

  Tips:<application>元素也支持android:process屬性,用於設置所有的組件。

  Android在內存較低的情況下,會關閉一些優先級較低的進程以增大內存運行更重要的進程,而在這個進程中的所有線程,也會被同時銷毀。在內存足夠的情況下,Android系統會視圖盡可能保持應用程序進程,以達到下次的運行的快速啟動,但最終需要移除舊的金慈寧宮,回收內存用於新的或更重要的進程。通過進程的優先級來判斷是否被回收,一般會回收優先級低的進程,以給優先級高的進程騰出資源。

  下面是五類Android進程,他們的優先級順序排列:

  1. Foreground process:前台進程。
  2. Visible prcess:可見進程。
  3. Service process:服務進程。
  4. Background process:後台進程。
  5. Empty process:空進程。

  Tips:一個進程的優先級是可以變化的。

線程

  當應用程序啟動時,系統會創建一個執行線程在這個應用程序的的進程中,一般被稱為“主線程”。這個線程是非常重要的,因為它負責把事件分發給響應的用戶組件,包括繪制事件等,因此主線程又被稱為UI線程。系統並不會為每個組件創建一個單獨的線程,而是在UI線程中,完成這些組件的初始化的,因此系統回調方法是運行在UI線程中,如click事件。

  當程序執行比較復雜的工作來應對用戶交互的時候,哪怕應用程序被正確的執行了,單線程模式也可能會導致運行性能很低下。舉例來說,如果一切的應用功能都發生在UI線程中,當執行耗時操作的時候,如訪問網絡或查詢數據,均會阻塞UI先,將導致其他的事件不被分發到事件隊列中,包括屏幕繪制事件。導致從用戶的角度來看,應用程序死掉了。而在Android系統中,當UI線程被阻塞超過幾秒鐘(大約是5秒)的時候,會彈出“應用程序沒有響應”的對話框,造成用戶體驗差,可能會迫使用戶決定退出你的應用或者干脆直接卸載它。

  此外,Android的UI ToolKit包下的所有組件都不是線程安全的,所以,不能在一個單獨的工作線程中操作這些UI組件,必須在UI線程中操作。因此,對於單線程模型,Android有兩個規則:

  1. 不能阻塞UI線程
  2. 不能在工作線程中訪問Android UI ToolKit包下的組件。

  對於耗時的操作,應該放在單獨的線程中。例如:下面通過一個Demo監聽按鈕點擊事件,下載一個圖片,從單獨的線程中,並顯示在一個ImageView中。

  1. btnError2.setOnClickListener(new View.OnClickListener() {              
  2.             @Override 
  3.             public void onClick(View v) {  
  4.                 // 增加一個線程訪問網絡  
  5.                 new Thread(new Runnable() {  
  6.                     @Override 
  7.                     public void run() {  
  8.                         // 獲取地址下的圖片  
  9.                         Bitmap btm=loadImageFromNetwork("http://ww4.sinaimg.cn/bmiddle/786013a5jw1e7akotp4bcj20c80i3aao.jpg");  
  10.                         imageView1.setImageBitmap(btm);                          
  11.                     }  
  12.                 }).start();  
  13.                   
  14.             }  
  15.         }); 

 起初,這似乎是合理的,啟動了一個新線程來訪問網絡,但是它違反了規則二,不能在Android UI主線程之外修改UI組件,而在click中new Thread的是一個工作線程,在工作線程中無法放為UI組件,以上Demo會報錯。

  要修正上面的錯誤,Android提供幾種方法可以從其他線程中訪問UI線程:

  • Activity.runOnUiThread(Runnable):運行在指定的UI線程上,如果當前線程是UI線程,那麼立即執行,如果當前線程不是UI線程,則發布到UI線程的事件隊列中。
  • View.post(Runnable):將事件發布到UI線程中,立即被執行。
  • View.postDelayed(Runnanle,long):將事件發布到UI線程中,延遲被執行,延遲數為傳遞的long參數。

  下面通過兩個Dem來通過上面介紹的方法來操作UI組件:

  Activity.runOnUiThread:

  1. btnRunOnUiThread.setOnClickListener(new View.OnClickListener() {  
  2.               
  3.             @Override 
  4.             public void onClick(View v) {  
  5.                 new Thread(new Runnable() {  
  6.                       
  7.                     @Override 
  8.                     public void run() {  
  9.                         final Bitmap btm=loadImageFromNetwork("http://ww4.sinaimg.cn/bmiddle/786013a5jw1e7akotp4bcj20c80i3aao.jpg");  
  10.                         MainActivity.this.runOnUiThread(new Runnable() {                              
  11.                             @Override 
  12.                             public void run() {  
  13.                                 imageView1.setImageBitmap(btm);                                  
  14.                             }  
  15.                         });  
  16.                     }  
  17.                 }).start();  
  18.             }  
  19.         }); 

 

  效果演示:

   View.post

  1. btnPost.setOnClickListener(new View.OnClickListener() {  
  2.               
  3.             @Override 
  4.             public void onClick(View v) {  
  5.                 new Thread(new Runnable() {  
  6.                       
  7.                     @Override 
  8.                     public void run() {  
  9.                         final Bitmap btm=loadImageFromNetwork("http://ww1.sinaimg.cn/bmiddle/88ff29e8jw1e7pjnpfxbrj20dp0a90tb.jpg");  
  10.                         imageView1.post(new Runnable() {  
  11.                               
  12.                             @Override 
  13.                             public void run() {  
  14.                                 // TODO Auto-generated method stub  
  15.                                 imageView1.setImageBitmap(btm);  
  16.                             }  
  17.                         });  
  18.                     }  
  19.                 }).start();                  
  20.             }  
  21.         }); 

 

 效果演示:

  以上Demo中,通過訪問網絡獲取圖片的方法,通過HttpClient實現,不清楚的朋友可以參見:http://www.fengfly.com/plus/view-213371-1.html。

  源碼下載

 

總結

  雖然上面介紹了幾種方式在工作線程中把消息發布到UI線程的消息隊列的方式來訪問UI組件。但是一般實際的開發當中,這種代碼會變的復雜且難於維護。處理更復雜的線程間交互,可以考慮使用Handle+Message,在UI線程中處理工作線程發送過來的消息,還可以繼承AsyncTask類來簡化工作線程發送消息到主線程中交互UI組件。這兩種方式會在接下來的博客中介紹到。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved