|
Android游戲開發(fā)之多線程的操作方式
游戲開發(fā)與軟件開發(fā)多線程的重要性
如果程序主線程被阻塞超過5秒,系統(tǒng)會(huì)提示“應(yīng)用程序無響應(yīng)” 這就是ANR 。 ANR的全稱是Application Not Responding,使用多線程可以避免ANR。但是這里要注意一下不要為了避免ANR而過多的使用多線程,除非萬不得已的情況。 比如訪問網(wǎng)絡(luò)服務(wù)端返回的過慢、數(shù)據(jù)過多導(dǎo)致滑動(dòng)屏幕不流暢、或者I/O讀取過大的資源等等。這里可以開啟一個(gè)新線程來處理這些耗時(shí)的操作。 如果過多使用多線程會(huì)出現(xiàn)數(shù)據(jù)同步的問題須要程序員去處理,所以使用多線程的時(shí)候盡量保持它的獨(dú)立不會(huì)被其它線程干預(yù)。java語言提供了一個(gè)線程鎖的概念 synchronized 可以添加對(duì)象鎖與方法鎖專門避免多線程同時(shí)訪問一個(gè)方法或者一個(gè)對(duì)象導(dǎo)致的問題,有興趣的朋友可以去看看這里我不羅嗦啦 。
1.Thread與Handler執(zhí)行多線程
Handler主要用于程序主線程與我們自己創(chuàng)建的線程進(jìn)行通信。在這個(gè)例子中點(diǎn)擊按鈕后創(chuàng)建一個(gè)新的線程去循環(huán)的加載100張圖片每加載完一張圖片在Thread中使用Handler發(fā)送消息通知UI線程更新顯示,直到加在完畢通知UI顯示加載完成一共耗時(shí)多少秒??梢奌andler的重要性它就是主線程與我們自己創(chuàng)建的線程的橋梁啊~~~ - import java.io.InputStream;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class SingleActivity extends Activity {
/**讀取進(jìn)度**/
public final static int LOAD_PROGRESS = 0;
/**標(biāo)志讀取進(jìn)度結(jié)束**/
public final static int LOAD_COMPLETE = 1;
/** 開始加載100張圖片按鈕 **/
Button mButton = null;
/** 顯示內(nèi)容 **/
TextView mTextView = null;
/** 加載圖片前的時(shí)間 **/
Long mLoadStatr = 0L;
/** 加載圖片后的時(shí)間 **/
Long mLoadEnd = 0L;
Context mContext = null;
//接收傳遞過來的信息
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case LOAD_PROGRESS:
mTextView.setText("當(dāng)前讀取到第" + msg.arg1 + "張圖片");
break;
case LOAD_COMPLETE:
mTextView.setText("讀取結(jié)束一共耗時(shí)" + msg.arg1 + "毫秒");
break;
}
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.single);
mContext = this;
/** 拿到button 與 TextView 對(duì)象 **/
mButton = (Button) findViewById(R.id.button0);
mTextView = (TextView) findViewById(R.id.textView0);
mTextView.setText("點(diǎn)擊按鈕開始更新時(shí)間");
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
//開始讀取圖片
LoadImage();
}
});
super.onCreate(savedInstanceState);
}
public void LoadImage() {
new Thread() {
@Override
public void run() {
//得到加載圖片開始的時(shí)間
mLoadStatr = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
// 這里循環(huán)加載圖片100遍
ReadBitMap(mContext, R.drawable.bg);
// 每讀取完一張圖片將進(jìn)度甩給handler
Message msg = new Message();
msg.what = LOAD_PROGRESS;
msg.arg1 = i + 1;
handler.sendMessage(msg);
}
//得到加載圖片結(jié)束的時(shí)間
mLoadEnd = System.currentTimeMillis();
//100張圖片加載完成
Message msg = new Message();
msg.what = LOAD_COMPLETE;
msg.arg1 = (int) (mLoadEnd - mLoadStatr);
handler.sendMessage(msg);
}
}.start();
}
/**
* 讀取本地資源的圖片
*
* @param context
* @param resId
* @return
*/
public Bitmap ReadBitMap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
// 獲取資源圖片
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
}
復(fù)制代碼 2.TimerTask與Handler延遲多線程
Timer與TimerTask可以構(gòu)建一個(gè)延遲器 就好比開啟一個(gè)線程每隔一段規(guī)定的時(shí)間訪問一次??梢栽谶@個(gè)線程中去關(guān)閉這個(gè)Timer 與TimerTask ,舉個(gè)例子比如現(xiàn)在我要做一個(gè)網(wǎng)游帳號(hào)登錄超時(shí)客戶端的檢測(cè) 用戶輸入完帳號(hào)密碼點(diǎn)擊登錄這時(shí)候我開啟一個(gè)TimerTask每過1秒檢查一下用戶是否登錄成功,過了10秒如果還沒有登錄成功提示他登陸超時(shí)。這個(gè)時(shí)候我就須要在這個(gè)檢測(cè)線程中去關(guān)閉Timer 與TimerTask 因?yàn)椴恍枰谘h(huán)檢測(cè)了。 調(diào)用cancel()就可以關(guān)閉,請(qǐng)同學(xué)們閱讀下面這個(gè)例子。 - import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class TimerTaskActivity extends Activity {
/**執(zhí)行Timer進(jìn)度**/
public final static int LOAD_PROGRESS = 0;
/**關(guān)閉Timer進(jìn)度**/
public final static int CLOSE_PROGRESS = 1;
/** 開始TimerTask按鈕 **/
Button mButton0 = null;
/** 關(guān)閉TimerTask按鈕 **/
Button mButton1 = null;
/** 顯示內(nèi)容 **/
TextView mTextView = null;
Context mContext = null;
/**Timer對(duì)象**/
Timer mTimer = null;
/**TimerTask對(duì)象**/
TimerTask mTimerTask = null;
/**記錄TimerID**/
int mTimerID = 0;
/**接收傳遞過來的信息**/
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case LOAD_PROGRESS:
mTextView.setText("當(dāng)前TimerID為" + msg.arg1 );
break;
case CLOSE_PROGRESS:
mTextView.setText("當(dāng)前Timer已經(jīng)關(guān)閉請(qǐng)重新開啟" );
break;
}
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.timer);
mContext = this;
/** 拿到button 與 TextView 對(duì)象 **/
mButton0 = (Button) findViewById(R.id.button0);
mButton1 = (Button) findViewById(R.id.button1);
mTextView = (TextView) findViewById(R.id.textView0);
mTextView.setText("點(diǎn)擊按鈕開始更新時(shí)間");
//開始
mButton0.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
//開始執(zhí)行timer
StartTimer();
}
});
//關(guān)閉
mButton1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
//停止執(zhí)行timer
CloseTimer();
}
});
super.onCreate(savedInstanceState);
}
public void StartTimer() {
if (mTimer == null) {
mTimerTask = new TimerTask() {
public void run() {
//mTimerTask與mTimer執(zhí)行的前提下每過1秒進(jìn)一次這里
mTimerID ++;
Message msg = new Message();
msg.what = LOAD_PROGRESS;
msg.arg1 = (int) (mTimerID);
handler.sendMessage(msg);
}
};
mTimer = new Timer();
//第一個(gè)參數(shù)為執(zhí)行的mTimerTask
//第二個(gè)參數(shù)為延遲的時(shí)間 這里寫1000的意思是mTimerTask將延遲1秒執(zhí)行
//第三個(gè)參數(shù)為多久執(zhí)行一次 這里寫1000表示每1秒執(zhí)行一次mTimerTask的Run方法
mTimer.schedule(mTimerTask, 1000, 1000);
}
}
public void CloseTimer() {
//在這里關(guān)閉mTimer 與 mTimerTask
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
if (mTimerTask != null) {
mTimerTask = null;
}
/**ID重置**/
mTimerID = 0;
//這里發(fā)送一條只帶what空的消息
handler.sendEmptyMessage(CLOSE_PROGRESS);
}
}
復(fù)制代碼 3.AsyncTask執(zhí)行多線程
執(zhí)行AsyncTask
onPreExecute()///首先執(zhí)行這個(gè)方法,它在UI線程中 可以執(zhí)行一些異步操作 比如初始化一些東西
doInBackground(Object... arg0) //異步后臺(tái)執(zhí)行 ,執(zhí)行完畢可以返回出去一個(gè)結(jié)果object對(duì)象
onPostExecute(Object result) //可以拿到執(zhí)行中的進(jìn)度 當(dāng)然進(jìn)度須要在doInBackground中手動(dòng)調(diào)用publishProgress()方法返回
通過例子可以清楚的看到計(jì)算出讀取100張圖片的時(shí)間,執(zhí)行的效率上來說AsyncTask 沒有Thread效率塊,但是AsyncTask 比Thread更規(guī)整,它可是時(shí)時(shí)的拿到異步線程中進(jìn)度以及最后的結(jié)果集,可以讓我們的代碼更佳規(guī)范。這里說一下 Thread能做到的事AsyncTask 都可以做到 但是AsyncTask 能做到的事Thread 不一定能做到就算勉強(qiáng)做到也很麻煩 。我給大家的建議是如果處理大量的異步操作就用AsyncTask 如果少部分的則使用Thread - import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class AsyncTaskActivity extends Activity {
/**執(zhí)行Timer進(jìn)度**/
public final static int LOAD_PROGRESS = 0;
/**關(guān)閉Timer進(jìn)度**/
public final static int CLOSE_PROGRESS = 1;
/** 開始StartAsync按鈕 **/
Button mButton0 = null;
/** 顯示內(nèi)容 **/
TextView mTextView = null;
Context mContext = null;
/**Timer對(duì)象**/
Timer mTimer = null;
/**TimerTask對(duì)象**/
TimerTask mTimerTask = null;
/**記錄TimerID**/
int mTimerID = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.async);
mContext = this;
/** 拿到button 與 TextView 對(duì)象 **/
mButton0 = (Button) findViewById(R.id.button0);
mTextView = (TextView) findViewById(R.id.textView0);
//開始
mButton0.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
//開始執(zhí)行StartAsync
StartAsync();
}
});
super.onCreate(savedInstanceState);
}
public void StartAsync() {
new AsyncTask<Object, Object, Object>() {
@Override
protected void onPreExecute() {
//首先執(zhí)行這個(gè)方法,它在UI線程中 可以執(zhí)行一些異步操作
mTextView.setText("開始加載進(jìn)度");
super.onPreExecute();
}
@Override
protected Object doInBackground(Object... arg0) {
//異步后臺(tái)執(zhí)行 ,執(zhí)行完畢可以返回出去一個(gè)結(jié)果object對(duì)象
//得到開始加載的時(shí)間
Long startTime = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
// 這里循環(huán)加載圖片100遍
ReadBitMap(mContext, R.drawable.bg);
//執(zhí)行這個(gè)方法會(huì)異步調(diào)用onProgressUpdate方法,可以用來更新UI
publishProgress(i);
}
//得到結(jié)束加載的時(shí)間
Long endTime = System.currentTimeMillis();
//將讀取時(shí)間返回
return endTime - startTime;
}
@Override
protected void onPostExecute(Object result) {
//doInBackground之行結(jié)束以后在這里可以接收到返回的結(jié)果對(duì)象
mTextView.setText("讀取100張圖片一共耗時(shí)" + result+ "毫秒");
super.onPostExecute(result);
}
@Override
protected void onProgressUpdate(Object... values) {
//時(shí)時(shí)拿到當(dāng)前的進(jìn)度更新UI
mTextView.setText("當(dāng)前加載進(jìn)度" + values[0]);
super.onProgressUpdate(values);
}
}.execute();//可以理解為執(zhí)行 這個(gè)AsyncTask
}
/**
* 讀取本地資源的圖片
*
* @param context
* @param resId
* @return
*/
public Bitmap ReadBitMap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
// 獲取資源圖片
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
}
復(fù)制代碼 4.多線程Looper的使用
Looper用來管理線程的消息隊(duì)列與循環(huán)隊(duì)列,在handler中默認(rèn)為mainlooper來進(jìn)行消息循環(huán),如果在handler中開啟一個(gè)新的線程那么在這個(gè)新的線程中就沒有Looper循環(huán),如果想讓這個(gè)新的線程具有消息隊(duì)列與消息循環(huán)我們須要調(diào)用 Looper.prepare();拿到它的loop ,這樣就好比在Thread中創(chuàng)建了消息隊(duì)列與循環(huán) 需要調(diào)用 Looper.loop(); 它的意思就是執(zhí)行這個(gè)消息循環(huán),下面我給出一個(gè)例子希望大家好好閱讀。 - import java.io.InputStream;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class LooperActivity extends Activity {
/** 發(fā)送消息按鈕 **/
Button mButton = null;
/** 加載圖片前的時(shí)間 **/
Long mLoadStatr = 0L;
/** 加載圖片后的時(shí)間 **/
Long mLoadEnd = 0L;
Context mContext = null;
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
new Thread() {
@Override
public void run() {
//如果handler不指定looper的話
//默認(rèn)為mainlooper來進(jìn)行消息循環(huán),
//而當(dāng)前是在一個(gè)新的線程中它沒有默認(rèn)的looper
//所以我們須要手動(dòng)調(diào)用prepare()拿到他的loop
//可以理解為在Thread創(chuàng)建Looper的消息隊(duì)列
Looper.prepare();
Toast.makeText(LooperActivity.this, "收到消息",Toast.LENGTH_LONG).show();
//在這里執(zhí)行這個(gè)消息循環(huán)如果沒有這句
//就好比只創(chuàng)建了Looper的消息隊(duì)列而
//沒有執(zhí)行這個(gè)隊(duì)列那么上面Toast的內(nèi)容是不會(huì)顯示出來的
Looper.loop();
//如果沒有 Looper.prepare(); 與 Looper.loop();
//會(huì)拋出異常Cant create handler inside thread that has not called Looper.prepare()
//原因是我們新起的線程中是沒有默認(rèn)的looper所以須要手動(dòng)調(diào)用prepare()拿到他的loop
}
}.start();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.loop);
mContext = this;
/** 拿到button 與 TextView 對(duì)象 **/
mButton = (Button) findViewById(R.id.button0);
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
new Thread() {
@Override
public void run() {
//發(fā)送一條空的消息
//空消息中必需帶一個(gè)what字段
//用于在handler中接收
//這里暫時(shí)我先寫成0
handler.sendEmptyMessage(0);
}
}.start();
}
});
super.onCreate(savedInstanceState);
}
/**
* 讀取本地資源的圖片
*
* @param context
* @param resId
* @return
*/
public Bitmap ReadBitMap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
// 獲取資源圖片
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
}
復(fù)制代碼 老規(guī)矩每篇文章都會(huì)附帶源代碼,最后如果你還是覺得我寫的不夠詳細(xì) 看的不夠爽 不要緊我把源代碼的下載地址貼出來 歡迎大家一起討論學(xué)習(xí)
第十六講多線程的操作方式.rar(375.16 KB, 下載次數(shù): 453)[/I]2011-9-3 01:03 上傳點(diǎn)擊文件名 下載積分: 下載豆 -2 |
上一篇: android讀取excel的數(shù)據(jù)下一篇: Android 軟件開發(fā)之?dāng)?shù)據(jù)的 新建 儲(chǔ)存 讀取 刪除 詳解(十四)
|