Android研究之游戏开发多线程详解



游戏开发与软件开发多线程的重要性

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

1.Thread与Handler执行多线程

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

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
{

/**读取进度**/

public
final static
int LOAD_PROGRESS
= 0;

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

public
final static
int LOAD_COMPLETE
= 1;

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

Button mButton
= null;

/** 显示内容 **/

TextView mTextView
= null;

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

Long
mLoadStatr =
0L;

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

Long
mLoadEnd =
0L;

Context mContext
= null;

//接收传递过来的信息

Handler
handler =
new Handler()
{

@Override

public
void handleMessage(Message
msg)
{

switch
(msg.what)
{

case
LOAD_PROGRESS:

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

break;

case
LOAD_COMPLETE:

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

break;

}

super.handleMessage(msg);

}

};

@Override

protected
void onCreate(Bundle
savedInstanceState)
{

setContentView(R.layout.single);

mContext
= this;

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

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

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

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

mButton.setOnClickListener(new
OnClickListener()
{

@Override

public
void onClick(View
arg0)
{

//开始读取图片

LoadImage();

}

});

super.onCreate(savedInstanceState);

}

public
void LoadImage()
{

new
Thread()
{

@Override

public
void run()
{

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

mLoadStatr
= System.currentTimeMillis();

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

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

ReadBitMap(mContext,
R.drawable.bg);

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

Message msg
= new
Message();

msg.what
= LOAD_PROGRESS;

msg.arg1
= i
+ 1;

handler.sendMessage(msg);

}

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

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);

}

}

2.TimerTask与Handler延迟多线程

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

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
{

/**执行Timer进度**/

public
final static
int LOAD_PROGRESS
= 0;

/**关闭Timer进度**/

public
final static
int CLOSE_PROGRESS
= 1;

/** 开始TimerTask按钮 **/

Button mButton0
= null;

/** 关闭TimerTask按钮 **/

Button
mButton1 =
null;

/** 显示内容 **/

TextView mTextView
= null;

Context mContext
= null;

/**Timer对象**/

Timer
mTimer =
null;

/**TimerTask对象**/

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("当前TimerID为"
+ msg.arg1
);

break;

case
CLOSE_PROGRESS:

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

break;

}

super.handleMessage(msg);

}

};

@Override

protected
void onCreate(Bundle
savedInstanceState)
{

setContentView(R.layout.timer);

mContext
= this;

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

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

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

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

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

//开始

mButton0.setOnClickListener(new
OnClickListener()
{

@Override

public
void onClick(View
arg0)
{

//开始执行timer

StartTimer();

}

});

//关闭

mButton1.setOnClickListener(new
OnClickListener()
{

@Override

public
void onClick(View
arg0)
{

//停止执行timer

CloseTimer();

}

});

super.onCreate(savedInstanceState);

}

public
void StartTimer()
{

if
(mTimer
== null)
{

mTimerTask
= new
TimerTask()
{

public
void run()
{

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

mTimerID
++;

Message
msg =
new Message();

msg.what
= LOAD_PROGRESS;

msg.arg1
= (int)
(mTimerID);

handler.sendMessage(msg);

}

};

mTimer
= new
Timer();

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

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

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

mTimer.schedule(mTimerTask,
1000,
1000);

}

}

public
void CloseTimer()
{

//在这里关闭mTimer 与 mTimerTask

if
(mTimer
!= null)
{

mTimer.cancel();

mTimer
= null;

}

if
(mTimerTask
!= null)
{

mTimerTask
= null;

}

/**ID重置**/

mTimerID
= 0;

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

handler.sendEmptyMessage(CLOSE_PROGRESS);

}

}

3.AsyncTask执行多线程

