Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android游戲 >> Android游戲開發 >> Android游戲開發教程之十六:多線程的操作方式

Android游戲開發教程之十六:多線程的操作方式

編輯:Android游戲開發

Android游戲開發教程之十六:多線程的操作方式

  游戲開發與軟件開發多線程的重要性

  如果程序主線程被阻塞超過5秒,系統會提示“應用程序無響應” 這就是ANR 。 ANR的全稱是Application Not Responding,使用多線程可以避免ANR。但是這裡要注意一下不要為了避免ANR而過多的使用多線程,除非萬不得已的情況。 比如訪問網絡服務端返回的過慢、數據過多導致滑動屏幕不流暢、或者I/O讀取過大的資源等等。這裡可以開啟一個新線程來處理這些耗時的操作。 如果過多使用多線程會出現數據同步的問題須要程序員去處理,所以使用多線程的時候盡量保持它的獨立不會被其它線程干預。java語言提供了一個線程鎖的概念 synchronized 可以添加對象鎖與方法鎖專門避免多線程同時訪問一個方法或者一個對象導致的問題,有興趣的朋友可以去看看這裡我不羅嗦啦 。

  1.Thread與Handler執行多線程

Android游戲開發教程之十六:多線程的操作方式

  Handler主要用於程序主線程與我們自己創建的線程進行通信。在這個例子中點擊按鈕後創建一個新的線程去循環的加載100張圖片每加載完一張圖片在Thread中使用Handler發送消息通知UI線程更新顯示,直到加在完畢通知UI顯示加載完成一共耗時多少秒。可見Handler的重要性它就是主線程與我們自己創建的線程的橋梁啊~~~

Java代碼
  1. import java.io.InputStream;  
  2.    
  3. import android.app.Activity;  
  4. import android.content.Context;  
  5. import android.graphics.Bitmap;  
  6. import android.graphics.BitmapFactory;  
  7. import android.os.Bundle;  
  8. import android.os.Handler;  
  9. import android.os.Message;  
  10. import android.view.View;  
  11. import android.view.View.OnClickListener;  
  12. import android.widget.Button;  
  13. import android.widget.TextView;  
  14.    
  15. public class SingleActivity extends Activity {  
  16.    
  17.     /**讀取進度**/  
  18.     public final static int LOAD_PROGRESS = 0;   
  19.    
  20.     /**標志讀取進度結束**/  
  21.     public final static int LOAD_COMPLETE = 1;   
  22.    
  23.     /** 開始加載100張圖片按鈕 **/  
  24.     Button mButton = null;  
  25.     /** 顯示內容 **/  
  26.     TextView mTextView = null;  
  27.     /** 加載圖片前的時間 **/  
  28.     Long mLoadStatr = 0L;  
  29.     /** 加載圖片後的時間 **/  
  30.     Long mLoadEnd = 0L;  
  31.    
  32.     Context mContext = null;  
  33.    
  34.     //接收傳遞過來的信息  
  35.     Handler handler = new Handler() {  
  36.     @Override  
  37.     public void handleMessage(Message msg) {  
  38.         switch (msg.what) {  
  39.         case LOAD_PROGRESS:  
  40.         mTextView.setText("當前讀取到第" + msg.arg1 + "張圖片");  
  41.         break;  
  42.         case LOAD_COMPLETE:  
  43.         mTextView.setText("讀取結束一共耗時" + msg.arg1 + "毫秒");  
  44.         break;  
  45.         }  
  46.         super.handleMessage(msg);  
  47.     }  
  48.     };  
  49.    
  50.     @Override  
  51.     protected void onCreate(Bundle savedInstanceState) {  
  52.     setContentView(R.layout.single);  
  53.     mContext = this;  
  54.    
  55.     /** 拿到button 與 TextView 對象 **/  
  56.     mButton = (Button) findViewById(R.id.button0);  
  57.     mTextView = (TextView) findViewById(R.id.textView0);  
  58.     mTextView.setText("點擊按鈕開始更新時間");  
  59.     mButton.setOnClickListener(new OnClickListener() {  
  60.         @Override  
  61.         public void onClick(View arg0) {  
  62.         //開始讀取圖片  
  63.         LoadImage();  
  64.         }  
  65.     });  
  66.    
  67.     super.onCreate(savedInstanceState);  
  68.     }  
  69.    
  70.     public void LoadImage() {  
  71.     new Thread() {  
  72.         @Override  
  73.         public void run() {  
  74.         //得到加載圖片開始的時間  
  75.         mLoadStatr = System.currentTimeMillis();  
  76.    
  77.         for (int i = 0; i < 100; i++) {  
  78.             // 這裡循環加載圖片100遍  
  79.             ReadBitMap(mContext, R.drawable.bg);  
  80.    
  81.             // 每讀取完一張圖片將進度甩給handler  
  82.             Message msg = new Message();  
  83.             msg.what = LOAD_PROGRESS;  
  84.             msg.arg1 = i + 1;  
  85.             handler.sendMessage(msg);  
  86.         }  
  87.    
  88.         //得到加載圖片結束的時間  
  89.         mLoadEnd = System.currentTimeMillis();  
  90.    
  91.         //100張圖片加載完成  
  92.         Message msg = new Message();  
  93.         msg.what = LOAD_COMPLETE;  
  94.         msg.arg1 = (int) (mLoadEnd - mLoadStatr);  
  95.         handler.sendMessage(msg);  
  96.         }  
  97.     }.start();  
  98.    
  99.     }  
  100.    
  101.     /** 
  102.      * 讀取本地資源的圖片 
  103.      * 
  104.      * @param context 
  105.      * @param resId 
  106.      * @return 
  107.      */  
  108.     public Bitmap ReadBitMap(Context context, int resId) {  
  109.     BitmapFactory.Options opt = new BitmapFactory.Options();  
  110.     opt.inPreferredConfig = Bitmap.Config.RGB_565;  
  111.     opt.inPurgeable = true;  
  112.     opt.inInputShareable = true;  
  113.     // 獲取資源圖片  
  114.     InputStream is = context.getResources().openRawResource(resId);  
  115.     return BitmapFactory.decodeStream(is, null, opt);  
  116.     }  
  117. }  

  2.TimerTask與Handler延遲多線程

