展会信息港展会大全

Android 游戏开发之多线程的操作方式
来源:互联网   发布日期:2016-01-13 22:10:50   浏览:2016次  

导读:如果程序主线程被阻塞超过5秒,系统会提示应用程序无响应 这就是ANR 。 ANR的全称是Application Not Responding,使用多线程可以避免ANR。但是这里要注意一下不要为了避免ANR而过多的使用多线程,除非万不得......

如果程序主线程被阻塞超过5秒,系统会提示 应用程序无响应这就是ANR 。 ANR的全称是Application Not Responding,使用多线程可以避免ANR。但是这里要注意一下不要为了避免ANR而过多的使用多线程,除非万不得已的情况。 比如访问网络服务端返回的过慢、数据过多导致滑动屏幕不流畅、或者I/O读取过大的资源等等。这里可以开启一个新线程来处理这些耗时的操作。 如果过多使用多线程会出现数据同步的问题须要程序员去处理,所以使用多线程的时候尽量保持它的独立不会被其它线程干预。java语言提供了一个线程锁的概 念 synchronized 可以添加对象锁与方法锁专门避免多线程同时访问一个方法或者一个对象导致的问题,有兴趣的朋友可以去看看这里我不罗嗦啦 。

1.Thread与Handler执行多线程

Handler主要用于程序主线程与我们自己创建的线程进行通信。在这个例子中点击按钮后创建一个新的线程去循环的加载100张图片每加载完一张图片在 Thread中使用Handler发送消息通知UI线程更新显示,直到加在完毕通知UI显示加载完成一共耗时多少秒。可见Handler的重要性它就是主 线程与我们自己创建的线程的桥梁啊~~~

001

import java.io.InputStream;

002

003

import android.app.Activity;

004

import android.content.Context;

005

import android.graphics.Bitmap;

006

import android.graphics.BitmapFactory;

007

import android.os.Bundle;

008

import android.os.Handler;

009

import android.os.Message;

010

import android.view.View;

011

import android.view.View.OnClickListener;

012

import android.widget.Button;

013

import android.widget.TextView;

014

015

public class SingleActivity extends Activity {

016

017

/**读取进度**/

018

public final static int LOAD_PROGRESS = 0;

019

020

/**标志读取进度结束**/

021

public final static int LOAD_COMPLETE = 1;

022

023

024

/** 开始加载100张图片按钮 **/

025

Button mButton = null;

026

/** 显示内容 **/

027

TextView mTextView = null;

028

/** 加载图片前的时间 **/

029

Long mLoadStatr = 0L;

030

/** 加载图片后的时间 **/

031

Long mLoadEnd = 0L;

032

033

Context mContext = null;

034

035

//接收传递过来的信息

036

Handler handler = new Handler() {

037

@Override

038

public void handleMessage(Message msg) {

039

switch (msg.what) {

040

case LOAD_PROGRESS:

041

mTextView.setText("当前读取到第" + msg.arg1 + "张图片");

042

break;

043

case LOAD_COMPLETE:

044

mTextView.setText("读取结束一共耗时" + msg.arg1 + "毫秒");

045

break;

046

}

047

super.handleMessage(msg);

048

}

049

};

050

051

@Override

052

protected void onCreate(Bundle savedInstanceState) {

053

setContentView(R.layout.single);

054

mContext = this;

055

056

/** 拿到button 与 TextView 对象 **/

057

mButton = (Button) findViewById(R.id.button0);

058

mTextView = (TextView) findViewById(R.id.textView0);

059

mTextView.setText("点击按钮开始更新时间");

060

mButton.setOnClickListener(new OnClickListener() {

061

@Override

062

public void onClick(View arg0) {

063

//开始读取图片

064

LoadImage();

065

}

066

});

067

068

069

super.onCreate(savedInstanceState);

070

}

071

072

public void LoadImage() {

073

new Thread() {

074

@Override

075

public void run() {

076

//得到加载图片开始的时间

077

mLoadStatr = System.currentTimeMillis();

078

079

for (int i = 0; i < 100; i++) {

080

// 这里循环加载图片100遍

081

ReadBitMap(mContext, R.drawable.bg);

082

083

// 每读取完一张图片将进度甩给handler

084

Message msg = new Message();

085

msg.what = LOAD_PROGRESS;

086

msg.arg1 = i + 1;

087

handler.sendMessage(msg);

088

}

089

090

//得到加载图片结束的时间

091

mLoadEnd = System.currentTimeMillis();

092

093

//100张图片加载完成

094

Message msg = new Message();

095

msg.what = LOAD_COMPLETE;

096

msg.arg1 = (int) (mLoadEnd - mLoadStatr);

097

handler.sendMessage(msg);

098

}

099

}.start();

100

101

}

102

103

/**

104

* 读取本地资源的图片

105

*

106

* @param context

107

* @param resId

108

* @return

109

*/

110

public Bitmap ReadBitMap(Context context, int resId) {

111

BitmapFactory.Options opt = new BitmapFactory.Options();

112

opt.inPreferredConfig = Bitmap.Config.RGB_565;

113

opt.inPurgeable = true;

114

opt.inInputShareable = true;

115

// 获取资源图片

116

InputStream is = context.getResources().openRawResource(resId);

117

return BitmapFactory.decodeStream(is, null, opt);

118

}

119

}

