Android 服务与多线程——编写简单的音乐播放器程序

Android 服务与多线程——编写简单的音乐播放器程序

一、实验目的

1)       学会使用MediaPlayer;

2)       学会简单的多线程编程,使用Handler更新UI;

3)       学会使用Service进行后台工作;

4)       学会使用Service与Activity进行通信。

二、实验要求

1)     实现音乐文件的播放控制(仅需要播放,暂停和停止)

2)     利用Handler更新播放进度

3)     利用Service开启(停止)后台服务进行后台播放

 

三、使用环境

Eclipse,Android 2.3

 

四、调试过程、代码解析及运行截图

1.向sdcard中添加音乐:

1)启动模拟器,打开DDMS视图;

2)选择FileExplorer标签页;

3)选择sdcard目录后点击右上角的push按钮即可。如下图:


 

2.创建MediaPlayer的对象,并利用控制条更新播放进度:

1)创建MediaPlayer及Handler:

  1. MediaPlayermPlayer = new MediaPlayer();  
  2.   
  3. Handler handler = new Handler();   
  4. RunnableupdateThread = new Runnable(){   
  5. public void run() {   
  6.     //获得歌曲现在播放位置并设置成播放进度条的值   
  7.     seekbar.setProgress(mPlayer.getCurrentPosition());   
  8.     //每次延迟100毫秒再启动线程   
  9.     handler.postDelayed(updateThread, 100);   
  10. }   



2)初始化音乐:
  1.       try {  
  2.           mPlayer.setDataSource("/sdcard/test.mp3"); //选择资源  
  3.            mPlayer.prepare();                           //准备就绪  
  4.            text.setText("初始化歌曲…");  
  5.        } catch (IOException e){  
  6.            text.setText("初始异常");  
  7.            e.printStackTrace();  
  8.   
  9.        //获取音乐的总长度以设置进度条的最大长度  
  10.        seekbar.setMax(mPlayer.getDuration());     
  11. mPlayer.setOnCompletionListener(complete);  



3)对开始/暂停键事件触发:
  1.        privateImageButton.OnClickListenerbtn_play = new         ImageButton.OnClickListener(){  
  2.    
  3.        @Override  
  4.        public void onClick(View arg0) {  
  5.        //TODOAuto-generated method stub  
  6.        try {   
  7.                if(mPlayer !=null)  {   
  8.                if(mPlayer.isPlaying()) {     //判断是否正在播放  
  9.                     mPlayer.pause();          //暂停播放器  
  10.                     handler.post(updateThread); //使用handler的post方法用于更新  
  11.                        bn_play.setImageDrawable(getResources().getDrawable(R.drawable.play));  
  12.                      text.setText("已暂停");   //更新图标  
  13.                    }   
  14.                else if(!mPlayer.isPlaying())  {   
  15.                     mPlayer.start();         //继续播放音乐  
  16.                     handler.post(updateThread);  
  17.                     bn_play.setImageDrawable(getResources().getDrawable(R.drawable.pause));  
  18.                      text.setText("播放中");   
  19.                    }   
  20.                }   
  21.               } catch (Exception e) {   
  22.                      text.setText("播放/暂停异常");   
  23.                      e.printStackTrace();   
  24.               }  
  25.        }  
  26.          
  27. };  




4)对停止键的事件触发:

  1. private ImageButton.OnClickListenerbtn_stop = new ImageButton.OnClickListener(){  
  2.    
  3.        @Override  
  4.        public void onClick(View arg0) {  
  5.            // TODO Auto-generated methodstub  
  6.            try{  
  7.               if(mPlayer.isPlaying()){  
  8.                   text.setText("已停止");  //更新文字提示  
  9.               }  
  10.               else {  
  11.                   text.setText("初始化歌曲…");  
  12.               }  
  13.               mPlayer.stop();              //停止播放音乐  
  14.               handler.removeCallbacks(updateThread);  
  15.               bn_play.setImageDrawable(getResources().getDrawable(R.drawable.play));  
  16.               mPlayer.reset();             //恢复至初始状态  
  17.                mPlayer.setDataSource("/sdcard/test.mp3");  
  18.               mPlayer.prepare();  
  19.            } catch (Exception e){  
  20.               text.setText("停止异常");  
  21.               e.printStackTrace();  
  22.            }  
  23.        }  
  24. };  




 

