第134页
if (thread != null) {
thread.interrupt(); //中断线程
thread = null;
}
super.onDestroy();
}
运行本实例,在屏幕上将显示一个“开始”按钮和一个“停止”按钮,单击“开始”按钮,将在日志面板中输出循环变量的值;单击“停止”按钮,将中断线程。日志面板的显示结果如图12.1所示。
图12.1 在日志面板中输出的内容
12.1.6 范例2:开启一个新线程播放背景音乐
例12.2 在Eclipse中创建Android项目,名称为12.2,开启一个新线程播放背景音乐,在音乐文件播放完毕后,暂停5秒钟后重新开始播放。(实例位置:光盘\TM\sl\12\12.2)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的TextView组件删除,然后在默认添加的线性布局管理器中添加一个“开始”按钮,用于开启线程并播放背景音乐,具体代码请参见光盘。
(2)在该MainActivity中,创建两个成员变量,具体代码如下:
private Thread thread; //声明一个线程对象
private static MediaPlayer mp = null; //声明一个MediaPlayer对象
(3)在onCreate()方法中,获取布局管理器中添加的“开始”按钮,并为该按钮添加单击事件监听器,在重写的onCreate()方法中,首先设置该按钮不可用,然后创建一个用于播放背景音乐的线程,并开启该线程,在重写的run()方法中,调用playBGSound()方法播放背景音乐,具体代码如下:
Button button = (Button) findViewById(R.id.button1); //获取布局管理器中添加的“开始”按钮
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
((Button) v).setEnabled(false); //设置按钮不可用
//创建一个用于播放背景音乐的线程
thread = new Thread(new Runnable() {
@Override
public void run() {
playBGSound(); //播放背景音乐
}
});
thread.start(); //开启线程
}
});
(4)编写playBGSound()方法,首先判断MediaPlayer对象是否为空,如果不为空,则释放该对象,然后创建一个用于播放背景音乐的MediaPlayer对象,并开始播放,再为该MediaPlayer对象添加播放完成事件监听器,在重写的onCompletion()方法中,让线程休眠5秒钟,并调用playBGSound()方法重新播放音乐,具体代码如下:
private void playBGSound() {
if (mp != null) {
mp.release(); //释放资源
}
mp = MediaPlayer.create(MainActivity.this, R.raw.jasmine);
mp.start(); //开始播放
//为MediaPlayer添加播放完成事件监听器
mp.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
try {
Thread.sleep(5000); //线程休眠5秒钟
playBGSound(); //重新播放音乐
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
(5)重写MainActivity的onDestroy()方法,停止播放背景音乐并释放资源,具体代码如下:
@Override
protected void onDestroy() {
if (mp != null) {
mp.stop(); //停止播放
mp.release(); //释放资源
mp = null;
}
if (thread != null) {
thread = null;
}
super.onDestroy();
}
运行本实例,在屏幕上将显示一个“开始”按钮,单击该按钮,该按钮将变为不可用状态,并且开始播放背景音乐,如图12.2所示。
图12.2 程序运行效果
12.2 Handler消息传递机制
教学录像:光盘\TM\lx\12\Handler消息传递机制.exe
在12.1节中,已经介绍了在Android中如何创建、开启、休眠和中断线程。不过,此时并没有在新创建的子线程中对UI界面上的内容进行操作,如果应用前面介绍的方法对UI界面进行操作,将抛出异常。例如,在子线程的run()方法中循环修改文本框的显示文本,将抛出如图12.3所示的异常信息。
图12.3 抛出的异常信息
为此,Android中引入了Handler消息传递机制,来实现在新创建的线程中操作UI界面。下面将对Handler消息传递机制进行介绍。
12.2.1 循环者(Looper)简介
在介绍Looper之前,需要先来了解一下MessageQueue的概念。在Android中,一个线程对应一个Looper对象,而一个Looper对象又对应一个MessageQueue(消息队列)。MessageQueue用于存放Message(消息),在MessageQueue中,存放的消息按照FIFO(先进先出)原则执行,由于MessageQueue被封装到Looper里面,所以这里不对MessageQueue进行过多介绍。
Looper对象用来为一个线程开启一个消息循环,从而操作MessageQueue。默认情况下,Android中新创建的线程是没有开启消息循环的,但是主线程除外。系统自动为主线程创建Looper对象,开启消息循环。所以,当在主线程中应用下面的代码创建Handler对象时不会出错,而如果在新创建的非主线程中应用下面的代码创建Handler对象,将产生如图12.4所示的异常信息。
Handler handler2 = new Handler();
如果想要在非主线程中创建Handler对象,首先需要使用Looper类的prepare()方法来初始化一个Looper对象,然后创建该Handler对象,再使用Looper类的loop()方法启动Looper,从消息队列中获取和处理消息。
图12.4 在非主线程中创建Handler对象产生的异常信息