执行AsyncTask

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

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

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
{

/**执行Timer进度**/

public
final static
int LOAD_PROGRESS
= 0;

/**关闭Timer进度**/

public
final static
int CLOSE_PROGRESS
= 1;

/** 开始StartAsync按钮 **/

Button mButton0
= null;

/** 显示内容 **/

TextView
mTextView =
null;

Context
mContext =
null;

/**Timer对象**/

Timer mTimer
= null;

/**TimerTask对象**/

TimerTask
mTimerTask =
null;

/**记录TimerID**/

int
mTimerID =
0;

@Override

protected
void onCreate(Bundle
savedInstanceState)
{

setContentView(R.layout.async);

mContext
= this;

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

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

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

//开始

mButton0.setOnClickListener(new
OnClickListener()
{

@Override

public
void onClick(View
arg0)
{

//开始执行StartAsync

StartAsync();

}

});

super.onCreate(savedInstanceState);

}

public
void StartAsync()
{

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

@Override

protected
void onPreExecute()
{

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

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

super.onPreExecute();

}

@Override

protected
Object doInBackground(Object...
arg0)
{

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

//得到开始加载的时间

Long
startTime =
System.currentTimeMillis();

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

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

ReadBitMap(mContext,
R.drawable.bg);

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

publishProgress(i);

}

//得到结束加载的时间

Long
endTime =
System.currentTimeMillis();

//将读取时间返回

return
endTime -
startTime;

}

@Override

protected
void onPostExecute(Object
result)
{

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

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

super.onPostExecute(result);

}

@Override

protected
void onProgressUpdate(Object...
values)
{

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

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

super.onProgressUpdate(values);

}

}.execute();//可以理解为执行
这个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);

}

}

