190 likes | 551 Views
第五章 Service 应用. 本章主要内容. 什么是 Service. 顾名思义, Service 即“服务”的意思, Activity 拥有前台运行的用户界面,而 Service 不能自己运行,需要通过某个 Activity 或者其他 Context 对象来调用。 在默认情况下, Service 运行在应用程序进程的主线程之中,但如果需要在 Service 中处理一些诸如连接网络等耗时操作时,就应该将其放在单独的线程中进行处理,避免阻塞用户界面。
E N D
什么是Service 顾名思义,Service即“服务”的意思,Activity拥有前台运行的用户界面,而Service不能自己运行,需要通过某个Activity或者其他Context对象来调用。 在默认情况下,Service运行在应用程序进程的主线程之中,但如果需要在Service中处理一些诸如连接网络等耗时操作时,就应该将其放在单独的线程中进行处理,避免阻塞用户界面。 可以通过Context.startService()和Context.bindService()两种方式来启动Service。
什么是Service • 使用Context.startService()启动Service 步骤如下: Context.startService—>onCreate()—>onStart()—>Service running—>onDestroy()—>Service 如果Service处于未运行的状态,则需要先调用onCreate()然后再调用onStart()的顺序来启动;如果Service已经处于运行状态,则只需要调用onStart()来启动Service即可。如果是使用这种方式启动Service,那么关闭Service的方法就很简单,可以通过调用stopService()方法停止Service,再调用onDestroy()方法销毁Service。 这种调用方式的Service生命周期:onCreate()—>onStart()(多次)—>onDestroy()。
什么是Service • 2. 使用Context.bindService()启动Service 步骤如下: Context.bindService()—>onCreate()—>onBind()—>Service running—>stopService()—>onUnbind()—>onDestroy()—>Service stop。 onBind将返回给客户端一个IBind接口实例,这个实例允许客户端回调服务方法,比如得到Service的运行状态的操作。这种方法会把调用者(Context,Activity等)和Service绑定在一起,Context退出时,Service也会调用onUnbind()—>onDestroy()退出。所以这种调用方式下Service的生命周期为:onCreate()—>onBind()(与第一种方式不同,这里onBind()只能绑定一次,不可多次绑定)—>onUnbind()—>onDestroy()。
跨进程调用 为了解决进程间数据共享的问题,需要把对象拆分成操作系统能理解的简单形式,以便伪装成本地对象进行跨界访问,为此就需要跨进程通信的双方约定一个统一的接口。由于编写这种接口的方法具有很大的共性,Android提供了AIDL工具来辅助完成接口的编写工作。 AIDL(Android Interface Definition Language,即Android 接口描述语言)属于IDL语言的一种,借助它可以快速地生成接口代码,使得在同一个Android设备上运行的两个进程之间可以通过内部通信进程进行交互。
跨进程调用 服务端需要以aidl文件的方式提供服务接口,AIDL工具将生成一个对应的java接口对象,并且在生成的接口中包含一个供客户端调用的stub服务桩类,Stub对象就是远程对象的本地代理。服务端的实现类需要提供返回stub服务桩类对象的方法。
Service实例——音乐播放器 可以分别通过Context.startService()和Context.bindService()两种方式来启动Service。 示例项目名称为ServiceDemo。程序的主界面由ServiceDemo.java实现,对应的布局文件是main.xml,主要实现了通过四个按钮分别启动四种不同的音乐播放方式播放音乐文件,即通过四个Button来启动四个不同的Activity。
Service实例——音乐播放器 程序运行效果 初始界面
Service实例——音乐播放器 代码如下: publicclassServiceDemoextends Activity implementsOnClickListener { private Button musicServiceBtn,bindMusicServiceBtn, receivermusicServiceBtn,remoteMusicServiceBtn; @Override publicvoidonCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); findView(); bindButton(); } privatevoidfindView() {//用于获取UI控件的代码集合 musicServiceBtn = (Button) findViewById(R.id.musicService); ……//另外三行相似代码略 } privatevoidbindButton() {//用于绑定点击事件监听器的代码集合 musicServiceBtn.setOnClickListener(this); //另外三行相似代码略 } publicvoidonClick(View v) {//点击事件响应处理方法 switch (v.getId()) { caseR.id.musicService: startActivity(new Intent(this, PlayMusic.class)); break; //另外9行相似代码略 } } }
Service实例——音乐播放器 启动服务的方式 • 使用startService启动服务 • 使用receiver方式启动服务 • 使用bindService方式启动服务 • 通过aidl方式使用远程服务
Service实例——音乐播放器 • 使用startService启动服务 需要构造一个Intent用于启动Service,代码如下: Intent intent = new Intent("com.android.ServiceDemo.musicService"); 其中"com.android.ServiceDemo.musicService"是在AndroidManifest.xml文件中对该service类的唯一指定name,正因为如此才可以通过该方法启动service。 <serviceandroid:enabled="true"android:name=".MusicService"> <intent-filter> <actionandroid:name="com.android.ServiceDemo.musicService"/> </intent-filter> </service> 然后把各种点击事件所对应的操作码存放到Bundle(一种数据结构)中再将该数据捆绑到intent上,代码如下: Bundle bundle = new Bundle(); bundle.putInt("op", op); intent.putExtras(bundle); 最后使用startService(intent)启动服务,服务启动时可以从intent中取出前面捆绑的bundle数据结构,从中获取操作码从而获知用户操作的事件。 用stopService方法停止播放音乐 用startService方法开始播放音乐
Service实例——音乐播放器 • 2.使用receiver方式启动服务 MusicReceiver的实现代码如下: publicclassMusicReceiverextendsBroadcastReceiver { @Override publicvoidonReceive(Context context, Intent intent) { Intent it = new Intent("com.android.ServiceDemo.musicService"); Bundle bundle = intent.getExtras(); it.putExtras(bundle);//构建新的intent用于启动MusicService if(bundle != null){ int op = bundle.getInt("op"); if(op == 4){//判断是否为停止服务操作 context.stopService(it);//在receiver中停止服务 }else{ context.startService(it);//其余操作仍交给MusicService处理 } } } }
Service实例——音乐播放器 • 3.使用bindService方式启动服务 publicclassBindMusicServiceextends Service { privateMediaPlayermediaPlayer; privatefinalIBinderbinder = newMyBinder(); publicclassMyBinderextends Binder {//定义自己的Binder类用于传递Service对象 BindMusicServicegetService() { returnBindMusicService.this; } } @Override publicIBinderonBind(Intent intent) {//当服务被绑定时返回一个IBinder对象,IBinder用于传递Service对象 returnbinder; } @Override publicvoidonCreate() { super.onCreate(); Toast.makeText(this, "绑定音乐播放器成功", Toast.LENGTH_SHORT).show(); if (mediaPlayer == null) { mediaPlayer = MediaPlayer.create(this, R.raw.tmp); mediaPlayer.setLooping(false); } }
Service实例——音乐播放器 • 3.使用bindService方式启动服务 @Override publicvoidonDestroy() { super.onDestroy(); Toast.makeText(this, "停止音乐播放器", Toast.LENGTH_SHORT).show(); if(mediaPlayer != null){ mediaPlayer.stop(); mediaPlayer.release(); } } publicvoid play() {//播放方法,用public声明供外部使用 if ((!mediaPlayer.isPlaying()) && mediaPlayer != null) { mediaPlayer.start(); } } publicvoid pause() {//暂停方法 if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); } } publicvoid stop() {//停止方法 if (mediaPlayer != null) { mediaPlayer.pause(); mediaPlayer.seekTo(0); } } }
Service实例——音乐播放器 • 4.通过aidl方式使用远程服务 为了实现通过aidl方式使用远程服务,首先需要使用aidl编写用于使用远程服务的接口,编写接口的方法是在项目的包构下建立后缀名为.aidl的文件。 本项目的IMusicControlService.aidl文件内容如下: package org.allin.android.remote; interface IMusicControlService{ void play(); void stop(); void pause(); }
Service实例——音乐播放器 • 4.通过aidl方式使用远程服务 如何在另外一个应用程序中通过aidl接口来调用服务呢? • 首先需要将aidl接口定义文件IMusicControlService.aidl包括其包结构复制到ServiceDemo下(如下图),ADT也会在项目中自动生成对应的Java接口代码; • 然后就可以通过类似于绑定本地服务的方式来绑定该远程服务了。
本章小结 本章介绍了使用Service最通用的一些用法,包括启动、停止以及关闭服务,但Service的还有很多更丰富的使用方法,限于篇幅本书就不进行介绍了,有兴趣的读者可以查阅相关资料,并在实际运用中加以深入学习。