Service
可以在后台长时间运行操作而不提供用户界面的组件。
组件可以启动服务,也可以绑定到服务。
服务可以处理网络事务,播放音乐,io操作,ContentProvider交互 等耗时操作都在后台执行。
服务在其托管进程的主线程中执行。(即不创建自己的线程,也不在单独的进程中执行(除非另外指定))
耗时操作应该在服务内创建新线程来工作。
使用Service 还是 使用线程来完成后台任务
服务是一种即使用户未与应用交互也可以在后台运行的组件。因此必要时才去创建服务。(比如:用户不管处于哪个app的哪个activity,都期望后台有此功能)
如果只是在用户正在与应用交互时,才有此需要,则应创建新线程,而非服务。例如,开发者只想在activity运行的同时,播放一些音乐。
托管Service的进程,相比托管Activity(启动线程的)的进程,有更高的进程优先级。服务被kill的机会更小。
两种形式的Service
Context.startService
一旦启动便在后台无限期运行,即使启动服务的组件已经销毁也不受影响
已启动的服务通常是执行单一的操作。
Context.bindService
当应用组件通过调用bindService后绑定到服务。
Context.startService方式启动服务
执行startService,生命周期执行的是
继续执行两次startService
执行stopService,生命周期是
在onStartCommand中做一些耗时的操作
onStartCommand在主线程执行,
代码
@Override public int onStartCommand(Intent intent, int flags, int startId) { logs("onStartCommand , thread " + Thread.currentThread().getName()); return super.onStartCommand(intent, flags, startId); }
打印
因此onStartCommand中不适合做一些耗时的操作,若使用耗时的操作,应该开启一个线程。
启动一个耗时Service的例子
启动service后,开启一个线程,用来做耗时的操作,等耗时操作完毕,使用handler发送消息给主线程,通知service关闭。
package com.jue.testservice1; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.support.annotation.Nullable; import android.util.Log; /** * Created by jue on 2016/11/17. */ public class MyService extends Service { private Handler mHander = new MyHandler(); @Override public void onCreate() { super.onCreate(); logs("onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { logs("onStartCommand , thread " + Thread.currentThread().getName() + " 开启线程,做一些耗时的操作"); new MyThread().start(); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); logs("onDestroy"); } @Nullable @Override public IBinder onBind(Intent intent) { logs("onBind"); return null; } @Override public void onRebind(Intent intent) { super.onRebind(intent); logs("onRebind"); } @Override public boolean onUnbind(Intent intent) { logs("onUnbind"); return super.onUnbind(intent); } private void logs(String txt) { Log.i("jue","MyService -> " + txt); } public class MyThread extends Thread { @Override public void run() { logs("MyThread-> run 一些耗时的操作 开始"); try{ Thread.sleep(16000); } catch (Exception e) { e.printStackTrace(); } logs("MyThread-> run 一些耗时的操作 结束,给主线程发送消息,通知Service关闭"); mHander.sendEmptyMessage(0); } } public class MyHandler extends Handler { @Override public void handleMessage(Message msg) { logs("MyHandler-> handleMessage 收到消息,执行stopSelf,结束"); stopSelf(); } } }
输出日志
onStartCommand的返回值
START_STICKY
如果service 进程在started之后(after returning from onStartComand),被kill。稍后,系统将re-create Service,并且会保证在新的servcie实例创建好后,调用onStartComand方法。开发者应该谨慎使用,如果没有等待的命令被传递到这个service,调用时,将传递一个null intent对象。
这个常量清楚的表明了service可能会在任意时间开启,停止。比如一个后台的音乐被重放。
START_NOT_STICKY
如果被kill掉 并且没有新的intent来启动,那么这个service不会被 re-create,直到再次被显示的调用Context.startService
START_REDELIVER_INTENT
如果被kill掉,最后一次被启动的intent将被再次传递到onStartComand方法。
START_STICKY_COMPATIBILITY
在被系统kill之后,不保证onStartComand将会再次被调用。
多次执行start与stop
启动已经存在的service,只会执行一次onCreate,会执行多次onStartCommand,
即便多次启动,也只需要执行一次stopService,即可stop掉
下面日志的代码执行了多次startService,执行了多次stopService
Context.startService之后,停止一个Service
启动的Service会一直保持运行。
主动停止Service
其他组件:Context.stopService.
Service自身:Service.stopSelf()
Context.bindService 何时使用
如需与Activity 和 Service进行交互
需要通过ipc向其他应用公开某些功能,则应该创建bind服务。
绑定Service的特点:
必须实现onBind回调方法,以返回IBinder,用于定义与服务通信
如果没有组件绑定到service,系统会销毁service。
多个客户端可以同时绑定到服务。
客户端完成与service交互后,需要取消绑定。一旦没有客户端绑定到该service,系统就会销毁service
多个service可以同时绑定到一个服务。但是只有第一个客户端绑定时,才会调用onBind
Activity 、Service、ContentProvider 可以绑定到服务。BroadcastReceiver不能绑定服务。
onServiceDisconnected 系统在与服务的连接意外中断时调用该方法,比如当前服务崩溃或被终止。当客户端取消绑定时,系统不会调用该方法。
绑定的service无法使用Context.stopService停止。
绑定的service无法使用Service.stopSelft停止。
开发者应该捕获DeadObjectException异常,在连接中断的时引发,这是远程调用的唯一异常
当服务与所有client的绑定全部取消时,系统会销毁服务。(除非还使用onStartCommand启动了该服务)
对于使用startService启动的service,必须显示的停止service。 stopService 或 stopSelf
创建绑定service,必须提供IBinder
有三种方法定义接口
- 继承自Binder类
- 同一进程中,优先使用此方式。
- 不以此方式创建接口的唯一原因:服务被启动应用占用 或 不同进程占用
- 使用Messenger
- 跨进程工作
- 无需使用AIDL即可执行IPC通信
- 使用Messenger会在单一线程中包含所有请求的队列,这样就不必对服务进行线程安全设计了
- 使用AIDL
如何解除bind 的service
Context.unbindService
同一进程中,继承Binder实现绑定Service的实现代码
BindService代码
public class BindService extends Service { private Binder localBinder = new LocalBinder(); @Nullable @Override public IBinder onBind(Intent intent) { return localBinder; } public class LocalBinder extends Binder { public BindService getService() { return BindService.this; } } public int getRandomInt() { logs("getRandomInt ............... bindService.hashCode= " + hashCode()); new Throwable().printStackTrace(); return new Random().nextInt(100); } public static void logs(String text) { Log.i("jue","BindService -> " + text); } }
与其交互的activity的代码
@Override public void onClick(View v) { if (v == mStart) { Intent intent = new Intent(MainActivity.this, MyService.class); startService(intent); return; } if (v == mStop) { Intent intent = new Intent(MainActivity.this, MyService.class); stopService(intent); return; } if (v == mStartIS) { logs("onClick start MyIntentService "); Intent intent = new Intent(MainActivity.this, MyIntentService.class); startService(intent); return; } if (v == mBind) { bindBindService(); return; } if (v == mBindGet) { getDataFromService(); return; } if (v == mUnbind) { unBindService(); return; } } private void getDataFromService() { if (!isBind) { logs("getDataFromService 还没有绑定,return"); return; } logs("getDataFromService"); int data = mBindService.getRandomInt(); mTextView.setText("来自Service的数据: " + data); } private void bindBindService() { if (isBind) { logs("bindService, 已经bind,return"); return; } logs("bindService"); Intent intent = new Intent(MainActivity.this, BindService.class); bindService(intent, connection, BIND_AUTO_CREATE); } private void unBindService() { if (!isBind) { logs("unBindService, 已经unbind,return"); return; } logs("unBindService"); unbindService(connection); } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { isBind = true; BindService.LocalBinder lb = (BindService.LocalBinder) service; mBindService = (BindService) lb.getService(); logs("ServiceConnection->onServiceConnected bindService.hashCode= " + mBindService.hashCode()); } @Override public void onServiceDisconnected(ComponentName name) { isBind = false; logs("ServiceConnection->onServiceDisconnected"); } };
输出日志
可以得知:
主线程中调用的实例就是BindService的实例。通过binder,获取到service实例,对其进行了直接调用。
继承Binder方式,并不合适跨进程,如果声明单独的进程
<service android:name=".BindService" android:process=":testprocess" />
发生异常的日志
Activity与Service 使用Messenger跨进程发送消息
首先看下Messenger的代码实现
即知public final class Messenger implements Parcelable { private final IMessenger mTarget; public Messenger(Handler target) { mTarget = target.getIMessenger(); } public void send(Message message) throws RemoteException { mTarget.send(message); } public IBinder getBinder() { return mTarget.asBinder(); } 。。。省略代码 public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); } }
- 通过handler可以创建Messenger
- 通过IBinder可以创建Messenger
- Messenger可以发送消息
- getBinder可以返回IBinder类型
MessengerService代码如下
通过上面代码可知:public class MessengerService extends Service { private Messenger mainMessenger; private Messenger serviceMessenger = new Messenger(new IncomingHandler()); @Nullable @Override public IBinder onBind(Intent intent) { logs("onBind"); return serviceMessenger.getBinder(); } private class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { logs("IncomingHandler->handleMessage , 我们要向Activity发送消息了"); Toast.makeText(MessengerService.this, "MessengerService->handleMessage", Toast.LENGTH_LONG).show(); mainMessenger = msg.replyTo; Message msg2 = Message.obtain(); try { mainMessenger.send(msg2); } catch (RemoteException e) { e.printStackTrace(); } } } public void logs(String text) { Log.i("jue","MessengerService -> " + text); } }
- Service通过Handler创建了Messenger
- onBind方法返回的是Messenger.getBinder()
- 在Service中得到消息之后,通过Messge.replyTo获得了activity线程的messenger
Activity代码
public class MessengerActivity extends AppCompatActivity implements View.OnClickListener { private boolean mIsBind; private Messenger mServiceMessenger; private TextView mTitle; private Button mBind; private Button mUnBind; private Button mDataGet; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_messenger); mTitle = (TextView) findViewById(R.id.top0); mBind = (Button) findViewById(R.id.bind); mUnBind = (Button) findViewById(R.id.bund); mDataGet = (Button) findViewById(R.id.get_bind); mBind.setOnClickListener(this); mUnBind.setOnClickListener(this); mDataGet.setOnClickListener(this); } @Override public void onClick(View v) { if (mBind == v) { bindService(); return; } if (mUnBind == v) { unbindService(); return; } if (mDataGet == v) { getDataFromService(); return; } } private void unbindService() { if (!mIsBind) { logs("onClick -> unbindService 已经解除绑定,return"); return; } logs("onClick -> unbindService"); unbindService(connection); } private void bindService() { if (mIsBind) { logs("onClick -> bindService 已经绑定,return"); return; } logs("onClick -> bindService"); Intent intent = new Intent(MessengerActivity.this, MessengerService.class); bindService(intent, connection, BIND_AUTO_CREATE); } public void getDataFromService() { logs("onClick -> getDataFromService"); Message msg = Message.obtain(); msg.replyTo = mMainMessenger; try { mServiceMessenger.send(msg); } catch (Exception e) { } } public ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mIsBind = true; mServiceMessenger = new Messenger(service); logs("ServiceConnection -> onServiceConnected "); } @Override public void onServiceDisconnected(ComponentName name) { mIsBind = false; logs("ServiceConnection -> onServiceDisconnected "); } }; public static void logs(String text) { Log.i("jue","MessengerActivity-> " + text); } private Messenger mMainMessenger = new Messenger(new MyHandler()); private class MyHandler extends Handler { @Override public void handleMessage(Message msg) { mTitle.setText("我们收到了来自跨进程service的消息"); } } }
Activity中
- 当service connected时,使用ibinder对象创建了Messenger。
- 使用Service的Messenger发送消息时,通过设定Message.replyTo来传递当前进程的Messenger给Service.
当向MessengerService发送多个耗时的消息时,MessengerService的收到的消息会依次执行。
log输出public class MessengerService extends Service { private Messenger mainMessenger; private Messenger serviceMessenger = new Messenger(new IncomingHandler()); @Nullable @Override public IBinder onBind(Intent intent) { logs("onBind"); return serviceMessenger.getBinder(); } private class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { logs("IncomingHandler->handleMessage " + msg.what + " , thread[" + Thread.currentThread().getName() + "] start "); try { Thread.sleep(12000); } catch (Exception e) { } logs("IncomingHandler->handleMessage " + msg.what + " , thread[" + Thread.currentThread().getName() + "] end "); } } }
bindService中, onRebind方法的调用
Android文档的图
onRebind执行的条件
- Service 被startService启动了,但未被销毁。
- Service被startService启动后,执行了bindServcie,unBindService,并且Service的onUnbind 返回true。
- 步骤1,2后,再次执行了bindService