Android游戲開發教程之十六:多線程的操作方式

  Timer與TimerTask可以構建一個延遲器 就好比開啟一個線程每隔一段規定的時間訪問一次。可以在這個線程中去關閉這個Timer 與TimerTask ,舉個例子比如現在我要做一個網游帳號登錄超時客戶端的檢測 用戶輸入完帳號密碼點擊登錄這時候我開啟一個TimerTask每過1秒檢查一下用戶是否登錄成功,過了10秒如果還沒有登錄成功提示他登陸超時。這個時候我就須要在這個檢測線程中去關閉Timer 與TimerTask  因為不需要它在循環檢測了。 調用cancel()就可以關閉,請同學們閱讀下面這個例子。

Java代碼
  1. import java.util.Timer;  
  2. import java.util.TimerTask;  
  3. import android.app.Activity;  
  4. import android.content.Context;  
  5. import android.os.Bundle;  
  6. import android.os.Handler;  
  7. import android.os.Message;  
  8. import android.view.View;  
  9. import android.view.View.OnClickListener;  
  10. import android.widget.Button;  
  11. import android.widget.TextView;  
  12.    
  13. public class TimerTaskActivity extends Activity {  
  14.    
  15.     /**執行Timer進度**/  
  16.     public final static int LOAD_PROGRESS = 0;   
  17.    
  18.     /**關閉Timer進度**/  
  19.     public final static int CLOSE_PROGRESS = 1;   
  20.    
  21.     /** 開始TimerTask按鈕 **/  
  22.     Button mButton0 = null;  
  23.    
  24.     /** 關閉TimerTask按鈕 **/  
  25.     Button mButton1 = null;  
  26.    
  27.     /** 顯示內容 **/  
  28.     TextView mTextView = null;  
  29.    
  30.     Context mContext = null;  
  31.    
  32.     /**Timer對象**/  
  33.     Timer mTimer = null;  
  34.    
  35.     /**TimerTask對象**/  
  36.     TimerTask mTimerTask = null;  
  37.    
  38.     /**記錄TimerID**/  
  39.     int mTimerID = 0;  
  40.    
  41.     /**接收傳遞過來的信息**/  
  42.     Handler handler = new Handler() {  
  43.     @Override  
  44.     public void handleMessage(Message msg) {  
  45.         switch (msg.what) {  
  46.         case LOAD_PROGRESS:  
  47.         mTextView.setText("當前TimerID為" + msg.arg1 );  
  48.         break;  
  49.         case CLOSE_PROGRESS:  
  50.         mTextView.setText("當前Timer已經關閉請重新開啟" );  
  51.         break;  
  52.    
  53.         }  
  54.         super.handleMessage(msg);  
  55.     }  
  56.     };  
  57.    
  58.     @Override  
  59.     protected void onCreate(Bundle savedInstanceState) {  
  60.     setContentView(R.layout.timer);  
  61.     mContext = this;  
  62.    
  63.     /** 拿到button 與 TextView 對象 **/  
  64.     mButton0 = (Button) findViewById(R.id.button0);  
  65.     mButton1 = (Button) findViewById(R.id.button1);  
  66.     mTextView = (TextView) findViewById(R.id.textView0);  
  67.     mTextView.setText("點擊按鈕開始更新時間");  
  68.    
  69.     //開始  
  70.     mButton0.setOnClickListener(new OnClickListener() {  
  71.         @Override  
  72.         public void onClick(View arg0) {  
  73.         //開始執行timer  
  74.         StartTimer();  
  75.         }  
  76.     });  
  77.    
  78.     //關閉  
  79.     mButton1.setOnClickListener(new OnClickListener() {  
  80.         @Override  
  81.         public void onClick(View arg0) {  
  82.         //停止執行timer  
  83.         CloseTimer();  
  84.         }  
  85.     });  
  86.    
  87.     super.onCreate(savedInstanceState);  
  88.     }  
  89.    
  90.     public void StartTimer() {  
  91.    
  92.     if (mTimer == null) {  
  93.         mTimerTask = new TimerTask() {  
  94.         public void run() {  
  95.             //mTimerTask與mTimer執行的前提下每過1秒進一次這裡  
  96.             mTimerID ++;  
  97.             Message msg = new Message();  
  98.             msg.what = LOAD_PROGRESS;  
  99.             msg.arg1 = (int) (mTimerID);  
  100.             handler.sendMessage(msg);  
  101.         }  
  102.         };  
  103.         mTimer = new Timer();  
  104.    
  105.         //第一個參數為執行的mTimerTask  
  106.         //第二個參數為延遲的時間 這裡寫1000的意思是mTimerTask將延遲1秒執行  
  107.         //第三個參數為多久執行一次 這裡寫1000表示每1秒執行一次mTimerTask的Run方法  
  108.         mTimer.schedule(mTimerTask, 1000, 1000);  
  109.     }  
  110.    
  111.     }  
  112.    
  113.     public void CloseTimer() {  
  114.    
  115.     //在這裡關閉mTimer 與 mTimerTask  
  116.     if (mTimer != null) {  
  117.         mTimer.cancel();  
  118.         mTimer = null;  
  119.     }  
  120.     if (mTimerTask != null) {  
  121.         mTimerTask = null;  
  122.     }  
  123.    
  124.     /**ID重置**/  
  125.     mTimerID = 0;  
  126.    
  127.     //這裡發送一條只帶what空的消息  
  128.     handler.sendEmptyMessage(CLOSE_PROGRESS);  
  129.     }  
  130. }  

  3.AsyncTask執行多線程

