Android AIDL和远程Service调用示例代码
本讲的内容,理解起来很难,也许你看了很多资料也看不明白,但是用起来缺简单的要命。所以我们干脆拿一个音乐播放器中进度条的实例来说明一下AID
Android:AIDL和远程Service调用
本讲的内容,理解起来很难,也许你看了很多资料也看不明白,但是用起来缺简单的要命。所以我们干脆拿一个音乐播放器中进度条的实例来说明一下AIDL和Remote Service的价值和使用方法,你把这个例子跑一边,体会一下就OK了。下面的例子是我
正在准备的项目实例中的一部分。
首先说明一下我们面临的问题,如果看不懂下面的描述请看前面的课程:
第一、我们知道在AndroId中如果需要进行音乐播放,最方面的方法就是使用自带的MediaPlayer对象,如果我们在Activity中控制MediaPlayer对象进行播放,那么一旦你打开了另外一个程序譬如浏览器,那么歌声就会立刻停止,这当然不是我们需要的结果。 我们需要的是在做其他事情的同时能够在后台听歌,于是我们就需要把对MediaPlayer对象的操作放在后台Service中去。
第二、我们已经把对MediaPlayer的操作转移到Service中去了,按照我们以前的做法,我们在Activity中发送一个Intent对象给Service对象,在Intent中传送播放啊、暂停啊一类的信息给Service,这样Service就知道该怎么做了。这一切看起来很美好,可是现在出了一个新问题,那就是我想在Activity中显示一个进度条,这个进度条要跟着Service中的MediaPlayer中的歌曲进度同步向前走,而且如果我点击进度条中的某一个位置,还想让歌曲跳转到新的时间点继续播放,这个,该怎么实现?
第三、我们需要在Activity中操作Service中的MediaPlayer对象,就好像这个对象是自己的一样。我们可以采用Android接口定义语言 AIDL(Android Interface Definition Language)技术:
1、把Service中针对MediaPlayer的操作封装成一个接口(.aidl文件)
2、在Service中建个子类实现这接口的存根(stub)对象
3、并在onBind()方法中返回这个存根对象。
4、在Activity中使用绑定服务的方式连接Service,但是不用Intent来传递信息,而是在ServiceConnection的onServiceConnected方法里,获得Service中Stub对象的客户端使用代理。我们通过操作Activity中的代理就可以达到操作Service中的MediaPlayer对象的目的。这样我们就可以想用本地对象一样操作Service中的对象了,那么进度条一类的需求自然也就迎刃而解。
下面的例子,并不是专门为本讲准备的,所以有些无关代码,而且没加注释,请见谅(本例完整讲解会放在项目实训中,正在准备):
1、新建一个项目 App_elfPlayer ,启动Activity是个启动画面:CoverActivity
2、AndroidManifest.xml 的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?xml version= "1.0" encoding= "utf-8" ?> <manifest package= "app.android.elfplayer" xmlns:android= "http://schemas.android.com/apk/res/android" android:versioncode= "1" android:versionname= "1.0" > <uses -sdk= "" android:minsdkversion= "7" > <uses -permission= "" android:name= "android.permission.WRITE_EXTERNAL_STORAGE" ></uses> <application android:label= "@string/app_name" android:icon= "@drawable/icon" > <activity android:name= ".CoverActivity" > <intent -filter= "" > <action android:name= "android.intent.action.MAIN" > <category android:name= "android.intent.category.LAUNCHER" > </category></action></intent> </activity> <activity android:name= ".PlayerActivity" > </activity> <service android:name= ".MusicService" android:enabled= "true" > </service> </application> </uses></manifest> |
我们注意到有2个Activity,1个Service,还有读写外部存储的权限声明3、CoverActivity.java的代码如下:这是个全屏的启动画面,2秒后会跳转到PlayerActivity
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 | package app.android.elfplayer; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.view.Window; import android.view.WindowManager; public class CoverActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.cover); new Handler().postDelayed( new Runnable(){ @Override public void run() { Intent mainIntent = new Intent(CoverActivity.this,PlayerActivity. class ); CoverActivity.this.startActivity(mainIntent); CoverActivity.this.finish(); } }, 2000); } } |
4、PlayerActivity.java的代码如下:
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 131 132 133 | package app.android.elfplayer; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.util.Log; import android.view.View; import android.widget.ImageButton; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; public class PlayerActivity extends Activity { public static final int PLAY = 1; public static final int PAUSE = 2; ImageButton imageButtonFavorite; ImageButton imageButtonNext; ImageButton imageButtonPlay; ImageButton imageButtonPre; ImageButton imageButtonRepeat; SeekBar musicSeekBar; IServicePlayer iPlayer; boolean isPlaying = false; boolean isLoop = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.player); imageButtonFavorite = (ImageButton) findViewById(R.id.imageButtonFavorite); imageButtonNext = (ImageButton) findViewById(R.id.imageButtonNext); imageButtonPlay = (ImageButton) findViewById(R.id.imageButtonPlay); imageButtonPre = (ImageButton) findViewById(R.id.imageButtonPre); imageButtonRepeat = (ImageButton) findViewById(R.id.imageButtonRepeat); musicSeekBar = (SeekBar) findViewById(R.id.musicSeekBar); bindService( new Intent(PlayerActivity.this, MusicService. class ), conn, Context.BIND_AUTO_CREATE); startService( new Intent(PlayerActivity.this, MusicService. class )); imageButtonPlay.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { Log.i( "yao" , "imageButtonPlay -> onClick" ); if (!isPlaying) { try { iPlayer.play(); } catch (RemoteException e) { e.printStackTrace(); } imageButtonPlay.setBackgroundResource(R.drawable.pause_button); isPlaying = true; } else { try { iPlayer.pause(); } catch (RemoteException e) { e.printStackTrace(); } imageButtonPlay.setBackgroundResource(R.drawable.play_button); isPlaying = false; } } }); musicSeekBar.setOnSeekBarChangeListener( new OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { if (iPlayer != null) { try { iPlayer.seekTo(seekBar.getProgress()); } catch (RemoteException e) { e.printStackTrace(); } } } }); handler.post(updateThread); } private ServiceConnection conn = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { Log.i( "yao" , "ServiceConnection -> onServiceConnected" ); iPlayer = IServicePlayer.Stub.asInterface(service); } public void onServiceDisconnected(ComponentName className) { }; }; Handler handler = new Handler() { @Override public void handleMessage(Message msg) { }; }; private Runnable updateThread = new Runnable() { @Override public void run() { if (iPlayer != null) { try { musicSeekBar.setMax(iPlayer.getDuration()); musicSeekBar.setProgress(iPlayer.getCurrentPosition()); } catch (RemoteException e) { e.printStackTrace(); } } handler.post(updateThread); } }; } |
5、其中用到的IServicePlayer.aidl,放在和Java文件相同的包中,内容如下:
1 2 3 4 5 6 7 8 9 10 | package app.android.elfplayer; interface IServicePlayer{ void play(); void pause(); void stop(); int getDuration(); int getCurrentPosition(); void seekTo(int current); boolean setLoop(boolean loop); } |
一旦你写好了这个IServicePlayer.aidl文件,ADT会自动帮你在gen目录下生成IServicePlayer.java文件。
6、MusicService.java的内容如下:
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 | package app.android.elfplayer; import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; public class MusicService extends Service { String tag = "yao" ; public static MediaPlayer mPlayer; public boolean isPause = false; IServicePlayer.Stub stub = new IServicePlayer.Stub() { @Override public void play() throws RemoteException { mPlayer.start(); } @Override public void pause() throws RemoteException { mPlayer.pause(); } @Override public void stop() throws RemoteException { mPlayer.stop(); } @Override public int getDuration() throws RemoteException { return mPlayer.getDuration(); } @Override public int getCurrentPosition() throws RemoteException { return mPlayer.getCurrentPosition(); } @Override public void seekTo(int current) throws RemoteException { mPlayer.seekTo(current); } @Override public boolean setLoop(boolean loop) throws RemoteException { return false; } }; @Override public void onCreate() { Log.i(tag, "MusicService onCreate()" ); mPlayer = MediaPlayer.create(getApplicationContext(), ElfPlayerUtil.getFileinSD( "wind.mp3" )); } @Override public IBinder onBind(Intent intent) { return stub; } } |
7、实现效果图:
最后总结一下,AIDL提供了一种非常简单的方式,让我们可以把一个进程内的对象或方法暴露给另一个程序使用,就好象另一个程序也拥有这些功能一样。
以上就是Android AIDL和远程Service 的介绍和简单应用,后续继续补充相关知识,谢谢大家的支持!