5)退出的事件触发

  1.    private Button.OnClickListenerbtn_exit = new Button.OnClickListener(){  
  2.    
  3.        @Override  
  4.        public void onClick(View arg0) {  
  5.            // TODO Auto-generated methodstub  
  6.             onDestroy();                    
  7.        }  
  8. };  
  9.    
  10.     @Override  
  11.     protected void onDestroy(){  
  12.         mPlayer.release();  
  13.         super.onDestroy();  
  14.         System.exit(0);   //完全退出系统  
  15. };  



6)歌曲播放结束的事件:
  1. privateMediaPlayer.OnCompletionListenercomplete = new    MediaPlayer.OnCompletionListener(){  
  2.     @Override  
  3.     public void onCompletion(MediaPlayerarg0) {  
  4.             try {  
  5.                 handler.removeCallbacks(updateThread);//移除对线程的调用  
  6.                 bn_play.setImageDrawable(getResources().getDrawable(R.drawable.play));  
  7.                 mPlayer.reset(); nbsp;                      //恢复到初始状态  
  8.                mPlayer.setDataSource("/sdcard/test.mp3");  
  9.                 mPlayer.prepare();  
  10.                 text.setText("播放结束!");  
  11.             } catch (IOException e){  
  12.                 text.setText("完成异常");  
  13.                 e.printStackTrace();  
  14.             }                
  15.     }  

7)对进度条的操作:

  1. privateSeekBar.OnSeekBarChangeListenerseekb = new SeekBar.OnSeekBarChangeListener(){  
  2.     @Override   
  3.     public void onProgressChanged(SeekBarseekBar, int progress,boolean fromUser) {   
  4.      // fromUser判断是用户改变的滑块的值   
  5.         if(fromUser==true){   
  6.             mPlayer.seekTo(progress);  //转到相应的进程中  
  7.         }   
  8.     }   
  9.   
  10.     @Override   
  11.     public voidonStartTrackingTouch(SeekBar seekBar) {   
  12.         //TODOAuto-generated methodstub   
  13.     }   
  14.     @Override   
  15.   
  16.     public void onStopTrackingTouch(SeekBarseekBar) {   
  17.         //TODOAuto-generated method stub         
  18.     }   
  19. };  



8)添加时间进度显示:

A.定义两个变量,一为现在正播放的时间:playtime;二为总时常alltime:

  1. playtime =(TextView)findViewById(R.id.progress);  
  2. alltime =(TextView)findViewById(R.id.alltime);  



B.在播放的设置中添加获取音乐总时长的数据:

  1. else if(!mPlayer.isPlaying())  {   
  2.      mPlayer.start();  
  3.      int Alltime=mPlayer.getDuration();   
  4.      alltime.setText(ShowTime(Alltime));  
  5.      handler.post(updateThread);  
  6.                bn_play.setImageDrawable(getResources().getDrawable(R.drawable.pause));  
  7.               text.setText("播放中");   

 

C.在Runnable里获取正在运行的时间:

  1.    Runnable updateThread = new Runnable(){   
  2.    public void run() {   
  3.         //获得歌曲现在播放位置并设置成播放进度条的值   
  4.         //将播放的时间调到正在播放的时间  
  5.   
  6.         int CurrentPosition=mPlayer.getCurrentPosition();   
  7.         playtime.setText(ShowTime(CurrentPosition));  
  8.        seekbar.setProgress(CurrentPosition);   
  9.   
  10.         //每次延迟100毫秒再启动线程   
  11.         handler.postDelayed(updateThread, 100);   
  12.     }   
  13. };  


 

D.显示时间的函数

  1. public String ShowTime(int time){   
  2.   time/=1000;   
  3.   int minute=time/60;   
  4.   int hour=minute/60;   
  5.   int second=time%60;   
  6.   minute%=60;   
  7.   return String.format("%02d:%02d", minute, second);   



3.利用Service开启(停止)后台服务进行后台播放

   Android中的服务,它与Activity不同,它是不能与用户交互的,不能自己启动的,运行在后台的程序,如果我们退出应用时,Service进程并没有结束,它仍然在后台运行。使用Service来管理音乐的后台播放

1) 创建Service子类,并在Manifest.xml中进行注册

<service android:name=".MyService"/>

 2) 创建接口MyBinder,负责Activity与Service之间进行沟通,帮助Activity调用Service里的方法

  1. package com.yzh;  
  2.   
  3. import android.app.Service;  
  4. import android.os.Binder;  
  5.   
  6. public interface MyBinder  
  7. {  
  8.     public Service getService();  
  9.     //负责Activity与Service之间进行沟通,帮助   Activity调用Service里的方法  



3) 将之前写的MediaPlayer相关的代码全部移动到Service类中