Android游戲開發教程之十六:多線程的操作方式

  執行AsyncTask

  onPreExecute()///首先執行這個方法,它在UI線程中 可以執行一些異步操作 比如初始化一些東西

  doInBackground(Object… arg0) //異步後台執行 ,執行完畢可以返回出去一個結果object對象

  onPostExecute(Object result) //可以拿到執行中的進度 當然進度須要在doInBackground中手動調用publishProgress()方法返回通過例子可以清楚的看到計算出讀取100張圖片的時間,執行的效率上來說AsyncTask 沒有Thread效率塊,但是AsyncTask 比Thread更規整,它可是時時的拿到異步線程中進度以及最後的結果集,可以讓我們的代碼更佳規范。這裡說一下 Thread能做到的事AsyncTask 都可以做到 但是AsyncTask  能做到的事Thread 不一定能做到就算勉強做到也很麻煩 。我給大家的建議是如果處理大量的異步操作就用AsyncTask 如果少部分的則使用Thread

Java代碼
  1. import java.io.InputStream;  
  2. import java.util.Timer;  
  3. import java.util.TimerTask;  
  4. import android.app.Activity;  
  5. import android.content.Context;  
  6. import android.graphics.Bitmap;  
  7. import android.graphics.BitmapFactory;  
  8. import android.os.AsyncTask;  
  9. import android.os.Bundle;  
  10. import android.view.View;  
  11. import android.view.View.OnClickListener;  
  12. import android.widget.Button;  
  13. import android.widget.TextView;  
  14.    
  15. public class AsyncTaskActivity extends Activity {  
  16.    
  17.     /**執行Timer進度**/  
  18.     public final static int LOAD_PROGRESS = 0;   
  19.    
  20.     /**關閉Timer進度**/  
  21.     public final static int CLOSE_PROGRESS = 1;   
  22.    
  23.     /** 開始StartAsync按鈕 **/  
  24.     Button mButton0 = null;  
  25.    
  26.     /** 顯示內容 **/  
  27.     TextView mTextView = null;  
  28.    
  29.     Context mContext = null;  
  30.    
  31.     /**Timer對象**/  
  32.     Timer mTimer = null;  
  33.    
  34.     /**TimerTask對象**/  
  35.     TimerTask mTimerTask = null;  
  36.    
  37.     /**記錄TimerID**/  
  38.     int mTimerID = 0;  
  39.    
  40.     @Override  
  41.     protected void onCreate(Bundle savedInstanceState) {  
  42.     setContentView(R.layout.async);  
  43.     mContext = this;  
  44.    
  45.     /** 拿到button 與 TextView 對象 **/  
  46.     mButton0 = (Button) findViewById(R.id.button0);  
  47.     mTextView = (TextView) findViewById(R.id.textView0);  
  48.     //開始  
  49.     mButton0.setOnClickListener(new OnClickListener() {  
  50.         @Override  
  51.         public void onClick(View arg0) {  
  52.         //開始執行StartAsync  
  53.         StartAsync();  
  54.         }  
  55.     });  
  56.    
  57.     super.onCreate(savedInstanceState);  
  58.     }  
  59.    
  60.     public void StartAsync() {  
  61.     new AsyncTask<Object, Object, Object>() {  
  62.    
  63.         @Override  
  64.         protected void onPreExecute() {  
  65.         //首先執行這個方法,它在UI線程中 可以執行一些異步操作  
  66.         mTextView.setText("開始加載進度");  
  67.         super.onPreExecute();  
  68.         }  
  69.    
  70.         @Override  
  71.         protected Object doInBackground(Object... arg0) {  
  72.         //異步後台執行 ,執行完畢可以返回出去一個結果object對象  
  73.    
  74.         //得到開始加載的時間  
  75.         Long startTime = System.currentTimeMillis();  
  76.         for (int i = 0; i < 100; i++) {  
  77.             // 這裡循環加載圖片100遍  
  78.             ReadBitMap(mContext, R.drawable.bg);  
  79.             //執行這個方法會異步調用onProgressUpdate方法,可以用來更新UI  
  80.             publishProgress(i);  
  81.         }  
  82.         //得到結束加載的時間  
  83.         Long endTime = System.currentTimeMillis();  
  84.    
  85.         //將讀取時間返回  
  86.         return endTime - startTime;  
  87.         }  
  88.    
  89.         @Override  
  90.         protected void onPostExecute(Object result) {  
  91.         //doInBackground之行結束以後在這裡可以接收到返回的結果對象  
  92.    
  93.         mTextView.setText("讀取100張圖片一共耗時" + result+ "毫秒");  
  94.    
  95.         super.onPostExecute(result);  
  96.         }  
  97.    
  98.         @Override  
  99.         protected void onProgressUpdate(Object... values) {  
  100.             //時時拿到當前的進度更新UI  
  101.    
  102.         mTextView.setText("當前加載進度" + values[0]);  
  103.             super.onProgressUpdate(values);  
  104.         }  
  105.     }.execute();//可以理解為執行 這個AsyncTask  
  106.    
  107.     }  
  108.     /** 
  109.      * 讀取本地資源的圖片 
  110.      * 
  111.      * @param context 
  112.      * @param resId 
  113.      * @return 
  114.      */  
  115.     public Bitmap ReadBitMap(Context context, int resId) {  
  116.     BitmapFactory.Options opt = new BitmapFactory.Options();  
  117.     opt.inPreferredConfig = Bitmap.Config.RGB_565;  
  118.     opt.inPurgeable = true;  
  119.     opt.inInputShareable = true;  
  120.     // 獲取資源圖片  
  121.     InputStream is = context.getResources().openRawResource(resId);  
  122.     return BitmapFactory.decodeStream(is, null, opt);  
  123.     }  
  124. }  

  4.多線程Looper的使用

