Android中Service的四种使用方式
Service是Android中的四大组件之一,用于在后台进行长时间作业,不提供用户界面。Service可以直接由其他的应用程序组件启动并在后台运行,也可以与其他组件绑定来实现进程间通信。本文介绍了创建Service的基本方法,并通过实例描述了Service的四种使用方式,包括Service的启动和绑定,以及IntentService和AIDL Service的使用。
Contents
0. 创建Service
创建一个Service有两个步骤:实现Service子类,以及在manifest文件中声明该Service。
0.1. 实现Service子类
与Activity类似,实现自己的Service子类时,需要重写一些回调方法,来管理Service的生命周期,如onStartCommand()、onBind()、onCreate()、onDestroy()等。需要注意的是,onBind()是必须要重写的。举例来说,一个Service的简单实现如[代码0-1]所示,这个Service开启了一个线程来对count 进行累加和打印。
public class MyService extends Service { private static final String TAG = "MyService"; private int count; private boolean quit; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "Service is created."); count = 0; quit = false; new Thread() { @Override public void run() { while (!quit) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count++; Log.i(TAG, "count = " + count); } } }.start(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "Service is started."); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); quit = true; Log.i(TAG, "Service is destroyed."); } @Override public IBinder onBind(Intent intent) { return null; } }
0.2. 在manifest文件中声明
在manifest文件中声明Service的方法也与Activity类似,只需将<service…/> 插入到<application> 元素下面即可,如[代码0-2]所示。
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application> </manifest>
关于<service> 下的更多配置可以参考这里。Google的教程中指出,从安全角度考虑,在启动服务或绑定服务时尽量显式地指出所要使用的Service,而不要通过向Service加入Intent Filters来指明可以启动该Service的Intent。
1. 启动Service
Service的启动可以通过调用Context中的startService()实现,相对应的,stopService()用于停止Service。
新建一个Activity,命名为MyActivity;再新建一个如[代码0-1]所示的MyService,并按照[代码0-2]的形式在AndroidManifest.xml文件中声明MyService。在MyActivity中加入两个按钮btnStartService 和btnStopService ,分别用于启动和停止服务,如[代码1-1]所示。
public class MyActivity extends Activity { private static final String TAG = "MyActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); final Intent serviceIntent = new Intent(this, MyService.class); Button btnStartService = (Button)findViewById(R.id.btnStartService); btnStartService.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { startService(serviceIntent); } }); Button btnStopService = (Button)findViewById(R.id.btnStopService); btnStopService.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { stopService(serviceIntent); } }); } }
运行MyActivity,点击按钮启动Service,然后再点击按钮停止Service,可以看到打印信息:
10-21 21:44:14.868 27063-27063/com.nex3z.servicetest I/MyService﹕ Service is created. 10-21 21:44:14.868 27063-27063/com.nex3z.servicetest I/MyService﹕ Service is started. 10-21 21:44:15.869 27063-27296/com.nex3z.servicetest I/MyService﹕ count = 1 10-21 21:44:16.870 27063-27296/com.nex3z.servicetest I/MyService﹕ count = 2 10-21 21:44:17.871 27063-27296/com.nex3z.servicetest I/MyService﹕ count = 3 10-21 21:44:18.802 27063-27063/com.nex3z.servicetest I/MyService﹕ Service is destroyed.
可见Service能够正常地启动与停止。连续两次点击按钮启动Service,可以看到打印信息:
10-21 21:46:05.866 27733-27733/com.nex3z.servicetest I/MyService﹕ Service is created. 10-21 21:46:05.866 27733-27733/com.nex3z.servicetest I/MyService﹕ Service is started. 10-21 21:46:06.877 27733-28241/com.nex3z.servicetest I/MyService﹕ count = 1 10-21 21:46:07.638 27733-27733/com.nex3z.servicetest I/MyService﹕ Service is started. 10-21 21:46:07.868 27733-28241/com.nex3z.servicetest I/MyService﹕ count = 2
由此可以看出,startService()首先会通过Service的onCreate()来生成Service,然后通过onStartCommand()来启动Service。重复启动同一个Service,onStartCommand()会被重复调用,而onCreate()只会执行一次。
2. 绑定Service
在上面的例子中,Activity仅仅是控制了Service的启动和停止,如果要实现与Service之间的数据交换,就需要将Service与Activity进行绑定,由Service需要向Activity提供访问其自身数据的方法。
新建一个Service,命名为MyBindService,其内容与[代码0-1]所示的MyService基本一致,仅仅是更改了TAG 并去掉了对count 的打印,如[代码2-1]所示。
public class MyBindService extends Service { private static final String TAG = "MyBindService"; private int count; private boolean quit; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "Service is created."); count = 0; quit = false; new Thread() { @Override public void run() { while (!quit) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count++; } } }.start(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "Service is started."); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); quit = true; Log.i(TAG, "Service is destroyed."); } @Override public IBinder onBind(Intent intent) { return null; } }
为了让MyBindService向MyActivity提供访问其自身数据的方法,需要确实地实现MyBindService的onBind()方法,并在MyActivity中对MyBindService进行绑定。
2.1. 实现onBind()方法
虽然[代码2-1]所示的MyBindService中也实现了onBind()方法,但只是返回了一个null 值,没有实际意义。这里需要修改让onBind()返回一个IBinder对象,这个IBinder对象会在MyService(或其他组件)绑定MyBindService的时候,由MyBindService返回给MyService,MyService可以通过这个IBinder对象与MyBindService进行通信。
实现IBinder时一般会直接继承Binder,它是IBinder的实现。这里实现的MyBinder如[代码2-2]所示,它提供了一个getCount()方法,返回了MyBindService中的count 。
public class MyBinder extends Binder { public int getCount() { return count; } }
接下来为MyBindService添加成员private MyBinder binder = new MyBinder(); ,并修改MyBindService的onBind()方法返回binder ,如[代码2-3]所示。
@Override public IBinder onBind(Intent intent) { Log.i(TAG, "Service is binded."); return binder; }
到这里便完成了MyBindService,完整代码如[代码2-4]所示。
public class MyBindService extends Service { private static final String TAG = "MyBindService"; private int count; private boolean quit; private MyBinder binder = new MyBinder(); @Override public void onCreate() { super.onCreate(); Log.i(TAG, "Service is created."); count = 0; quit = false; new Thread() { @Override public void run() { while (!quit) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count++; } } }.start(); } public class MyBinder extends Binder { public int getCount() { return count; } } @Override public IBinder onBind(Intent intent) { Log.i(TAG, "Service is binded."); return binder; } @Override public boolean onUnbind(Intent intent) { Log.i(TAG, "Service is unbinded."); return true; } @Override public void onDestroy() { super.onDestroy(); quit = true; Log.i(TAG, "Service is destroyed."); } }
2.2. Service的绑定
Service的绑定是通过bindService()实现的,其完整签名为:
public abstract boolean bindService (Intent service, ServiceConnection conn, int flags)
其中service 指明所要绑定的Service,conn 是一个ServiceConnection对象,用于接收Service启动和停止的信息,flags 用于设置绑定的参数,比如是否在绑定时自动创建Service(0表示不自动创建,BIND_AUTO_CREATE 表示自动创建等,其他选项可以参考API文档)。
为了完成绑定,需要实现bindService()参数中的ServiceConnection,它用于监听所绑定Service的连接状态:当连接成功时,将回调ServiceConnection对象的onServiceConnected()方法,Service中onBind()方法返回的IBinder会作为参数传递给onServiceConnected();当连接意外中断时,则回调onServiceDisconnected()。
回到例子的具体实现上,首先为MyActivity添加成员private MyBinder binder = new MyBinder(); 用于在本地保存MyBindService返回的IBinder,然后实现ServiceConnection如[代码2-5]所示。
private ServiceConnection serviceConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { binder = (MyBindService.MyBinder) iBinder; Log.i(TAG, "Service is connected."); } @Override public void onServiceDisconnected(ComponentName componentName) { Log.i(TAG, "Service is disconnected."); } };
这里onServiceConnected()将作为参数传递过来的iBinder 保存为本地的binder ,onServiceDisconnected()只进行了打印。
在MyActivity中加入三个按钮:btnBindService 用于绑定MyBindService,btnStatus 用于读取MyBindService中count 的值,btnBindService 用于解绑MyBindService。最终结果如[代码2-6]所示。
public class MyActivity extends Activity { private static final String TAG = "MyActivity"; private MyBindService.MyBinder binder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); final Intent bindServiceIntent = new Intent(this, MyBindService.class); Button btnBindService = (Button)findViewById(R.id.btnBindService); btnBindService.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { bindService(bindServiceIntent, serviceConn, Service.BIND_AUTO_CREATE); } }); Button btnStatus = (Button)findViewById(R.id.btnStatus); btnStatus.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { Toast.makeText(MyActivity.this, "count = " + binder.getCount(), Toast.LENGTH_SHORT).show(); Log.i(TAG, "count = " + binder.getCount()); } }); Button btnUnbindService = (Button)findViewById(R.id.btnUnbindService); btnUnbindService.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { unbindService(serviceConn); } }); } private ServiceConnection serviceConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { binder = (MyBindService.MyBinder) iBinder; Log.i(TAG, "Service is connected."); } @Override public void onServiceDisconnected(ComponentName componentName) { Log.i(TAG, "Service is disconnected."); } }; }
其中第17行通过bindService(bindServiceIntent, serviceConn, Service.BIND_AUTO_CREATE); ,使MyActivity与bindServiceIntent 所指定的MyBindService进行了绑定,由serviceConn 管理连接,Service.BIND_AUTO_CREATE 表示在绑定时自动创建Service。
运行MyActivity,点击btnBindService 绑定MyBindService后,点击btnStatus 读取MyBindService中count 的数值,再点击btnBindService 解除绑定,得到打印信息:
10-18 18:34:38.874 4492-4492/com.nex3z.servicetest I/MyBindService﹕ Service is created. 10-18 18:34:38.894 4492-4492/com.nex3z.servicetest I/MyBindService﹕ Service is binded. 10-18 18:34:38.894 4492-4492/com.nex3z.servicetest I/MyActivity﹕ Service is connected. 10-18 18:34:41.547 4492-4492/com.nex3z.servicetest I/MyActivity﹕ count = 3 10-18 18:34:44.260 4492-4492/com.nex3z.servicetest I/MyActivity﹕ count = 6 10-18 18:34:46.762 4492-4492/com.nex3z.servicetest I/MyBindService﹕ Service is unbinded. 10-18 18:34:46.762 4492-4492/com.nex3z.servicetest I/MyBindService﹕ Service is destroyed.
可见,MyActivity可以获取到MyBindService中count 的数值。在绑定服务时,首先执行的是Service中的onCreate()和onBind()方法,然后才会回调在绑定Service时所指定的ServiceConnection对象的onServiceConnected()方法。解除绑定时,onServiceDisconnected()方法没有执行,这是因为上面的例子是通过unBindService()来主动解除绑定的,onServiceConnected()只有在Service所在的宿主进程意外终止导致连接断开时才会被调用。
3. IntentService
在前面的例子中,Activity所启动的Service与Activity在同一个进程中运行,而且Service也不会自动为自己的业务新建线程,如果在Service中直接处理耗时任务,就可能导致应用程序无响应(Application Not Responding,ANR )。所以在[代码0-1]和[代码2-4]中,都是在Service的onCreate()中手动新建了线程对count 进行计数。
IntentService是Service的子类,顾名思义,客户端使用startService()通过Intent请求启动IntentService,IntentService使用队列对客户端发来的Intent进行管理,并开启新线程来处理Intent。
一个例子如[代码3-1]所示,IntentService为onBind()和onStartCommand()提供了默认实现,所以只重写了onHandleEvent()。MyIntentService进行了30秒的计时和打印。
public class MyIntentService extends IntentService { private static final String TAG = "MyIntentService"; public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { Log.i(TAG, "Count start."); int count = 1; while (count <= 30) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Log.i(TAG, "count = " + count); count++; } Log.i(TAG, "Count complete."); } }
为了与IntentService对比,建立如[代码3-2]所示的普通Service,CounterService同样是进行30秒的计时和打印,但没有手动新建线程。
public class CounterService extends Service { private static final String TAG = "CounterService"; @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "Count start."); int count = 1; while (count <= 30) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Log.i(TAG, "count = " + count); count++; } Log.i(TAG, "Count complete."); return START_STICKY; } @Override public IBinder onBind(Intent intent) { return null; } }
执行MyIntentService,打印结果如下:
... 10-25 15:59:29.529 6271-6990/com.nex3z.servicetest I/MyIntentService﹕ count = 27 10-25 15:59:30.530 6271-6990/com.nex3z.servicetest I/MyIntentService﹕ count = 28 10-25 15:59:31.541 6271-6990/com.nex3z.servicetest I/MyIntentService﹕ count = 29 10-25 15:59:31.541 6271-6990/com.nex3z.servicetest I/MyIntentService﹕ Count complete.
可见MyIntentService顺利完成了计时。而执行CounterService时,打印结果如下:
... 10-25 16:02:02.128 6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 19 10-25 16:02:03.129 6271-6276/com.nex3z.servicetest I/dalvikvm﹕ threadid=3: reacting to signal 3 10-25 16:02:03.159 6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 20 10-25 16:02:03.159 6271-6276/com.nex3z.servicetest I/dalvikvm﹕ Wrote stack traces to '/data/anr/traces.txt' 10-25 16:02:04.160 6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 21 10-25 16:02:05.161 6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 22 10-25 16:02:06.162 6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 23 10-25 16:02:07.153 6271-6271/com.nex3z.servicetest I/CounterService﹕ count = 24 10-25 16:02:07.544 6271-6271/com.nex3z.servicetest A/libc﹕ Fatal signal 6 (SIGABRT) at 0x0000027b (code=0), thread 6271 (x3z.servicetest)
在CounterService开始执行后,应用便会失去响应,执行时间过长还会出现ANR。
4. AIDL Service
如前所述,通过绑定Service可以实现Service与其他组件之间的数据交换,但Android系统中的进程之间一般无法直接交换数据,为了让Service能够跨进程与其他组件进行通信,就需要使用AIDL(Android Interface Definition Language)。
下面依旧通过一个例子说明AIDL Service的使用。例子内容与前面类似,依旧是在Service中进行计数,由Activity读取Service中计数的值,不过这次Activity与Service不在处于同一进程,Service需要定义一个远程调用接口,并提供实现,Activity远程调用Service的接口,与Service进行通信。
4.1. 编写AIDL文件
AIDL用于定义远程接口,其详细使用方法可以参考官方文档,这里只是通过例子给出一个简单的示范,建立一个MyAIDLInterface.aidl文件,如[代码4-1]所示。
interface IMyAidlInterface { int getCount(); }
可以看出,AIDL的语法与Java类似,上面的例子声明了一个getCount()方法,该方法返回一个int型的值。如果使用Eclipse,上面的AIDL文件保存之后,就可以在gen目录下找到一个自动生成的MyAidlInterface.java文件;如果使用的是Android Studio,则需要先“Make Project”,然后就可以在build/generated/source/aidl下找到自动生成的MyAidlInterface.java文件。MyAidlInterface包含一个内部类Stub,该内部类继承了Binder,并实现了IMyAidlInterface。由于Stub继承了Binder,可以将Stub的实例作为Service的onBind()方法的返回值,提供给远程组件;由于与Stub实现了IMyAidlInterface,使得Stub可以向远程组件提供MyAIDLInterface.aidl中定义的接口。
4.2. 建立Service
MyAIDLInterface.aidl文件中声明了接口getCount(),接下来就要给出getCount()的实现,如代码[4-2]所示。
public class MyAidlServiceBinder extends IMyAidlInterface.Stub { @Override public int getCount() throws RemoteException { return count; } }
声明MyAidlServiceBinder继承IMyAidlInterface.Stub,并实现getCount()返回count ——Service中的计数值。
然后为Service添加成员private MyAidlServiceBinder binder; ,并通过onBind()返回binder ,如[代码4-3]所示。
@Override public IBinder onBind(Intent intent) { return binder; }
最终得到的Service如[代码4-4]所示:
public class MyAidlService extends Service { private static final String TAG = "MyAidlActivity"; private int count; private boolean quit; private MyAidlServiceBinder binder; public class MyAidlServiceBinder extends IMyAidlInterface.Stub { @Override public int getCount() throws RemoteException { return count; } } @Override public IBinder onBind(Intent intent) { return binder; } @Override public void onCreate() { super.onCreate(); Log.i(TAG, "Service is created."); binder = new MyAidlServiceBinder(); count = 0; quit = false; new Thread() { @Override public void run() { while (!quit) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count++; } } }.start(); } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG, "Service is destroyed."); } }
在onCreate()中,通过binder = new MyAidlServiceBinder(); 创建了MyAidlServiceBinder的实例,供onBind()作为返回值。此时可以先运行MyAidlService,供后面MyActivity进行远程调用。
4.3. 远程调用
依旧使用MyActivity远程调用MyAidlService。为MyActivity添加成员private IMyAidlInterface aidlService; ,添加按钮btnBindAidlService 和btnUnbindAidlService 分别用于绑定Service和解绑Service,如[代码4-5]所示。
final Intent bindAidlServiceIntent = new Intent(this, MyAidlService.class); Button btnBindAidlService = (Button)findViewById(R.id.btnBindAidlService); btnBindAidlService.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { bindService(bindAidlServiceIntent, aidlServiceConn, Service.BIND_AUTO_CREATE); } }); Button btnUnbindAidlService = (Button)findViewById(R.id.btnUnbindAidlService); btnUnbindAidlService.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { unbindService(aidlServiceConn); } });
可以看出,绑定和解绑AIDL Service的流程与绑定普通Service类似,第7行中绑定Service时使用的aidlServiceConn 如[代码4-6]所示。
private ServiceConnection aidlServiceConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { aidlService = IMyAidlInterface.Stub.asInterface(iBinder); Log.i(TAG, "AIDL Service is connected."); } @Override public void onServiceDisconnected(ComponentName componentName) { aidlService = null; Log.i(TAG, "AIDL Service is disconnected."); } };
在onServiceConnected()的第4行,把通过参数传递过来的iBinder 保存到本地。注意这里使用了IMyAidlInterface.Stub.asInterface(iBinder) ,而不是直接像[代码2-6]中那样直接对iBinder 进行类型转换,这是因为绑定远程Service的ServiceConnection不能直接获取Service中onBind()方法返回的对象,而只能通过IMyAidlInterface.Stub.asInterface(iBinder) 获取onBind()返回对象的代理。
为MyActivity添加按钮btnStatusAidl 用于读取并打印MyAidlService中的count 值,如代码[4-7]所示。
Button btnStatusAidl = (Button)findViewById(R.id.btnStatusAidl); btnStatusAidl.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { try { Toast.makeText(MyActivity.this, "count = " + aidlService.getCount(), Toast.LENGTH_SHORT).show(); Log.i(TAG, "count = " + aidlService.getCount()); } catch (RemoteException e) { e.printStackTrace(); } } });
直接通过aidlService.getCount() 就实现了远程调用,获取了MyAidlService中count 的值,打印结果如下:
... 10-25 17:15:00.780 9505-9505/com.nex3z.servicetest I/MyActivity﹕ count = 2 10-25 17:15:02.392 9505-9505/com.nex3z.servicetest I/MyActivity﹕ count = 4
可见,MyActivity成功与MyAidlService进行了通信。