Service组件
身为四大组件之一的Service在程序中使用频率是比较高的,主要用来处理一些不需要UI的耗时的后台操作,或者在后台对外提供接口,供其他组件调用。Service的实现是比较典型的C/S模式,后文介绍用法时会有体会。
两种常见的Service
- IntentService:适合同一时间只处理一个任务,代码少,使用简单
是Service类的子类,默认会开启一个工作线程,你需要覆盖
onHandleIntent
方法,用来处理startService
传过来的Intent,在一个生命周期内,如果多次调用startService
只会进一次onCreate(),但是会进对应次数的onHandleIntent
,在同一时间只能处理一个Intent,也就是说不管调用多少次startService
只有一个工作线程在工作,其余的排队等待,一旦所有的Intent处理完成,自动调用onDestroy
方法,无须手动调用stopService
或者stopSelf
IntentService的示例
public class MyIntentService extends IntentService { private static final String TAG = "MyIntentService"; public static final String ACTION_ONE = "action_one"; public static final String ACTION_TWO = "action_two"; /** * A constructor is required, and must call the super IntentService(String) * constructor with a name for the worker thread. */ public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { if (intent != null) { final String action = intent.getAction(); if (ACTION_ONE.equals(action)) { handleActionOne(); } else if (ACTION_TWO.equals(action)) { handleActionTwo(); } } } private void handleActionOne(){ for (int i = 0;i<3;i++){ Log.i(TAG,"handleActionOne"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } private void handleActionTwo(){ for (int i = 0;i<3;i++){ Log.i(TAG,"handleActionTwo"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void onCreate() { super.onCreate(); Log.i(TAG,"onCreate"); } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG,"onDestroy"); } }
可以通过如下方法调用多次同时调用
Intent intent = new Intent(this,MyIntentService.class); intent.setAction(MyIntentService.ACTION_ONE); //intent.setAction(MyIntentService.ACTION_TWO); startService(intent);
调用后我们会发现IntentService不需要我们另起线程来执行耗时的操作,同时你无论触发多少次ACTION_ONE或者ACTION_TWO动作,它们都会按触发的顺序顺序执行,且任务一旦执行完成无需我们调用stopService或者stopSelf就会自己进入onDestory
- Service:适合并发请求,代码较多,较复杂,更灵活
需要手动调用
stopService
或者执行完任务后调用stopSelf
,即使Service销毁了,其中的线程如果没有执行完会继续执行,如果不掉用的话即使启动该Service的activity销毁了,该Service仍然存在,系统内存不够时会销毁后台的service,service存在时间越长,被销毁的可能性越大Service的示例
public class MyService extends Service { private static final String TAG = "MyService"; public MyService() { } @Override public IBinder onBind(Intent intent) { //暂时用不到 return null; } @Override public void onCreate() { super.onCreate(); Log.i(TAG,"onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { MyServiceThread thread = new MyServiceThread(startId); thread.start(); // If we get killed, after returning from here, restart return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG,"onDestroy"); } class MyServiceThread extends Thread{ int startId = 0; public MyServiceThread(int startId){ this.startId = startId; } @Override public void run() { for(int i = 0;i<3;i++){ Log.i(TAG,"my startId:"+startId); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } //根据startId停止,能保证最近一次的请求执行完才销毁 stopSelf(startId); } } }
我们可以通过startService多次调用,但运行完需要显式的调用stopSelf来结束自己,在这里例子中我们可以看到在Service中需要我们自己启动线程来执行耗时的任务,可以同时并行的执行多个任务,还能看到startId的用处,可以用来保证最近的一次请求执行完才onDestory
service使用方式
- startService
可以直接看上面的两个例子
- bindService
- 继承binder类
如果只是想利用Service作为你应用的一个后台工作线程的话,这是首选的实现方式,除非你的Service需要被其他应用使用或者跨进程通讯。
先看Service代码
public class MyLocalService extends Service { private static final String TAG = "MyLocalService"; // Binder given to clients private final IBinder mBinder = new LocalBinder(); // Random number generator private final Random mGenerator = new Random(); //实现Binder类,是用来在onBind返回,客户端可以在 //onServiceConnected()回调方法内获取该对象的实例, //进而通过该实例的getService()方法获得Service的引用 //最后就可以通过调用Service的Public方法来和Service通讯 public class LocalBinder extends Binder{ MyLocalService getService(){ // Return this instance of LocalService so clients can call public methods return MyLocalService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } /** method for clients */ public int getRandomNumber(){ return mGenerator.nextInt(100); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG,"onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public boolean onUnbind(Intent intent) { Log.i(TAG,"onUnbind"); return super.onUnbind(intent); } @Override public void onRebind(Intent intent) { Log.i(TAG,"onRebind"); super.onRebind(intent); } @Override public void onCreate() { Log.i(TAG,"onCreate"); super.onCreate(); } @Override public void onDestroy() { Log.i(TAG,"onDestroy"); super.onDestroy(); } }
再看activity代码
public class ServiceMainActivity extends Activity implements View.OnClickListener{ private final static String TAG = "ServiceMainActivity"; //报存获取的Service的引用 MyLocalService mService; boolean mBound = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_service_main); btnStartBind.setOnClickListener(this); btnStopBind.setOnClickListener(this); btnGetData.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btnStartBind: startBind(); return; case R.id.btnStopBind: stopBind(); return; case R.id.btnGetBindData: getBindData(); return; } } /** 定义Service绑定的回调方法,就是通过这个回调方法来实现与Service的绑定的 */ private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // We‘ve bound to LocalService, cast the IBinder and get LocalService instance Log.i(TAG,"onServiceConnected"); MyLocalService.LocalBinder binder = (MyLocalService.LocalBinder)service; mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName name) { Log.i(TAG,"onServiceDisconnected"); mBound = false; } }; private void startBind(){ Intent intent = new Intent(this,MyLocalService.class); bindService(intent,mConnection, Context.BIND_AUTO_CREATE); } private void stopBind(){ if (mBound){ unbindService(mConnection); mBound = false; } } private void getBindData(){ if(mBound){ // 调用Service的方法 // 这里注意要避免调用Service里可能导致挂起的方法 //如果要调用的话需要在独立的线程里调用,避免影响UI线程的性能 int random = mService.getRandomNumber(); Toast.makeText(this,"random number:"+random,Toast.LENGTH_SHORT).show(); } } }
使用的步骤总结
- 在你的Service中创建一个Binder的实现,包含一个可以被客户端调用的公共方法,这个公共方法需要返回这个Service的实例或者Service中的其他内部类的实例
- 在
onBind
方法中返回你实现的Binder的实例 - 客户端通过
onServiceConnected()
回调方法获取你实现的Binder实例后就可以调用在Service中提供的方法了
- 使用Messenger
这是一种简单的实现跨进程通讯方式,因为使用 Messenger queues来处理所有的请求,而所有的请求都在一个线程中,所以你不需要关心Service的线程安全的问题。但是这所有的请求也都是顺序被执行,这种方式应该能满足绝大部分的需求。
先看Service的代码
public class MessengerService extends Service { private static final String TAG = "MessengerService"; /** Command to the service to display a message */ static final int MSG_SAY_HELLO = 1; /** * 用于存放客户端的Messenger,可以通过这个实例响应客户端,这里只响应一个客户端 * 可以用集合存储,响应多个客户端 */ Messenger clientMessenger; /** * Command to the service to register a client, receiving callbacks from the * service. The Message‘s replyTo field must be a Messenger of the client * where callbacks should be sent. */ static final int MSG_REGISTER_CLIENT = 2; /** * Handler of incoming messages from clients. */ class IncomingHandler extends Handler{ @Override public void handleMessage(Message msg) { switch (msg.what){ case MSG_SAY_HELLO: // Toast.makeText(getApplicationContext(),"Hello!",Toast.LENGTH_SHORT).show(); Log.i(TAG,"Hello!"); if(clientMessenger != null){ Message message = Message.obtain(null,MSG_SAY_HELLO); try { clientMessenger.send(message); } catch (RemoteException e) { e.printStackTrace(); } } break; case MSG_REGISTER_CLIENT: clientMessenger = msg.replyTo; break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ final Messenger mMessenger = new Messenger(new IncomingHandler()); /** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */ @Override public IBinder onBind(Intent intent) { Log.i(TAG,"binding"); return mMessenger.getBinder(); } }
activity的代码
public class MessengerActivity extends ActionBarActivity implements View.OnClickListener{ private final static String TAG = "MessengerActivity"; /** Messenger for communicating with the service. */ Messenger mService = null; /** Flag indicating whether we have called bind on the service. */ boolean mBound; class IncomingHandler extends Handler{ @Override public void handleMessage(Message msg) { switch (msg.what){ case MessengerService.MSG_SAY_HELLO: Log.i(TAG,"response Hello!"); break; default: super.handleMessage(msg); } } } final Messenger mMessenger = new Messenger(new IncomingHandler()); /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // This is called when the connection with the service has been // established, giving us the object we can use to // interact with the service. We are communicating with the // service using a Messenger, so here we get a client-side // representation of that from the raw IBinder object. mService = new Messenger(service); mBound = true; Message msg = Message.obtain(null,MessengerService.MSG_REGISTER_CLIENT); msg.replyTo = mMessenger; try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { mService = null; mBound = false; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_messenger); Button btnBindService = (Button)findViewById(R.id.btnBindService); Button btnSayHello = (Button) findViewById(R.id.btnSayHello); Button btnUnbindService = (Button) findViewById(R.id.btnUnbindService); btnBindService.setOnClickListener(this); btnSayHello.setOnClickListener(this); btnUnbindService.setOnClickListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_messenger, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btnBindService: doBindService(); break; case R.id.btnSayHello: sayHello(); break; case R.id.btnUnbindService: doUnbindService(); break; } } private void sayHello(){ if(!mBound) return; // Create and send a message to the service, using a supported ‘what‘ value Message msg = Message.obtain(null,MessengerService.MSG_SAY_HELLO); try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } private void doBindService(){ Intent intent = new Intent(this,MessengerService.class); bindService(intent,mConnection, Context.BIND_AUTO_CREATE); mBound = true; } private void doUnbindService(){ if(mBound){ unbindService(mConnection); mBound = false; } } }
这里实现了activity和service通过Messenger来进行通讯,在Manifest对应的Service中添加android:process=”:remote”,发现依然可以通讯,证明了这是一种可以跨进程的通讯方式。
使用步骤总结
- Service实现一个Handler,接收客户端发过来的Message
- 上面实现的Handler被用来创建一个Messenger对象,这个对象其实就是Handler的引用
- 这个Messenger对象可以用来在
onBind()
方法中创建一个IBinder对象用于return。 - 客户端在
onServiceConnected()
回调方法中利用IBinder来实例化客户端中的Messenger,然后就可以调用Messenger实例的send方法,来发送Message到绑定的Service了 - Service可以在实现的Handler类中的
handleMessage()
方法更具msg.what
来处理客户端不同类型的Message了 - 以上只是客户端->Service单向的通讯,如果想让客户端接收Service响应的消息,可以在客户端中创建一个Handler,并用这个Handler创建Messenger对象,在
onServiceConnected()
回调方法中通过Message的replyTo发送给Service,Service保留收到的客户端的Messenger,利用该对象的send方法给客户端发送Message,客户端可以在Handler的回调方法handleMessage()
中根据msg.what来不处理不同类型的消息
需要注意的是:只有activity,service和content provider能够绑定服务,而broadcast receiver 不能够绑定
客户端使用步骤总结
- 实现ServiceConnection类,覆写其中的两个回调方法
onServiceConnected()
当和Service绑定后会回调这个方法,需要注意的是bindService()
方法是异步的,Service中的onBind()
方法return的IBinder是通着onServiceConnected()
传递到客户端的onServiceDisconnected()
这个方法是当Service意外的丢失了而由android系统调用的,比如Service崩溃了或者被系统kill掉了,当客户端调用unbindService()
方法该回调方法是不会被调用的
- 调用
bindService()
方法 - 当
onServiceConnected()
方法调用后你就与Service建立了联系,可以调用Service提供的接口跟Service进行通讯了 - 调用
unbindService()
完成通讯
以上代码大部分是参照的google提供的示例,自己修改部分代码及注释。个人感觉先看代码,能知道每个方法是做什么用的,最后再来总结一下实现方式,能加深理解记忆
- 使用AIDL
其实上面的Messenger底层也是基于AIDL实现的,使用这种方式就更灵活,适合于处理多个需要同时运行的请求,但也带来了复杂性,需要你自己维护Service的线程安全。这里先不详细介绍该用法了。
- 继承binder类
- startService and bindService
就是Service即实现了
onBind()
又实现了onStartCommand()
方法,使用的话把上面的两个例子合起来就行了。这里唯一要注意的就是Service的生命周期,下文中会提到。
Service和Thread
- 使用场景
- Service通常是运行在后台的,即使用户与应用没有任何交互。且Service可以供多个客户端使用,甚至是其他进程的其他应用。
- 如果你仅仅是在用户与应用交互的时候需要在主线程外执行一些操作, 这个时候可以用Thread。比如说,只有当你的activity运行的时候才需要播放一段音乐,这个时候你就可以在
onCreate()
方法中创建线程,onStop()
方法中关闭线程,在Android一般不使用原生的Thread,因为android给我们准备了更好用的 AsyncTask 或者 HandlerThread类供我们使用
你启动的Service默认是运行在应用进程的主线程中的(remote service是运行在自己的进程中),所以不要直接在service中运行资源密集或者阻塞操作,而是需要在service中启动一个线程去执行这些操作
生命周期
- 先引用一下官网的图
可以看到不管是startService还是bindService,通用的都有
onCreate()
和onDestory
且在一个生命周期内只会调用一次onCreate()
和onDestory
。 - 以表格形式总结一下
service | onCreate | onStartCommand | onbind | onDestory |
---|---|---|---|---|
start Service | startService触发,只调用一次 | startService触发,多次启动调用多次 | / | stopService或stopSelf或被系统kill触发,只调用一次 |
bind Service | bindService触发,只调用一次 | / | bindService触发,一个客户端只触发一次 | 所有bind的客户端都调用了unbindService或者所有的客户端都不存在了触发,只触发一次 |
start&bind Service | bindService或startService触发,只调用一次 | startService触发,多次启动调用多次 | bindService触发,一个客户端只触发一次 | 调用stopService且所有bind的客户端都调用了unbindService或者所有的客户端都不存在了才触发,只触发一次 |
- bindService更详细的生命周期
如果你想让客户端下次再绑定Service的时候调用
onRebind()
方法的话,可以覆写onUnbind()
方法,使其返回true,这样下次客户端再调用bindService的时候就会进onUnbind()
方法,而不再进onBind()
方法了,虽然onUnbind()
方法是renrun void的,但是客户端仍然能够通过onServiceConnected()
回调方法获取到IBinder对象。在官网上有更详细的生命周期配图,如下