Android游戲開發教程之十六:多線程的操作方式

  Looper用來管理線程的消息隊列與循環隊列,在handler中默認為mainlooper來進行消息循環,如果在handler中開啟一個新的線程那麼在這個新的線程中就沒有Looper循環,如果想讓這個新的線程具有消息隊列與消息循環我們須要調用 Looper.prepare();拿到它的loop ,這樣就好比在Thread中創建了消息隊列與循環  需要調用   Looper.loop(); 它的意思就是執行這個消息循環,下面我給出一個例子希望大家好好閱讀。

Java代碼
  1. import java.io.InputStream;  
  2.    
  3. import android.app.Activity;  
  4. import android.content.Context;  
  5. import android.graphics.Bitmap;  
  6. import android.graphics.BitmapFactory;  
  7. import android.os.Bundle;  
  8. import android.os.Handler;  
  9. import android.os.Looper;  
  10. import android.os.Message;  
  11. import android.view.View;  
  12. import android.view.View.OnClickListener;  
  13. import android.widget.Button;  
  14. import android.widget.Toast;  
  15.    
  16. public class LooperActivity extends Activity {  
  17.     /** 發送消息按鈕 **/  
  18.     Button mButton = null;  
  19.     /** 加載圖片前的時間 **/  
  20.     Long mLoadStatr = 0L;  
  21.     /** 加載圖片後的時間 **/  
  22.     Long mLoadEnd = 0L;  
  23.    
  24.     Context mContext = null;  
  25.    
  26.     private Handler handler = new Handler() {  
  27.     public void handleMessage(Message msg) {  
  28.         new Thread() {  
  29.         @Override  
  30.         public void run() {  
  31.    
  32.             //如果handler不指定looper的話  
  33.             //默認為mainlooper來進行消息循環,  
  34.             //而當前是在一個新的線程中它沒有默認的looper  
  35.             //所以我們須要手動調用prepare()拿到他的loop  
  36.             //可以理解為在Thread創建Looper的消息隊列  
  37.             Looper.prepare();  
  38.    
  39.             Toast.makeText(LooperActivity.this, "收到消息",Toast.LENGTH_LONG).show();  
  40.    
  41.             //在這裡執行這個消息循環如果沒有這句  
  42.             //就好比只創建了Looper的消息隊列而  
  43.             //沒有執行這個隊列那麼上面Toast的內容是不會顯示出來的  
  44.             Looper.loop();  
  45.    
  46.         //如果沒有   Looper.prepare();  與 Looper.loop();  
  47.         //會拋出異常Can't create handler inside thread that has not called Looper.prepare()  
  48.         //原因是我們新起的線程中是沒有默認的looper所以須要手動調用prepare()拿到他的loop  
  49.    
  50.         }  
  51.         }.start();  
  52.     }  
  53.     };  
  54.    
  55.     @Override  
  56.     protected void onCreate(Bundle savedInstanceState) {  
  57.     setContentView(R.layout.loop);  
  58.     mContext = this;  
  59.    
  60.     /** 拿到button 與 TextView 對象 **/  
  61.     mButton = (Button) findViewById(R.id.button0);  
  62.     mButton.setOnClickListener(new OnClickListener() {  
  63.         @Override  
  64.         public void onClick(View arg0) {  
  65.         new Thread() {  
  66.             @Override  
  67.             public void run() {  
  68.    
  69.                 //發送一條空的消息  
  70.                     //空消息中必需帶一個what字段  
  71.                 //用於在handler中接收  
  72.                 //這裡暫時我先寫成0  
  73.                 handler.sendEmptyMessage(0);  
  74.    
  75.             }  
  76.         }.start();  
  77.         }  
  78.     });  
  79.    
  80.     super.onCreate(savedInstanceState);  
  81.     }  
  82.     /** 
  83.      * 讀取本地資源的圖片 
  84.      * 
  85.      * @param context 
  86.      * @param resId 
  87.      * @return 
  88.      */  
  89.     public Bitmap ReadBitMap(Context context, int resId) {  
  90.     BitmapFactory.Options opt = new BitmapFactory.Options();  
  91.     opt.inPreferredConfig = Bitmap.Config.RGB_565;  
  92.     opt.inPurgeable = true;  
  93.     opt.inInputShareable = true;  
  94.     // 獲取資源圖片  
  95.     InputStream is = context.getResources().openRawResource(resId);  
  96.     return BitmapFactory.decodeStream(is, null, opt);  
  97.     }  
  98. }  

  源碼下載地址:http://vdisk.weibo.com/s/aakLw

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