概要
本文主要讲解Service与Activity进行通信的几种方式,Activity主要通过Intent出传递数据到Service,而比较常见的Service传递数据到Activity有三种方式,但是这次所介绍的Service与Activity的通信方式都是位于同一应用进程中的,并没有介绍不同进程之间如何通信,不同进程通过aidl或者Messenger在以后的文章中会再做详细的介绍。
Service和Activity是Android中最常用的两大组件,Activity的设计与Web页面非常类似,从页面的跳转通过连接,以及从页面的定位通过URL,从每个页面的独立封装等方面都可以看出来,它主要负责与用户进行交互。Service则是在后台运行,默默地为用户提供功能,进行调度和统筹,它用来“执行”后台的、耗时的、重要的任务,三者缺一不可,而最重要的原因是第三点:要执行重要的任务。
从上面可以看出,Activity负责与用户的直接交互,Service是运行在后台用户不可见的而且常常执行的是重要的任务,既然是重要的任务然而用户却不知道任务的执行情况,比如下载,用户想知道下载的进度,这种情况下就需要与Activity通信,方面用户实时知道任务的执行情况。
Activity传递数据到Service
这种方式传递数据基本上是依赖Intent来传递数据的,更为详细的请参考Intent学习笔记,在Activity中可以使用两种方式来与Service进行交互,一种是通过bindService(Intent service, ServiceConnection conn, int flags)
方法,另一种是startService(Intent service)
,当然了也可以通过广播来与服务通信,但是个人感觉Activity通过Broadcast来通信就有点多此一举了,但是反过来则是可行的,在Service中通过Broadcast来传递数据到Activity。
接下来介绍最常见的通过Service传递数据到Activity的三种方式。
方式一:通过Binder
在以前的一篇文章Service简介及启动方式就已经介绍过,Service主要提供两种功能,其中之一就是将应用程序的某些功能暴露给其它程序,该种方式就是通过Binder来实现的。
我们知道在每创建一个Service实例是都要实现一个方法onBind(Intent intent)
,该方法返回一个IBinder类型的实例,IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分。但它不仅用于远程调用,也用于进程内调用。这个接口定义了与远程对象交互的协议。不要直接实现这个接口,而应该从Binder派生。
下面是一个简单的Service实现:
publicclassMyServiceextendsService{
@Override
publicIBinder onBind(Intent intent){
returnnewMyBinder();
}
publicclassMyBinderextendsBinder{
privateMyService service;
publicMyBinder(){
service=MyService.this;
}
publicStringgetName(){
return"admin";
}
publicvoidstartPlay(){
service.startPlay();
}
}
privatevoidstartPlay(){
System.out.println("start play");
}
}
当我们在Activity通过bindService(Intent service, ServiceConnection conn, int flags)来绑定一个Service时,bindService会立即返回,它不会返回Ibinder给客户端,要接收Ibinder,客户端Activity必须创建一个长连接ServiceConnection的实例并传给bindService(),ServiceConnection包含一个回调方法,系统调用这个方法来传递要返回的IBinder。
下面是Activity的代码:
publicclassMainActivityextendsActivity{
privateMyBinder service;
privateMyConnection conn;
privateIntent intent;
privateStringstr;
protectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_main);
conn=newMyConnection();
intent=newIntent(this,MyService.class);
}
publicvoidbinderClick(Viewv){
bindService(intent,conn,Context.BIND_AUTO_CREATE);
//这里输出null 因为conn还没有执行完成回调函数
System.out.println("aaaaaaaaaaaaaaaa:"+str);
}
privateclassMyConnectionimplementsServiceConnection{
@Override
publicvoidonServiceConnected(ComponentName name,IBinder binder){
service=(MyBinder)binder;
str=service.getName();
}
@Override
publicvoidonServiceDisconnected(ComponentName name){
}
}
@Override
protectedvoidonDestroy(){
super.onDestroy();
//注销长连接
unbindService(conn);
}
}
在本实例中,主要是通过IBinder来建立一个Service与Activity的一个桥梁,通过Binder对象来调用Service中的方法,当然了也可以在Service中在新建一个Binder对象时将Service对象本身作为一个方法的返回值返回,然后我们在Activity中就可以直接通过ServiceConnection长连接回调函数中的IBinder对象来得到一个Service实例,这样就可以使用Service中暴露的特定方法了,这种方式好像更简洁了,IBinder的目的就是为了将Service对象传递到Activity,其余的事情都不用管了。
方式二:通过Broadcast广播
在上一篇文章中我们已经讲过Broadcast可以用于应用程序之间的通信,更多的内容可以查看Android Broadcast广播机制。
Service的代码实现:
publicclassBServiceextendsService{
privateintprogress=0;
//创建一个Intent,设置Action为com.sunny.demo.RECEIVER
privateIntent intent=newIntent("com.sunny.demo.RECEIVER");
publicIBinder onBind(Intent intent){
returnnewBBinder();
}
publicclassBBinderextendsBinder{
//返回Service对象
publicBService getService(){
returnBService.this;
}
}
//下载方法
publicvoidstartDownload(){
newThread(newRunnable(){
publicvoidrun(){
while(progress<100){
progress++;
SystemClock.sleep(200);
//设置进度 发送广播
intent.putExtra("progress",progress);
sendBroadcast(intent);
}
}
}).start();
}
}
Activity中代码实现:
publicclassBActivityextendsActivity{
privateProgressBar progressBar;
privateBService service;
privateIntent intent;
privateMyConnection conn;
privateMyReceiver receiver;
protectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_b);
progressBar=(ProgressBar)findViewById(R.id.progressBar);
// 动态注册广播接收器
receiver=newMyReceiver();
IntentFilter intentFilter=newIntentFilter();
intentFilter.addAction("com.sunny.demo.RECEIVER");
registerReceiver(receiver,intentFilter);
conn=newMyConnection();
intent=newIntent(this,BService.class);
bindService(intent,conn,Context.BIND_AUTO_CREATE);
}
publicclassMyReceiverextendsBroadcastReceiver{
@Override
publicvoidonReceive(Context context,Intent intent){
// 拿到进度,更新UI
intprogress=intent.getIntExtra("progress",0);
progressBar.setProgress(progress);
}
}
//开始下载
publicvoidbinderClick(Viewv){
service.startDownload();
}
privateclassMyConnectionimplementsServiceConnection{
@Override
publicvoidonServiceConnected(ComponentName name,IBinder binder){
service=((BBinder)binder).getService();
}
@Override
publicvoidonServiceDisconnected(ComponentName name){
}
}
@Override
protectedvoidonDestroy(){
super.onDestroy();
//注销服务和广播
unbindService(conn);
unregisterReceiver(receiver);
}
}
方式三:自定义接口回调
事实上方式一也是一种接口回调,只是回调接口中传递的是IBinder的一个实例而已,这里我们们仍然会借助第一种方式的接口回调来获取IBinder的一个实例,然后通过该实例获取相对应得Service实例,再通过Service中自定义的回调接口将下载的进度实时更新。先看一下回调接口的定义:
//自定义下载回调借口
publicinterfaceProgressListener{
voidonProgress(intprogress);
}
Service代码:
publicclassCallbackServiceextendsService{
privateintprogress=0;
privateProgressListener progressListener;
publicIBinder onBind(Intent intent){
returnnewCallbackBinder();
}
publicclassCallbackBinderextendsBinder{
//获取service
publicCallbackService getService(){
returnCallbackService.this;
}
}
publicvoidstartDownload(){
newThread(newRunnable(){
publicvoidrun(){
while(progress<100){
progress++;
SystemClock.sleep(200);
//更新进度
progressListener.onProgress(progress);
}
}
}).start();
}
publicvoidsetProgressListener(ProgressListener progressListener){
this.progressListener=progressListener;
}
}
Activity的部分代码:
publicvoidonServiceConnected(ComponentName name,IBinder binder){
//通过binder得到service
service=((CallbackBinder)binder).getService();
//设置service的回调监听
service.setProgressListener(newProgressListener(){
@Override
publicvoidonProgress(intprogress){
//设置进度更新
progressBar.setProgress(progress);
}
});
}
小结
本篇文章虽然说是三种方式实现Service传递数据到Activity,可是归根到底仍然只是一种方式,都是借助于IBinder暴露Service中的相应操作。网上还有些文章中提到用观察者模式,观察者模式也是接口回调的一种方式,xUtils框架中的下载直接使用Service中的静态方法来获取了下载管理器DownloadManager,它的这种方式是直接在Activity或者Fragment中使用了DownloadService,然后在获取下载管理器DownloadManager的方法中再启动Service,简单来说就是调用一个类的静态方法,这种方式跟面向对象中在一个类中调用另一个类的静态方法一样,不需要借助任何纽带桥梁直接创建调用,应用程序之间通信,大概就这么两种形式,一种是通过一个桥梁纽带来传递数据,另外一种就是直接在调用者中使用被调用者。