Android AIDL和远程Service调用示例代码
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 的内容如下:
<?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
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的代码如下:
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文件相同的包中,内容如下:
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的内容如下:
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 的介绍和简单应用,后续继续补充相关知识,谢谢大家的支持!