service相关
本篇仍以问题为驱动
一、什么时Service?
Service是Android程序中四大基础组件之一,它和Activity一样都是Context的子类,只不过它没有UI界面,是在后台运行的组件。Service是Android中实现程序后台运行的解决方案,它非常适用于去执行那些不需要和用户交互而且还要求长期运行的任务。Service默认并不会运行在子线程中,它也不运行在一个独立的进程中,它同样执行在UI线程中,因此,不要在Service中执行耗时的操作,除非你在Service中创建了子线程来完成耗时操作。
二、Service种类
按运行地点分类:
按运行类型分类:
按使用方式分类:
三、Service生命周期
OnCreate()
系统在service第一次创建时执行此方法,来执行只运行一次的初始化工作。如果service已经运行,这个方法不会被调用。
onStartCommand()
每次客户端调用startService()方法启动该Service都会回调该方法(多次调用)。一旦这个方法执行,service就启动并且在后台长期运行。通过调用stopSelf()或stopService()来停止服务。
OnBind()
当组件调用bindService()想要绑定到service时(比如想要执行进程间通讯)系统调用此方法(一次调用,一旦绑定后,下次再调用bindService()不会回调该方法)。在你的实现中,你必须提供一个返回一个IBinder来以使客户端能够使用它与service通讯,你必须总是实现这个方法,但是如果你不允许绑定,那么你应返回null。
OnUnbind()
当前组件调用unbindService(),想要解除与service的绑定时系统调用此方法(一次调用,一旦解除绑定后,下次再调用unbindService()会抛出异常)。
OnDestory()
系统在service不再被使用并要销毁时调用此方法(一次调用)。service应在此方法中释放资源,比如线程,已注册的侦听器,接收器等等.这是service收到的最后一个调用。
下面介绍三种不同情况下Service的生命周期情况。
1.startService / stopService
生命周期顺序:onCreate->onStartCommand->onDestroy
如果一个Service被某个Activity 调用 Context.startService方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务,还有一种方法可以关闭服务,在设置中,通过应用->找到自己应用->停止。
注意点:
①第一次 startService 会触发 onCreate 和 onStartCommand,以后在服务运行过程中,每次 startService 都只会触发 onStartCommand
②不论 startService 多少次,stopService 一次就会停止服务
2.bindService / unbindService
生命周期顺序:onCreate->onBind->onUnBind->onDestroy
如果一个Service在某个Activity中被调用bindService方法启动,不论bindService被调用几次,Service的onCreate方法只会执行一次,同时onStartCommand方法始终不会调用。
当建立连接后,Service会一直运行,除非调用unbindService来接触绑定、断开连接或调用该Service的Context不存在了(如Activity被Finish——即通过bindService启动的Service的生命周期依附于启动它的Context),系统在这时会自动停止该Service。
注意点:
第一次 bindService 会触发 onCreate 和 onBind,以后在服务运行过程中,每次 bindService 都不会触发任何回调
3.混合型(上面两种方式的交互)
当一个Service在被启动(startService)的同时又被绑定(bindService),该Service将会一直在后台运行,并且不管调用几次,onCreate方法始终只会调用一次,onStartCommand的调用次数与startService调用的次数一致(使用bindService方法不会调用onStartCommand)。同时,调用unBindService将不会停止Service,必须调用stopService或Service自身的stopSelf来停止服务。
在什么情况下使用 startService 或 bindService 或 同时使用startService 和 bindService?
①如果你只是想要启动一个后台服务长期进行某项任务那么使用 startService 便可以了。
②如果你想要与正在运行的 Service 取得联系,那么有两种方法,一种是使用 broadcast ,另外是使用 bindService ,前者的缺点是如果交流较为频繁,容易造成性能上的问题,并且 BroadcastReceiver 本身执行代码的时间是很短的(也许执行到一半,后面的代码便不会执行),而后者则没有这些问题,因此我们肯定选择使用 bindService(这个时候你便同时在使用 startService 和 bindService 了,这在 Activity 中更新 Service 的某些运行状态是相当有用的)。
③如果你的服务只是公开一个远程接口,供连接上的客服端(android 的 Service 是C/S架构)远程调用执行方法。这个时候你可以不让服务一开始就运行,而只用 bindService ,这样在第一次 bindService 的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是Remote Service,那么该效果会越明显(当然在 Service 创建的时候会花去一定时间,你应当注意到这点)。
四、Service与Activity怎么实现通信
1.Binder:
- 多个客户端可以全部连接到该服务。但是,系统只会在第一个客户端绑定时调用您的服务的onBind()方法来检索IBinder。系统然后将相同的IBinder传递给绑定的任何其他客户端,而无需再次调用onBind()。
- 当最后一个客户端解除绑定服务时,系统会销毁该服务(除非该服务也由startService()启动)。
- 实现绑定服务时,最重要的部分是定义onBind()回调方法返回的接口。您可以通过几种不同的方法来定义服务的IBinder接口,以下部分将讨论每种技术。
(1)扩展Binder类
简述:如果您的Service对您自己的应用程序是私有的,并且与客户端在相同的进程中运行(这是常见的),则应该通过扩展Binder类并创建其实例,onBind()返回该实例。 客户端收到Binder,可以使用它直接访问Binder实现或甚至Service中可用的公共方法。
实现如下:
通过 Binder 接口的形式实现,当 Activity 绑定 Service 成功的时候 Activity 会在 ServiceConnection 的类 的 onServiceConnected()回调方法中获取到 Service 的 onBind()方法 return 过来的 Binder 的子类,然后通过对象调用方法。- 新建一个继承自Service的类MusicService,然后在AndroidManifest.xml里注册这个Service
- Activity里面使用bindService方式启动MyService,也就是绑定了MyService
(到这里实现了绑定,Activity与Service通信的话继续下面的步骤) - 新建一个继承自Binder的类MusicBinder(一般为service内部类)
- 在MusicService里实例化一个MusicBinder对象mBinder,并在onBind回调方法里面返回这个mBinder对象
-
第2步bindService方法需要一个ServiceConnection类型的参数,在ServiceConnection里可以取到一个IBinder对象,就是第4步onBinder返回的mBinder对象(也就是在Activity里面拿到了Service里面的mBinder对象)
- 在Activity里面拿到mBinder之后就可以调用这个binder里面的方法了(也就是可以给Service发消息了),需要什么方法在MyBinder类里面定义实现就行了。如果需要Service给Activity发消息的话,通过这个binder注册一个自定义回调即可。
public class MusicService extends Service { //实现一个播放音乐的servicepublic MediaPlayer mediaPlayer;private MusicBinder mBinder = new MusicBinder(this); public MusicService() { mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource("/storage/emulated/0/netease/cloudmusic/Music/雷军 - Are You OK.MP3"); mediaPlayer.prepare(); mediaPlayer.setLooping(true); } catch (Exception e) { } } @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } @Override public int onStartCommand(Intent intent, int flags, int startId) {return super.onStartCommand(intent, flags, startId); } public void playOrPause() { if (mediaPlayer.isPlaying()) { mediaPlayer.pause(); } else { mediaPlayer.start(); } } public void stop() { if (mediaPlayer != null) { mediaPlayer.stop(); try { mediaPlayer.reset(); mediaPlayer.setDataSource(Environment.getExternalStorageState()
+ "/storage/emulated/0/netease/cloudmusic/Music/雷军 - Are You OK.MP3"); mediaPlayer.prepare(); mediaPlayer.seekTo(0); } catch (Exception e) { } } } public class MusicBinder extends Binder { MusicService musicService; private OnTestListener mListener; public MusicBinder(MusicService service) { this.musicService = service; } public MusicService getService() { return musicService; } public void playOrPause() { musicService.playOrPause(); mListener.onTest(musicService.mediaPlayer.isPlaying() ? "play" : "pause"); } public void stop() { musicService.stop(); mListener.onTest("stop"); } // MyBinder 里面提供一个注册回调的方法 public void setOnTestListener(OnTestListener listener) { this.mListener = listener; } } //自定义一个回调接口 public interface OnTestListener { void onTest(String str); } }
activity代码如下:
public class MusicActivity extends Activity implements MusicService.OnTestListener { private MusicService.MusicBinder mBinder; private MusicService mService; private ServiceConnection sc = new MusicServiceConnection(); private TextView textView; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_btns); init(); } private void init() { textView = (TextView) findViewById(R.id.tv_music); findViewById(R.id.btn_1).setOnClickListener(v -> { //绑定Service Intent intent = new Intent(this, MusicService.class); startService(intent); bindService(intent, sc, Context.BIND_AUTO_CREATE); }); findViewById(R.id.btn_2).setOnClickListener(v -> { //解除绑定 unbindService(sc); }); findViewById(R.id.btn_3).setOnClickListener(v -> { //也可以直接通过binder获取的service实例,操作播放暂停方法 // mService.playOrPause(); //通过binder实例操作binder内部的service方法 mBinder.playOrPause(); }); findViewById(R.id.btn_4).setOnClickListener(v -> { // mService.stop(); mBinder.stop(); }); } @Override public void onTest(String str) { //接口回调,提供给service向activity的通信,实现双向通信 textView.setText(str); } class MusicServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mBinder = (MusicService.MusicBinder) iBinder; mService = mBinder.getService(); mBinder.setOnTestListener(MusicActivity.this); } @Override public void onServiceDisconnected(ComponentName componentName) { mService = null; } } }
简述:如果需要Service 和客户端位于不同的进程,则可以使用Messenger为服务创建一个interface。 以这种方式,服务定义响应不同类型的Message对象的Handler。 该Handler是Messenger的基础,可以与客户端共享IBinder,允许客户端使用Message对象向服务发送命令。 此外,客户端可以定义自己的Messenger,因此服务可以发回消息。
这是执行进程间通信(IPC)的最简单的方法,因为Messenger将所有请求排队到单个线程中,以便使用者不必将服务设计为线程安全。
它引用了一个Handler对象,以便others能够向它发送消息(使用mMessenger.send(Message msg)方法)。该类允许跨进程间基于Message的通信(即两个进程间可以通过Message进行通信),在服务端使用Handler创建一个Messenger,客户端持有这个Messenger就可以与服务端通信了。一个Messeger不能同时双向发送,两个就就能双向发送了
使用:如果您需要服务与远程进程通信,那么可以使用Messenger为服务提供接口。这种技术允许执行进程间通信(IPC),而无需使用AIDL。
以下是Messenger使用如下:
A、Service实现一个Handler,接收客户端发送的消息。
B、Handler用于创建一个Messenger对象(这是对Handler的引用)。
C、Messenger创建一个IBinder,Service将IBinder 从onBind()返回给客户端。
D、客户端使用IBinder来实例化Messenger (引用服务的Handler),客户端利用Messenger将Message对象发送到服务。
E、Service接收 Message在其Handler的handleMessage()方法中进行处理。
service实现代码
a. service创建Handler对象。
* 处理来自客户端的消息 */ private Handler mHandler = new Handler() { //创建Handler对象 @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Toast.makeText(getApplicationContext(), "hello,remote service", Toast.LENGTH_SHORT).show(); //通过message对象获取客户端传递过来的Messenger对象。 Messenger messenger = msg.replyTo; if (messenger != null) { Message messg = Message.obtain(null, MSG_SAY_HELLO); try { //向客户端发送消息 messenger.send(messg); } catch (RemoteException e) { e.printStackTrace(); } } break; case MSG_MUSIC_PLAY: //播放/暂停音乐 playOrPause(); break; case MSG_MUSIC_STOP: //停止播放 stop(); break; default: break; } } };
b.创建Messenger对象
//创建Mesenger 对象 public Messenger mMessenger;
public RemoteMusicService() { mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource("/storage/emulated/0/netease/cloudmusic/Music/雷军 - Are You OK.MP3"); mediaPlayer.prepare(); mediaPlayer.setLooping(true); } catch (Exception e) { } mMessenger = new Messenger(mHandler); }
c.onBind()返回IBinder对象
private MusicBinder mBinder = new MusicBinder(this);
@Nullable @Override public IBinder onBind(Intent intent) { //通过Mesenger对象获取IBinder实例,并返回 return mMessenger.getBinder(); }
d.服务端接收客户端发送的消息,并在Handler对象的hanleMessage方法中进行处理。
完整service代码如下
public class RemoteMusicService extends Service { //实现一个播放音乐的service public static final int MSG_SAY_HELLO = 101; public static final int MSG_MUSIC_PLAY = 102; public static final int MSG_MUSIC_STOP = 103; public MediaPlayer mediaPlayer; /** * 处理来自客户端的消息 */ private Handler mHandler = new Handler() { //创建Handler对象 @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Toast.makeText(getApplicationContext(), "hello,remote service", Toast.LENGTH_SHORT).show(); //通过message对象获取客户端传递过来的Messenger对象。 Messenger messenger = msg.replyTo; if (messenger != null) { Message messg = Message.obtain(null, MSG_SAY_HELLO); try { //向客户端发送消息 messenger.send(messg); } catch (RemoteException e) { e.printStackTrace(); } } break; case MSG_MUSIC_PLAY: //播放/暂停音乐 playOrPause(); break; case MSG_MUSIC_STOP: //停止播放 stop(); break; default: break; } } }; //创建Mesenger 对象 public Messenger mMessenger; private MusicBinder mBinder = new MusicBinder(this); public RemoteMusicService() { mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource("/storage/emulated/0/netease/cloudmusic/Music/雷军 - Are You OK.MP3"); mediaPlayer.prepare(); mediaPlayer.setLooping(true); } catch (Exception e) { } mMessenger = new Messenger(mHandler); } @Nullable @Override public IBinder onBind(Intent intent) { //通过Mesenger对象获取IBinder实例,并返回 return mMessenger.getBinder(); } @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } public void playOrPause() { if (mediaPlayer.isPlaying()) { mediaPlayer.pause(); } else { mediaPlayer.start(); } } public void stop() { if (mediaPlayer != null) { mediaPlayer.stop(); try { mediaPlayer.reset(); mediaPlayer.setDataSource(Environment.getExternalStorageState() + "/storage/emulated/0/netease/cloudmusic/Music/雷军 - Are You OK.MP3"); mediaPlayer.prepare(); mediaPlayer.seekTo(0); } catch (Exception e) { } } } public class MusicBinder extends Binder { RemoteMusicService musicService; public MusicBinder(RemoteMusicService service) { this.musicService = service; } public void playOrPause() { musicService.playOrPause(); } public void stop() { musicService.stop(); } } }
AndroidManifest文件
<service android:name=".service.musicService.RemoteMusicService" android:enabled="true" android:exported="true" android:icon="@drawable/ic_menu_camera" android:label="music" android:process=":music" />
exported要设置为true,否则别的进程不能使用该Service
客户端activity实现
a.绑定Service
b. 连接成功回调
实现ServiceConnection,必须覆盖两个回调方法:
findViewById(R.id.btn_5).setOnClickListener(v -> { Intent intent = new Intent(this, RemoteMusicService.class); bindService(intent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { // 当与服务端连接成功时,回调该方法。 //通过IBinder参数获取Messenger mRemoteMessenger = new Messenger(iBinder); textView.setText("connected"); } @Override public void onServiceDisconnected(ComponentName componentName) { mRemoteMessenger = null; textView.setText("disconnected"); } }, Context.BIND_AUTO_CREATE); textView.setText("connecting"); });
c. 向服务端发送消息
Message属性replyTo不是必须的,如果希望Service向客户端发送消息则需要。
private void sendMsg(int what) { if (mRemoteMessenger == null) return; //创建消息,设置what属性 Message msg = Message.obtain(null, what); //replyTo不是必须的,如果希望Service向客户端发送消息则需要设置。 msg.replyTo = mMessenger; try { //发送消息 mRemoteMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } }
d. 接收Service的回复
private Messenger mRemoteMessenger; private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case MSG_SAY_HELLO: textView.setText("hello"); break; default: break; } } }; private Messenger mMessenger = new Messenger(mHandler);
e. 解除绑定
需要断开与服务的连接时,请调用unbindService()。
完整客户端activity代码如下
public class MusicActivity extends Activity { private TextView textView; private Messenger mRemoteMessenger; private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case MSG_SAY_HELLO: textView.setText("hello"); break; default: break; } } }; private Messenger mMessenger = new Messenger(mHandler); @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_btns); init(); } private void init() { textView = (TextView) findViewById(R.id.tv_music); findViewById(R.id.btn_5).setOnClickListener(v -> { Intent intent = new Intent(this, RemoteMusicService.class); bindService(intent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { // 当与服务端连接成功时,回调该方法。 //通过IBinder参数获取Messenger mRemoteMessenger = new Messenger(iBinder); textView.setText("connected"); } @Override public void onServiceDisconnected(ComponentName componentName) { mRemoteMessenger = null; textView.setText("disconnected"); } }, Context.BIND_AUTO_CREATE); textView.setText("connecting"); }); findViewById(R.id.btn_6).setOnClickListener(v -> { sendMsg(MSG_SAY_HELLO); }); findViewById(R.id.btn_7).setOnClickListener(v -> { sendMsg(MSG_MUSIC_PLAY); }); findViewById(R.id.btn_8).setOnClickListener(v -> { sendMsg(MSG_MUSIC_STOP); }); } private void sendMsg(int what) { if (mRemoteMessenger == null) return; //创建消息,设置what属性 Message msg = Message.obtain(null, what); //replyTo不是必须的,如果希望Service向客户端发送消息则需要设置。 msg.replyTo = mMessenger; try { //发送消息 mRemoteMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } }
简述:AIDL(Android Interface Definition Language)执行所有的工作,将对象分解为基元,操作系统可以在进程之间了解和编组它们以执行IPC。 之前使用的Messenger技术实际上是基于AIDL作为其底层结构。 如上所述,Messenger在单个线程中创建所有客户端请求的队列,因此服务一次接收一个请求。 但是,如果您希望您的服务同时处理多个请求,则可以直接使用AIDL。 在这种情况下,您的服务必须能够进行多线程并建立线程安全。
要直接使用AIDL,您必须创建一个定义编程接口的.aidl文件。 Android SDK工具使用此文件生成一个实现接口并处理IPC的抽象类,然后您可以在服务中扩展它。
注意:大多数应用程序不应该使用AIDL创建绑定的服务,因为它可能需要多线程功能,并可能导致更复杂的实现。因此,AIDL不适用于大多数应用程序。
aidl 比较适合当客户端和服务端不在同一个应用下的场景。五、IntentService有什么优点(与Service区别)
IntentService是Service的子类,是一个异步的,会自动停止的服务,很好解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题
- 会创建独立的worker线程来处理所有的Intent请求;
- 会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程问题;
- 所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service;
- 为Service的onBind()提供默认实现,返回null;
- 为Service的onStartCommand提供默认实现,将请求Intent添加到队列中;
- IntentService不会阻塞UI线程,而普通Serveice会导致ANR异常
- Intentservice若未执行完成上一次的任务,将不会新开一个线程,是等待之前的任务完成后,再执行新的任务,等任务完成后再次调用stopSelf()
简单实现