乐读文学

Android从入门到精通

乐读文学 > 科普学习 > Android从入门到精通

第134页

书籍名:《Android从入门到精通》    作者:明日科技




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对象产生的异常信息