2.TimerTask与Handler延迟多线程

Timer与TimerTask可以构建一个延迟器 就好比开启一个线程每隔一段规定的时间访问一次。可以在这个线程中去关闭这个Timer 与TimerTask ,举个例子比如现在我要做一个网游帐号登录超时客户端的检测 用户输入完帐号密码点击登录这时候我开启一个TimerTask每过1秒检查一下用户是否登录成功,过了10秒如果还没有登录成功提示他登陆超时。这个时 候我就须要在这个检测线程中去关闭Timer 与TimerTask 因为不需要它在循环检测了。 调用cancel()就可以关闭,请同学们阅读下面这个例子。

001

import java.util.Timer;

002

import java.util.TimerTask;

003

import android.app.Activity;

004

import android.content.Context;

005

import android.os.Bundle;

006

import android.os.Handler;

007

import android.os.Message;

008

import android.view.View;

009

import android.view.View.OnClickListener;

010

import android.widget.Button;

011

import android.widget.TextView;

012

013

public class TimerTaskActivity extends Activity {

014

015

/**执行Timer进度**/

016

public final static int LOAD_PROGRESS = 0;

017

018

/**关闭Timer进度**/

019

public final static int CLOSE_PROGRESS = 1;

020

021

/** 开始TimerTask按钮 **/

022

Button mButton0 = null;

023

024

/** 关闭TimerTask按钮 **/

025

Button mButton1 = null;

026

027

/** 显示内容 **/

028

TextView mTextView = null;

029

030

Context mContext = null;

031

032

/**Timer对象**/

033

Timer mTimer = null;

034

035

/**TimerTask对象**/

036

TimerTask mTimerTask = null;

037

038

/**记录TimerID**/

039

int mTimerID = 0;

040

041

042

/**接收传递过来的信息**/

043

Handler handler = new Handler() {

044

@Override

045

public void handleMessage(Message msg) {

046

switch (msg.what) {

047

case LOAD_PROGRESS:

048

mTextView.setText("当前TimerID为" + msg.arg1 );

049

break;

050

case CLOSE_PROGRESS:

051

mTextView.setText("当前Timer已经关闭请重新开启" );

052

break;

053

054

}

055

super.handleMessage(msg);

056

}

057

};

058

059

@Override

060

protected void onCreate(Bundle savedInstanceState) {

061

setContentView(R.layout.timer);

062

mContext = this;

063

064

/** 拿到button 与 TextView 对象 **/

065

mButton0 = (Button) findViewById(R.id.button0);

066

mButton1 = (Button) findViewById(R.id.button1);

067

mTextView = (TextView) findViewById(R.id.textView0);

068

mTextView.setText("点击按钮开始更新时间");

069

070

//开始

071

mButton0.setOnClickListener(new OnClickListener() {

072

@Override

073

public void onClick(View arg0) {

074

//开始执行timer

075

StartTimer();

076

}

077

});

078

079

//关闭

080

mButton1.setOnClickListener(new OnClickListener() {

081

@Override

082

public void onClick(View arg0) {

083

//停止执行timer

084

CloseTimer();

085

}

086

});

087

088

super.onCreate(savedInstanceState);

089

}

090

091

public void StartTimer() {

092

093

if (mTimer == null) {

094

mTimerTask = new TimerTask() {

095

public void run() {

096

//mTimerTask与mTimer执行的前提下每过1秒进一次这里

097

mTimerID ++;

098

Message msg = new Message();

099

msg.what = LOAD_PROGRESS;

100

msg.arg1 = (int) (mTimerID);

101

handler.sendMessage(msg);

102

}

103

};

104

mTimer = new Timer();

105

106

//第一个参数为执行的mTimerTask

107

//第二个参数为延迟的时间 这里写1000的意思是mTimerTask将延迟1秒执行

108

//第三个参数为多久执行一次 这里写1000表示每1秒执行一次mTimerTask的Run方法

109

mTimer.schedule(mTimerTask, 1000, 1000);

110

}

111

112

}

113

114

public void CloseTimer() {

115

116

//在这里关闭mTimer 与 mTimerTask

117

if (mTimer != null) {

118

mTimer.cancel();

119

mTimer = null;

120

}

121

if (mTimerTask != null) {

122

mTimerTask = null;

123

}

124

125

/**ID重置**/

126

mTimerID = 0;

127

128

//这里发送一条只带what空的消息

129

handler.sendEmptyMessage(CLOSE_PROGRESS);

130

}

131

}