4.多线程Looper的使用

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

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
{

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

Button mButton
= null;

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

Long
mLoadStatr =
0L;

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

Long
mLoadEnd =
0L;

Context mContext
= null;

private
Handler handler
= new
Handler()
{

public
void handleMessage(Message
msg)
{

new
Thread()
{

@Override

public
void run()
{

//如果handler不指定looper的话

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

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

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

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

Looper.prepare();

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

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

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

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

Looper.loop();

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

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

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

}

}.start();

}

};

@Override

protected
void onCreate(Bundle
savedInstanceState)
{

setContentView(R.layout.loop);

mContext
= this;

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

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

mButton.setOnClickListener(new
OnClickListener()
{

@Override

public
void onClick(View
arg0)
{

new
Thread()
{

@Override

public
void run()
{

//发送一条空的消息

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

//用于在handler中接收

//这里暂时我先写成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);

}

}

老规矩每篇文章都会附带源代码,最后如果你还是觉得我写的不够详细 看的不够爽 不要紧我把源代码的下载地址贴出来 欢迎大家一起讨论学习一起进步。

源码下载:Android_multineline

Android研究之游戏开发多线程详解

时间: 2024-10-17 19:34:32

Android研究之游戏开发多线程详解的相关文章

Unity3D游戏开发之详解 Animation类和Animator类

Unity3D游戏开发之详解 Animation类和Animator类 Animation类 animation组件用于播放动画.可以指定动画剪辑到动画组件并从脚本控制动画播放.在Unity的动画系统基于权重并且支持动画融合,叠加动画,动画混合,标签和完全控制动画播放的各个方面. 如果想播放一个简单的动画,可以使用Animation.Play:如果想在动画之间交叉淡入,可以使用Animation.CrossFade:如果想改变动画模式(循环,一次,乒乓),可以改变动画导入设置里面的动画帧的Wra

Android研究之游戏开发摄像头更新

 游戏中摄像头的原理介绍        在游戏开发中更新摄像头的位置可以决定屏幕显示的内容,尤其是RPG类游戏摄像头有着非常重要的作用,我举一个例子 有时候我们在玩RPG游戏的时候进入一个新的场景 触发一段脚本后 发现镜头开始向上移动 根据镜头移动玩家可以大概浏览一下这个场景有什么东西 ,触发什么样的剧情.这个实现的方式就是游戏摄像头原理.上章学习了Android游戏开发地图编辑器有需要的可以看下. 如图所示:首先摄像头显示的区域也是手机屏幕显示的区域 如果需要更改摄像头的位置  其实是更改

Android研究之游戏开发碰撞检测

 游戏碰撞的大致可以分为这几种类 1.主角与边界的碰撞,限制主角不能走出手机屏幕外. 2.主角与物理层的碰撞,与地图中的房子 桌子 椅子等等. 3.主角与游戏人物之间的碰撞,这里指NPC等. 4.主角与脚本框发生的碰撞,例如走进房间出线一段剧情对话等等. 由此可见游戏中的碰撞主要是可以分为 1.点与矩形之间的碰撞 2.矩形与矩形之间的碰撞 3.圆形与圆形之间的碰撞 4.圆形与矩形之间的碰撞          今天我主要介绍一下以上中最特殊的一个碰撞方式 主角与物理层之间的碰撞. 如图所示:每

Android研究之游戏开发处理按键的响应

1.onKeyDown 方法 onKeyDown 方法是KeyEvent.Callback 接口中的一个抽象方法,重写onKeyDown 方法可以监听到按键被按下的事件,我们先看看onKeyDown方法的函数原型. 第一个参数为键值,手机中每一个按钮都拥有一个完全独立的键值 通过按键键值就可以确定当前按下的是那一个按键. 第二个参数为按键事件,  该对象中保存着当前按键的所有信息 比如 按键发生的时间 按键发生的次数  按键发生的类型等等. 通过以上两个参数就可以拿到当前按键事件的所附带的一切信

Android研究之游戏开发帧动画实现

 1.帧动画的原理        帧动画帧动画顾名思义,一帧一帧播放的动画就是帧动画. 帧动画和我们小时候看的动画片的原理是一样的,在相同区域快速切换图片给人们呈现一种视觉的假象感觉像是在播放动画,其实不过是N张图片在一帧一帧的切换罢了.对摄像头不清楚的请看Android研究之游戏开发摄像头更新        如图所示:人物行走动画的实现方式, 4帧行走动画在播放区域 一帧一帧向左切换播放 给人们一种播放动画的假象 ,图片就动了起来, 很简单吧,其它三方向播放动画的方法类似我就不再一一举例.

Android研究之游戏开发主角与地图的滚动

 人物移动地图的平滑滚动处理         玩过rpg游戏的朋友应该都知道RPG的游戏地图一般都比较大 今天我和大家分享一下在RPG游戏中如何来处理超出手机屏幕大小的游戏地图. 如图所示为程序效果动画图 . 地图滚动的原理        在本人之前博客的文章中介绍过人物在屏幕中的移动方式,因为之前拼的游戏地图是完全填充整个手机屏幕的,所以无需处理地图的平滑滚动.这篇文章我着重的向大家介绍一下控制人物移动后地图滚动的处理方式.举个例子 如上图所示 比如人物向右移动,如果地图贴在屏幕左边边界 

Android+3D游戏开发技术详解与典型案例

 内容导读 本书共分两篇,第一篇介绍了Android 3D游戏开发的基础知识,主要对OpenGL ES的相关内容进行了介绍. 章 名主 要 内 容 第1章 英雄还看今朝-Android简介本章介绍了市场上主流的手机平台,同时也分析了未来手机平台的发展趋势及Android平台的前景 第2章 数风流人物-当前流行游戏类型简介本章以分类的方式简要地介绍了当前流行的游戏的玩法,游戏的视觉效果,游戏的设计及<仙剑>等著名游戏的历史 第3章 不积跬步,无以至千里-游戏开发基础知识本章初步介绍了游戏开发的基

Canvas前端游戏开发——FlappyBird详解

一直想自己做点小东西,直到最近看了本<HTML5游戏开发>,才了解游戏开发中的一点点入门知识. 本篇就针对学习的几个样例,自己动手实践,做了个FlappyBird,源码共享在度盘;也可以参考github,里面有更多的游戏样例. (可点击底部[阅读原文]下载源码) 游戏截图 HTML5之Canvas Canvas是Html5中用于绘图的元素,它可以绘制各种图形,比如长方形,多边形,圆形等等.如果想要了解Canvas的使用可以参考: http://www.w3school.com.cn/tags/

iOS 开发 多线程详解

常用的多线程开发有三种方式: 1.NSThread 2.NSOperation 3.GCD 线程状态分为isExecuting(正在执行).isFinished(已经完成).isCancellled(已经取消)三种.其中取消状态程序可以干预设置,只要调用线程的cancel方法即可.但是需要注意在主线程中仅仅能设置线程状态,并不能真正停止当前线程,如果要终止线程必须在线程中调用exist方法,这是一个静态方法,调用该方法可以退出当前线程. NSThread NSThread是轻量级的多线程开发,使