A. OnCreate()

  1.      @Override  
  2.      publicvoid onCreate() {   //初始化MediaPlayer  
  3.          // TODO Auto-generated method stub  
  4.          super.onCreate();  
  5.          mPlayer =new MediaPlayer();  
  6.      
  7.          try {  
  8.              mPlayer.setDataSource("/sdcard/test.mp3");  
  9.              mPlayer.prepare();  
  10.          } catch (IOException e) {  
  11.              //TODO Auto-generated catch block  
  12.              e.printStackTrace();  
  13.          }  
  14. }  



B. OnDestroy()
  1. @Override  
  2. publicvoid onDestroy() {  
  3.     // TODO Auto-generated methodstub  
  4.     super.onDestroy();  
  5.     mPlayer.release();  
  6. }  



C. startorpauseMusic()
  1. publicboolean startorpauseMusic()  
  2. {  
  3.         boolean playing =false;  
  4.         if(!mPlayer.isPlaying()){  
  5.            playing = false;  
  6.            mPlayer.start();  
  7.         }  
  8.         else{  
  9.            playing = true;  
  10.            mPlayer.pause();  
  11.        }  
  12.         
  13.        return playing;  
  14. }  



D. stopMusic()
  1. public void stopMusic()  
  2.   
  3.   mPlayer.seekTo(0);  
  4.       mPlayer.stop();  
  5.       try {  
  6.           mPlayer.prepare();  
  7.       } catch (IllegalStateException e) {  
  8.           // TODO Auto-generated catch block  
  9.    e.printStackTrace();  
  10.       } catch (IOException e) {  
  11.           // TODO Auto-generated catch block  
  12.           e.printStackTrace();  
  13.       }  
  14.    }  



4) 通过Binder来保持Activity与Service的通信
  1.     private IBinder binder = new MyBinderImpl();  
  2.        @Override  
  3.     public IBinder onBind(Intentarg0) {  
  4.            // TODO Auto-generated method stub  
  5.            return binder;  
  6. }  
  7.    
  8. //在主Activity退出时必须解除绑定,否则会抛出异常  
  9.         @Override  
  10.         public boolean onUnbind(Intent intent) {  
  11.         // TODO Auto-generated methodstub  
  12.            return super.onUnbind(intent);  
  13. }  
  14.    
  15.         public class MyBinderImpl  extends Binderimplements MyBinder  
  16.         {  
  17.            @Override  
  18.           public Service getService() {  
  19.            // TODO Auto-generated methodstub  
  20.                 return MyService.this;  
  21.           }  
  22.        }  

5) 在主Activity中调用 bindService 保持与Service的通信:

  1. ServiceConnection sc = new ServiceConnection(){  
  2.    
  3.        @Override  
  4.        public void onServiceConnected(ComponentName arg0, IBinder binder) {  
  5.           // TODO Auto-generated method stub  
  6.           MyBinder myBinder = (MyBinder)binder;  
  7.           service = (MyService)myBinder.getService();  
  8.           seekbar.setMax(service.getDuration());  
  9.           int length = service.getDuration();  
  10.           int secLength = length/1000+1;  
  11.           service.onComplete(handler);  
  12.        }  
  13.    
  14.        @Override  
  15.        public void onServiceDisconnected(ComponentName arg0) {  
  16.           // TODO Auto-generated method stub  
  17.            
  18.        }  
  19.         
  20. };  