3.AsyncTask执行多线程

执行AsyncTask

onPreExecute()///首先执行这个方法,它在UI线程中 可以执行一些异步操作 比如初始化一些东西

doInBackground(Objectarg0) //异步后台执行 ,执行完毕可以返回出去一个结果object对象

onPostExecute(Object result) //可以拿到执行中的进度 当然进度须要在doInBackground中手动调用publishProgress()方法返回

通过例子可以清楚的看到计算出读取100张图片的时间,执行的效率上来说AsyncTask 没有Thread效率块,但是AsyncTask 比Thread更规整,它可是时时的拿到异步线程中进度以及最后的结果集,可以让我们的代码更佳规范。这里说一下 Thread能做到的事AsyncTask 都可以做到 但是AsyncTask 能做到的事Thread 不一定能做到就算勉强做到也很麻烦 。我给大家的建议是如果处理大量的异步操作就用AsyncTask 如果少部分的则使用Thread

001

import java.io.InputStream;

002

import java.util.Timer;

003

import java.util.TimerTask;

004

import android.app.Activity;

005

import android.content.Context;

006

import android.graphics.Bitmap;

007

import android.graphics.BitmapFactory;

008

import android.os.AsyncTask;

009

import android.os.Bundle;

010

import android.view.View;

011

import android.view.View.OnClickListener;

012

import android.widget.Button;

013

import android.widget.TextView;

014

015

public class AsyncTaskActivity extends Activity {

016

017

/**执行Timer进度**/

018

public final static int LOAD_PROGRESS = 0;

019

020

/**关闭Timer进度**/

021

public final static int CLOSE_PROGRESS = 1;

022

023

/** 开始StartAsync按钮 **/

024

Button mButton0 = null;

025

026

027

/** 显示内容 **/

028

TextView mTextView = null;

029

030

Context mContext = null;

031

032

/**Timer对象**/

033

Timer mTimer = null;

034

035

/**TimerTask对象**/

036

TimerTask mTimerTask = null;

037

038

/**记录TimerID**/

039

int mTimerID = 0;

040

041

@Override

042

protected void onCreate(Bundle savedInstanceState) {

043

setContentView(R.layout.async);

044

mContext = this;

045

046

/** 拿到button 与 TextView 对象 **/

047

mButton0 = (Button) findViewById(R.id.button0);

048

mTextView = (TextView) findViewById(R.id.textView0);

049

//开始

050

mButton0.setOnClickListener(new OnClickListener() {

051

@Override

052

public void onClick(View arg0) {

053

//开始执行StartAsync

054

StartAsync();

055

}

056

});

057

058

059

super.onCreate(savedInstanceState);

060

}

061

062

public void StartAsync() {

063

new AsyncTask<Object, Object, Object>() {

064

065

@Override

066

protected void onPreExecute() {

067

//首先执行这个方法,它在UI线程中 可以执行一些异步操作

068

mTextView.setText("开始加载进度");

069

super.onPreExecute();

070

}

071

072

@Override

073

protected Object doInBackground(Object... arg0) {

074

//异步后台执行 ,执行完毕可以返回出去一个结果object对象

075

076

//得到开始加载的时间

077

Long startTime = System.currentTimeMillis();

078

for (int i = 0; i < 100; i++) {

079

// 这里循环加载图片100遍

080

ReadBitMap(mContext, R.drawable.bg);

081

//执行这个方法会异步调用onProgressUpdate方法,可以用来更新UI

082

publishProgress(i);

083

}

084

//得到结束加载的时间

085

Long endTime = System.currentTimeMillis();

086

087

//将读取时间返回

088

return endTime - startTime;

089

}

090

091

@Override

092

protected void onPostExecute(Object result) {

093

//doInBackground之行结束以后在这里可以接收到返回的结果对象

094

095

mTextView.setText("读取100张图片一共耗时" + result+ "毫秒");

096

097

super.onPostExecute(result);

098

}

099

100

101

@Override

102

protected void onProgressUpdate(Object... values) {

103

//时时拿到当前的进度更新UI

104

105

mTextView.setText("当前加载进度" + values[0]);

106

super.onProgressUpdate(values);

107

}

108

}.execute();//可以理解为执行 这个AsyncTask

109

110

}

111

/**

112

* 读取本地资源的图片

113

*

114

* @param context

115

* @param resId

116

* @return

117

*/

118

public Bitmap ReadBitMap(Context context, int resId) {

119

BitmapFactory.Options opt = new BitmapFactory.Options();

120

opt.inPreferredConfig = Bitmap.Config.RGB_565;

121

opt.inPurgeable = true;

122

opt.inInputShareable = true;

123

// 获取资源图片

124

InputStream is = context.getResources().openRawResource(resId);

125

return BitmapFactory.decodeStream(is, null, opt);

126

}

127

}

