本文主要介绍Service的概念及作用、使用(包括长时间运行的服务、应用内交互的服务、应用间交互的服务的分别举例、生命周期、使用场景)、特殊的Service。
示例代码见ServiceDemo,示例APK见:TrineaAndroidDemo.apk。
由于ANR对Activity和BroadcastReceiver响应时间的限制(Activity对事件响应不超过5秒,BroadcastReceiver执行不超过10秒),使得在其中都不适合执行较耗时操作,这样像网络、数据库、复杂计算这类耗时操作的执行就需要一个组件来承担。Service作为Android四大组件之一,其功能之一就是耗时操作的执行,主要功能如下:
a. 执行需要长时间运行的操作,这个操作不与用户进行交互,如网络下载、大文件I/O、复杂计算。
b. 应用内或应用间数据通信,Android每个应用程序都在自己的dalvik虚拟机中运行,一个应用是不允许访问其他应用的内存信息的,为此Android引入了Content Provider在不同应用间共享数据,BroadcastReceiver广播信息给不同应用程序,但Content Provider更多用于数据的共享,BroadcastReceiver广播的信息会被所有应用接收较耗费系统资源,对于两个应用间动态的进行交互还需要通过Service来完成。
2、使用
(1) startService启动不可进行交互的Service
a. 示例代码及介绍
public class MyService extends Service { @Override public void onCreate() { super.onCreate(); Toast.makeText(this, "Service Create", Toast.LENGTH_SHORT).show(); } @Override public void onDestroy() { Toast.makeText(this, "Service Destroty", Toast.LENGTH_SHORT).show(); super.onDestroy(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "Service Start", Toast.LENGTH_SHORT).show(); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { return null; } }
服务都必须在AndroidManifest.xml文件中注册<service android:name=".MyService"/>,在Activity中定义对象private Intent myServiceIntent = new Intent(this, MyService.class);在onCreate函数中startService(myServiceIntent)启动服务; onDestroy函数中stopService(myServiceIntent)关闭服务;
b. 生命周期
通过startService启动服务,若服务未启动,会先执行onCreate函数(若服务已启动则不执行此函数),再执行onStartCommand函数。由此可知多次调用startService传入相同参数不会启动多个服务(onStartCommand函数会执行多次),所以最终只需要调用一次stopService或stopSelf函数停止服务;我们可以将service的处理逻辑放入onStartCommand函数中。服务一直运行,在程序退出后服务也不会停止,直到stopService或stopSelf函数被调用,当然可能被系统回收。
对于onStartCommand的返回值,若返回START_STICKY表示服务通过显式调用启动或停止,若返回START_NOT_STICKY
orSTART_REDELIVER_INTENT表示服务仅在有请求发送过来处理时才处于运行状态。
c. 使用场景
因为这种方式Service无法与外部进行方便的动态交互,所以适合做后台服务,如网络下载(用户通过Intent传入Url到Service,推荐使用IntentService).
(2) bindService启动的Service应用内交互
a. 示例代码及介绍
在上面的方式中Context可以通过Intent向Service传入简单的信息,但是如果希望调用Service的接口进行操作或是获取Service的属性则无法实现,这里我们可以通过bindService实现,如下先定义自己的服务
public class MyService extends Service { private int count; private MyBinder myBinder = new MyBinder(); @Override public void onCreate() { Toast.makeText(this, "Service onCreate", Toast.LENGTH_SHORT).show(); count = 0; super.onCreate(); } @Override public void onDestroy() { Toast.makeText(this, "Service onDestroy", Toast.LENGTH_SHORT).show(); super.onDestroy(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } /** * 服务被绑定时调用 * 返回值用于让调用者和服务通信,传入ServiceConnection的public void onServiceConnected(ComponentName name, IBinder service)函数第二个参数 */ @Override public IBinder onBind(Intent intent) { return myBinder; } public int getCount() { return count; } public int increaseCount() { return ++count; } public int decreaseCount() { return --count; } public class MyBinder extends Binder { MyService getService() { return MyService.this; } } }
从上可以看出我们重写onBind函数并返回自己的Binder用于调用者和服务之间通信。下面我们在调用者中进行调用
private MyService myService; private Intent myServiceIntent; private ServiceConnection con = new ServiceConnection() { /** * 服务所在进程被kill或是crash时系统调用,而不是unbindService时调用 */ @Override public void onServiceDisconnected(ComponentName name) { Toast.makeText(getApplicationContext(), "Service disconnect", Toast.LENGTH_SHORT).show(); } /** * 服务连接时调用,若已经连接不进行调用 */ @Override public void onServiceConnected(ComponentName name, IBinder service) { myService = ((MyBinder)service).getService(); Toast.makeText(getApplicationContext(), "Service Connect", Toast.LENGTH_SHORT).show(); } };
接着调用bindService(myServiceIntent, con, Context.BIND_AUTO_CREATE);绑定服务,绑定成功返回true。这时会执行ServiceConnection对象的onServiceConnected函数为myService变量赋值。接着我们就可以通过myService.increaseCount();操作Service的属性,myService.getCount()获得Service的属性,这样我们就成功和Service进行了通信。在不需要通信时通过unbindService(con);解除服务绑定。
b. 生命周期
通过bindService绑定服务,若服务未启动,会先执行Service的onCreate函数,再执行onBind函数,最后执行ServiceConnection对象的onServiceConnected函数。若服务已启动但尚未绑定,先执行onBind函数,再执行ServiceConnection对象的onServiceConnected函数。若服务已绑定成功,则直接返回。这里不会自动调用onStartCommand函数。
通过unbindService解除绑定服务,若已绑定成功,会先执行Service的onUnbind函数,再执行onDestroy函数,注意这里不会执行ServiceConnection对象的onServiceDisconnected函数,因为该函数是在服务所在进程被kill或是crash时被调用。若服务尚未绑定系统会报服务尚未注册异常,我们可以通过如下代码解决
if (myService != null) { unbindService(con); myService = null; }
与startService启动的Service不同,若服务通过bindService启动并且没有通过startService启动过后,则在连接断开时服务就会自动解绑(onUnbind)并终止(onDestroy),而在调用者(Activity)退出后会自动断开连接,所以这时服务会自己解绑并终止。若存在某个组件绑定了该服务,则调用该服务的stopService不会停止服务。
c. 使用场景
应用内通信,如音乐播放器,在服务中控制播放器的播放、暂停、停止,在Activity中通过对服务操作控制播放器。
(3) bindService启动的Service应用间交互——AIDL
在Android AIDL应用间交互中 详细介绍见Android AIDL交互。
通过上面的三个例子我们可以看出Context.startService()
和Context.bindService()
的区别
a. bindService启动的Service可以和Context进行交互,而startService启动的Service不可以交互,因而使用场景也不同
b. 执行的生命周期不同
3、特殊的Service
(1). 异步服务IntentService
默认Service是运行在主线程内的 ,如果在Service内运行一个耗时操作就会阻塞主线程,可能导致ANR,为此我们可以在Service中自己新建线程去执行耗时操作,不过Android系统引入了IntentService方便的解决了这个问题, IntentService会启动一个工作线程去完成用户onHandleIntent中定义的操作,需要注意的是对于同一个IntentService的多次请求(startService调用),在同一个线程中处理,一次只会执行一个请求的onHandleIntent函数。对于不同IntentService的同时请求,在不同的线程中处理,所以每个请求的onHandleIntent函数可以并发执行。示例代码如下:
public class MyIntentService extends IntentService { public MyIntentService(){ super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { try { System.out.println("IntentService1 Begin Sleep. " + "Thread name: " + Thread.currentThread().getName() + ", Thread Id: " + Thread.currentThread().getId()); Thread.sleep(3000); System.out.println("IntentService1 End. "); } catch (InterruptedException e) { e.printStackTrace(); } } }
在AndroidManifest.xml文件中注册服务<service android:name=".MyIntentService"/>,在Activity中定义service对象private Intent myIntentServiceIntent = new Intent(ServiceDemo.this, MyIntentService.class);在onCreate函数中startService(myIntentServiceIntent)启动服务;
从上面我们可以看出
a. IntentService只需要重定义onHandleIntent函数并定义一个无参构造函数(xml中服务注册初始化时使用)即可。
b. IntentService服务在onHandleIntent执行结束后会自动关闭。
IntentService和普通Service的区别如下:
a. 普通Service运行在主线程中,IntentService运行在一个工作线程中不会阻塞主线程。
b. 普通Service需要手动调用停止接口,IntentService自动停止。
c. IntentService的onStartCommand函数根据mRedelivery属性值返回START_REDELIVER_INTENT或START_NOT_STICKY,而普通Service自定义返回。
参考:
http://developer.android.com/reference/android/app/Service.html
http://developer.android.com/guide/components/services.html
http://developer.android.com/reference/android/app/IntentService.html
发表评论 取消回复