6) 在Activity中将所有的MediaPlayer的方法都替换为Service里的函数。

 

7) 在Service中还需要定义的方法:

  1. //获取音乐的长度  
  2. lic int getDuration()  
  3. {  
  4.     return mPlayer.getDuration();  
  5. }  
  6.   
  7. //获取音乐的播放位置  
  8. public int getPosition()  
  9. {  
  10.     return mPlayer.getCurrentPosition();  
  11. }  
  12.   
  13. //跳转至音乐播放的进程  
  14. public void seekto(int progress){  
  15.     mPlayer.seekTo(progress);  
  16. }  

4.实验截图

图1 初始化状态

 

图2 点击播放按钮:播放音乐

显示总时长,以及现在播放的时间,进度条实时更新

 

图3 点击暂停按钮,暂停播放

显示“已暂停”,停止更新

 

图4 点击停止按钮,停止播放

当再次点击播放按钮时,音乐从头开始

 

图5 当音乐播放完后,显示“播放结束!”

 

图6 关掉运行界面后,

播放器在后台运行仍然运行

 

图7 项目的文件结构

 

五、遇到的困难和解决方法

在使用Service作后台处理的时候,忽略了音乐播放结束后的事件触发,无法显示“播放结束”,原来判断音乐播放结束是MediaPlayer的一个方法,无法在Activity里进行,必须要在Service里定义,于是采用使用Handler来传参的方法。具体解决步骤如下:

A. 在Service中定义OnComplete方法:

  1. public void onComplete(final Handler handler)  
  2.     {  
  3.        MediaPlayer.OnCompletionListener complete = newMediaPlayer.OnCompletionListener() {  
  4.             
  5.            @Override  
  6.            public void onCompletion(MediaPlayer arg0) {  
  7.               // TODO Auto-generated method stub  
  8.               Message msg = new Message(); //获得消息  
  9.               msg.arg1 = 0;  
  10.               handler.sendMessage(msg);     //传递给handler  
  11.            }  
  12.        };  
  13.        mPlayer.setOnCompletionListener(complete);  
  14.     }  

B. 在Activity中获取消息:

  1. Handler handler = new Handler(){  
  2.    
  3.      @Override  
  4.       public void handleMessage(Messagemsg) {  
  5.          // TODO Auto-generated method stub  
  6.          super.handleMessage(msg);  
  7.          if(msg.arg1 == 0)  
  8.          {  
  9.             handler.removeCallbacks(updateThread);//移除对线程的调用  
  10.                 bn_play.setImageDrawable(getResources().getDrawable(R.drawable.play));  
  11.               text.setText("播放结束");  
  12.          }  
  13.      }  
  14. };  