4.多线程Looper的使用

Looper用来管理线程的消息队列与循环队列,在handler中默认为mainlooper来进行消息循环,如果在handler中开启一个新的线程 那么在这个新的线程中就没有Looper循环,如果想让这个新的线程具有消息队列与消息循环我们须要调用 Looper.prepare();拿到它的loop ,这样就好比在Thread中创建了消息队列与循环 需要调用 Looper.loop(); 它的意思就是执行这个消息循环,下面我给出一个例子希望大家好好阅读。

001

import java.io.InputStream;

002

003

import android.app.Activity;

004

import android.content.Context;

005

import android.graphics.Bitmap;

006

import android.graphics.BitmapFactory;

007

import android.os.Bundle;

008

import android.os.Handler;

009

import android.os.Looper;

010

import android.os.Message;

011

import android.view.View;

012

import android.view.View.OnClickListener;

013

import android.widget.Button;

014

import android.widget.Toast;

015

016

public class LooperActivity extends Activity {

017

/** 发送消息按钮 **/

018

Button mButton = null;

019

/** 加载图片前的时间 **/

020

Long mLoadStatr = 0L;

021

/** 加载图片后的时间 **/

022

Long mLoadEnd = 0L;

023

024

Context mContext = null;

025

026

private Handler handler = new Handler() {

027

public void handleMessage(Message msg) {

028

new Thread() {

029

@Override

030

public void run() {

031

032

//如果handler不指定looper的话

033

//默认为mainlooper来进行消息循环,

034

//而当前是在一个新的线程中它没有默认的looper

035

//所以我们须要手动调用prepare()拿到他的loop

036

//可以理解为在Thread创建Looper的消息队列

037

Looper.prepare();

038

039

040

Toast.makeText(LooperActivity.this, "收到消息",Toast.LENGTH_LONG).show();

041

042

043

//在这里执行这个消息循环如果没有这句

044

//就好比只创建了Looper的消息队列而

045

//没有执行这个队列那么上面Toast的内容是不会显示出来的

046

Looper.loop();

047

048

049

050

051

//如果没有Looper.prepare();与 Looper.loop();

052

//会抛出异常Can't create handler inside thread that has not called Looper.prepare()

053

//原因是我们新起的线程中是没有默认的looper所以须要手动调用prepare()拿到他的loop

054

055

}

056

}.start();

057

}

058

};

059

060

@Override

061

protected void onCreate(Bundle savedInstanceState) {

062

setContentView(R.layout.loop);

063

mContext = this;

064

065

/** 拿到button 与 TextView 对象 **/

066

mButton = (Button) findViewById(R.id.button0);

067

mButton.setOnClickListener(new OnClickListener() {

068

@Override

069

public void onClick(View arg0) {

070

new Thread() {

071

@Override

072

public void run() {

073

074

//发送一条空的消息

075

//空消息中必需带一个what字段

076

//用于在handler中接收

077

//这里暂时我先写成0

078

handler.sendEmptyMessage(0);

079

080

}

081

}.start();

082

}

083

});

084

085

086

super.onCreate(savedInstanceState);

087

}

088

/**

089

* 读取本地资源的图片

090

*

091

* @param context

092

* @param resId

093

* @return

094

*/

095

public Bitmap ReadBitMap(Context context, int resId) {

096

BitmapFactory.Options opt = new BitmapFactory.Options();

097

opt.inPreferredConfig = Bitmap.Config.RGB_565;

098

opt.inPurgeable = true;

099

opt.inInputShareable = true;

100

// 获取资源图片

101

InputStream is = context.getResources().openRawResource(resId);

102

return BitmapFactory.decodeStream(is, null, opt);

103

}

104

}

赞助本站

人工智能实验室
AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港