然而,当使用一个服务时,从而向用户的活动向MediaPlayer发出命令变得更为复杂。
为了能够控制MediaPlayer,需要把该活动与服务绑定在一起。一旦这样做,由于活动和服务在相同的进程中运行,因此可以直接调用该服务中的方法。如果正在创建一个远程服务,那么必须采起更深入一步的步骤。
我们在上述活动中添加一个按钮,其标签为“have fun”。当单击这个按钮时,会使得MediaPlayer倒回来几秒中,然后继续播放音频文件。
首先把该按钮添加到布局XML:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical" 5 > 6 <TextView 7 android:text="Backgroung Audio Player" 8 android:layout_width="fill_parent" 9 android:layout_height="wrap_content"></TextView> 10 <Button 11 android:layout_width="wrap_content" 12 android:layout_height="wrap_content" 13 android:id="@+id/StartPlayerbackButton" 14 android:text="Start Playerback"/> 15 <Button 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:id="@+id/StopPlayerbackButton" 19 android:text="Stop Playerback"/> 20 <Button 21 android:layout_width="wrap_content" 22 android:layout_height="wrap_content" 23 android:id="@+id/HaveFunButton" 24 android:text="Have Fun"/> 25 </LinearLayout>
然而在活动中会得到该按钮的引用,并且把它的OnClickListener设置为活动本身,就像现有的按钮一样。
1 package com.nthm.androidtestActivity; 2 3 import com.nthm.androidtest.R; 4 import com.nthm.androidtestService.BackgroundAudioService; 5 import android.app.Activity; 6 import android.content.Intent; 7 import android.os.Bundle; 8 import android.view.View; 9 import android.view.View.OnClickListener; 10 import android.widget.Button; 11 12 public class BackgroundAudioActivity extends Activity implements 13 OnClickListener { 14 private Button startPlaybackButton; 15 private Button stopPlaybackButton; 16 private Button haveFunButton; 17 private Intent playbackServiceIntent; 18 @Override 19 protected void onCreate(Bundle savedInstanceState) { 20 super.onCreate(savedInstanceState); 21 setContentView(R.layout.backgroundaudioactivity); 22 startPlaybackButton=(Button) findViewById(R.id.StartPlayerbackButton); 23 stopPlaybackButton=(Button) findViewById(R.id.StopPlayerbackButton); 24 haveFunButton=(Button) findViewById(R.id.HaveFunButton); 25 startPlaybackButton.setOnClickListener(this); 26 stopPlaybackButton.setOnClickListener(this); 27 haveFunButton.setOnClickListener(this); 28 playbackServiceIntent=new Intent(this, BackgroundAudioService.class); 29 }
为了使此按钮与服务中运行的MediaPlayer进行交互,必须绑定到该服务。完成该操作的方式是采用bindService方法。该方法接受一个意图;事实上,可以重用启动该服务的playbackServiceIntent、一个ServiceConnection对象以及一些指明当该服务没有运行时应该做什么的标志。
在活动的onClick方法中,当按下startPlaybackButton时,在启动服务之后就会立刻绑定他。当调用stopPlaybackButton时,将与服务解除绑定。
1 @Override 2 public void onClick(View v) { 3 if(v==startPlaybackButton){ 4 //startService(playbackServiceIntent); 5 //finish(); 6 bindService(playbackServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); 7 }else if(v==stopPlaybackButton){ 8 //stopService(playbackServiceIntent); 9 //finish(); 10 unbindService(serviceConnection); 11 }
你可能会注意到其中使用了一个没有定义的新对象:serviceConnection。过一会儿将对该对象进行介绍。
接下来还需要完成onClick方法。因为该活动也被设置为新按钮的OnClickListener。所以同样应该处理这种情况以完成onClick方法。
1 else if(v==haveFunButton){ 2 baService.havaFun(); 3 } 4 }
在新的部分中使用了另一个新对象:baService。baService是BackgroundAudioService类型的对象。现在仅仅是声明它,在创建serviceConnection对象时再介绍如何创建它。
1 private BackgroundAudioService baService;
如前所述,我们仍然没有声明和实例化一个称为serviceConnection的对象。serviceConnection是一个ServiceConnection类型的对象,他是一个接口,用于监听所绑定服务的状态。
现在开始介绍如何创建serviceConnection:
1 private ServiceConnection serviceConnection=new ServiceConnection() {
当通过一条bindService命令建立了与服务的连接,且该bindService命令把这个对象命名为serviceConnection(如同在bindService调用中所做的一样)时,将调用如下所示的onServiceConnected方法。
向该方法传递一个IBinder对象,其实际上是从服务本身创建和提交的。在当前情况下,这个IBinder对象将使BackgroundAudioServiceBinder类型,我们将在服务中创建它。它将有一个方法用于返回我们的服务本身,称为getService。可以对该方法返回的对象直接操作,正如我们在单击haveFunButton时所做的一样。
1 @Override 2 public void onServiceConnected(ComponentName name, IBinder service) { 3 baService=((BackgroundAudioService.BackgroundAudioServiceBinder)service).getService(); 4 }
还需要一恶搞onServiceDisconnected方法来处理与服务断开时的情况。
1 @Override 2 public void onServiceDisconnected(ComponentName name) { 3 baService=null; 4 } 5 }; 6 }
现在可以关注在服务本身中需要做出的更改。
1 package com.nthm.androidtestService; 2 3 import com.nthm.androidtest.R; 4 import android.app.Service; 5 import android.content.Intent; 6 import android.media.MediaPlayer; 7 import android.media.MediaPlayer.OnCompletionListener; 8 import android.os.Binder; 9 import android.os.IBinder; 10 11 public class BackgroundAudioService extends Service implements 12 OnCompletionListener { 13 private MediaPlayer mediaPlayer;
需要在服务中做出的第一个改动是创建一个内部类,其扩展Binder,在请求时可以返回服务本身。
1 public class BackgroundAudioServiceBinder extends Binder{ 2 public BackgroundAudioService getService(){ 3 return BackgroundAudioService.this; 4 } 5 }
随后,将其实例化为一个对象,称为basBinder。
1 private final IBinder basBinder=new BackgroundAudioServiceBinder();
并且重写onBind的实现以返回它。
1 @Override 2 public IBinder onBind(Intent intent) { 3 return basBinder; 4 }
以上就是实现绑定多需要完成的工作。现在只需要处理“Having Fun”按钮。
如前所述,当单击haveFunButton时,我们希望MediaPlayer倒回来几秒钟,在当前实现中,它将倒回2500毫秒或2.5秒。
1 public void havaFun(){ 2 if(mediaPlayer.isPlaying()){ 3 mediaPlayer.seekTo(mediaPlayer.getCurrentPosition()-2500); 4 } 5 }
以上就是服务中的更新。下面是除此之外的其他代码。
1 @Override 2 public void onCreate() { 3 super.onCreate(); 4 mediaPlayer=MediaPlayer.create(this, R.raw.music); 5 mediaPlayer.setOnCompletionListener(this); 6 } 7 8 @Override 9 public int onStartCommand(Intent intent, int flags, int startId) { 10 if(!mediaPlayer.isPlaying()){ 11 mediaPlayer.start(); 12 } 13 return START_STICKY; 14 } 15 16 @Override 17 public void onDestroy() { 18 if(mediaPlayer.isPlaying()){ 19 mediaPlayer.stop(); 20 } 21 mediaPlayer.release(); 22 super.onDestroy(); 23 } 24 @Override 25 public void onCompletion(MediaPlayer mp) { 26 stopSelf(); 27 } 28 }
既然已经搭建好了基础,现在就可以向服务中添加任何喜欢的功能,同时通过绑定服务,可以直接从活动中调用havaFun等各种方法。如果没有绑定服务,那么除了启动和停止该服务之外,我们将不能做任何其他事情。
上述示例提供了一个良好的起点。可用于搭建在后台播放音频文件的应用程序,即当音频持续播放时允许用户继续执行其他的任务。可以对第二个示例进行扩展,以构建一个具有完整功能的音频播放应用程序。