六、附录——main.xml

  1. <?xml version="1.0"encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical">  
  6.    
  7.     <LinearLayout  
  8.         android:id="@+id/linearLayout7"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:orientation="vertical">  
  12.    
  13.         <TextView  
  14.             android:id="@+id/textView2"  
  15.             android:layout_width="wrap_content"  
  16.             android:layout_height="wrap_content"  
  17.             android:textSize="25sp"  
  18.             android:layout_gravity="center_horizontal"  
  19.             android:textColor="#0489B1"  
  20.         android:text="My MusicPlayer" />  
  21.    
  22.     </LinearLayout>  
  23.    
  24.     <LinearLayout  
  25.         android:id="@+id/linearLayout4"  
  26.         android:layout_width="match_parent"  
  27.         android:layout_height="wrap_content"  
  28.         android:gravity="center">  
  29.    
  30.         <LinearLayout  
  31.             android:id="@+id/linearLayout5"  
  32.             android:layout_width="wrap_content"  
  33.             android:layout_height="match_parent"  
  34.             android:orientation="vertical">  
  35.    
  36.             <TextView  
  37.                 android:id="@+id/text"  
  38.                 android:layout_width="fill_parent"  
  39.                 android:layout_height="wrap_content"  
  40.                 android:textSize="20dp"  
  41.                 android:text="Enjoy yourMusic: " />  
  42.    
  43.         </LinearLayout>  
  44.    
  45.         <LinearLayout  
  46.             android:id="@+id/linearLayout6"  
  47.             android:layout_width="wrap_content"  
  48.             android:layout_height="match_parent">  
  49.    
  50.             <TextView  
  51.                 android:id="@+id/textView1"  
  52.                 android:layout_width="wrap_content"  
  53.                 android:layout_height="wrap_content"  
  54.                 android:textSize="20dp"  
  55.                 android:text="test.mp3"/>  
  56.    
  57.         </LinearLayout>  
  58.    
  59.     </LinearLayout>  
  60.    
  61.     <LinearLayout  
  62.         android:id="@+id/linearLayout9"  
  63.         android:layout_width="match_parent"  
  64.         android:layout_height="wrap_content"  
  65.         android:gravity="center">  
  66.    
  67.         <TextView  
  68.             android:id="@+id/progress"  
  69.             android:layout_width="wrap_content"  
  70.             android:layout_height="wrap_content"  
  71.             android:text="00:00"  
  72.             android:textColor="#FFBF00"  
  73.             android:textSize="18dp"/>  
  74.    
  75.         <TextView  
  76.             android:id="@+id/textView3"  
  77.             android:layout_width="wrap_content"  
  78.             android:layout_height="wrap_content"  
  79.             android:textColor="#FFBF00"  
  80.             android:textSize="18dp"  
  81.             android:text=" / "/>  
  82.    
  83.         <TextView  
  84.             android:id="@+id/alltime"  
  85.             android:layout_width="wrap_content"  
  86.             android:layout_height="wrap_content"  
  87.             android:textColor="#FFBF00"  
  88.             android:textSize="18dp"  
  89.             android:text="00:00"/>  
  90.    
  91.     </LinearLayout>  
  92.    
  93.     <LinearLayout  
  94.         android:id="@+id/linearLayout8"  
  95.         android:layout_width="match_parent"  
  96.         android:layout_height="wrap_content"  
  97.         android:orientation="vertical">  
  98.    
  99.         <SeekBar  
  100.             android:id="@+id/seekbar"  
  101.             android:layout_width="match_parent"  
  102.             android:layout_height="wrap_content"  
  103.             android:layout_weight="1"/>  
  104.    
  105.     </LinearLayout>  
  106.    
  107.    
  108.     <LinearLayout  
  109.         android:id="@+id/linearLayout1"  
  110.         android:layout_width="wrap_content"  
  111.         android:layout_height="wrap_content"  
  112.         android:layout_gravity="center_horizontal">  
  113.    
  114.         <ImageButton  
  115.             android:id="@+id/bn_play"  
  116.             android:layout_width="wrap_content"  
  117.             android:layout_height="wrap_content"  
  118.             android:src="@drawable/play"/>  
  119.    
  120.         <ImageButton  
  121.             android:id="@+id/bn_stop"  
  122.             android:layout_width="wrap_content"  
  123.             android:layout_height="wrap_content"  
  124.             android:src="@drawable/stop"/>  
  125.    
  126.    
  127.         <Button  
  128.             android:id="@+id/bn_exit"  
  129.             android:layout_width="wrap_content"  
  130.             android:layout_height="match_parent"  
  131.             android:text="Exit"/>  
  132.    
  133.     </LinearLayout>  
  134.    
  135.     <LinearLayout  
  136.         android:id="@+id/linearLayout2"  
  137.         android:layout_width="match_parent"  
  138.         android:layout_height="wrap_content">  
  139.    
  140.     </LinearLayout>  
  141.    
  142.     <LinearLayout  
  143.         android:id="@+id/linearLayout3"  
  144.         android:layout_width="match_parent"  
  145.         android:layout_height="wrap_content">  
  146.    
  147.     </LinearLayout>  
  148.    
  149. </LinearLayout>  



七、参考链接

[1]
http://www.verydemo.com/demo_c131_i30811.html
[2]
http://griffinshi.iteye.com/blog/641037
[3]
http://www.cnblogs.com/newcj/archive/2011/05/30/2061370.html
[4]
http://blog.csdn.net/cjjky/article/details/6552852
[5]
http://www.pocketdigi.com/20100908/92.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值