我们都知道要开启一个Android的Service有两种方式,一种是startService(Intent)的方式,
另一种就是绑定的方式。
至于为什么会有两种呢,因为我们有不同的需求,有时候我们仅仅是想开启一个Service作为后台进程,
但有时我们想给Service发送一个消息,所谓的消息就是要调用Service的方法执行某个操作,
这时候要想用某个对象的方法,首先就要获得这个对象,可惜的是,这个Service对象不是我们创建的而是系统
创建的我们没法直接获得这个对象引用,所以需要一种框架,或者说是某种设计模式,将我们想要调用的方法
暴漏出来,能够供其他的类(Activity或者Service)调用。
在最后我会给出程序的源码下载地址
1.首先实现一个固定的套路也就是实现绑定方式开启Service的固定代码
以下是Activity的代码,至于在manifest文件中的配置和Activity的布局我就不再给出来,为了代码简洁删除了很多和此程序无关的
应该实现的方法。
[code lang=”java”]
package com.comcons.activity;
import com.comcons.service.MyService;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
public class MainActivity extends Activity {
private MyServiceConnection myServiceConnection;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* 初始化
*/
myServiceConnection = new MyServiceConnection() ;
}
/**
* 绑定服务的Button的事件
* @param view
*/
public void bindMyService(View view){
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, myServiceConnection, BIND_AUTO_CREATE);
}
/**
* 调用服务中的方法的事件
* @param view
*/
public void callMyServiceMethod(View view){
}
private class MyServiceConnection implements ServiceConnection{
/**
* 当绑定成功后会调用这个方法
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
System.out.println("====连接成功====");
}
/**
* 服务异常终止,也就是在没用调用unbindService(myServiceConnection);
* 方法时。比如内存清理,或者退出Service
* 只service的宿主进程崩溃或者被杀掉也可能是Service本身死掉了(此处感觉没解释清楚)
* 而且我也没发现,如何才能调用这个方法
*/
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
System.out.println("dis connect");
}
}
}
[/code]
可以看出通过以下两行代码可以绑定开启一个服务
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, myServiceConnection, BIND_AUTO_CREATE);
其中bindService(intent,myServiceConnection, BIND_AUTO_CREATE)是ContextWrapper中的方法,Activity间接 实现了ContextWrapper类,所以会继承这个方法,我们看到这个方法中有三个参数,
第一参数没什么问题就是Intent,指向要开启的Service
第二个参数是一个ServiceConnection接口的实现,这个要自己实现,也很简单接口中就只有两个方法,实现就可以了。接口中的这两个方法是回调函数,代码注释我已经写的很清楚了。
第三个参数是一个int类型的flag,这个代表绑定时的选项,从API中可以看到有很多中选择,这里我们选择了,BIND_AUTO_CREATE(如果绑定时Service不存在,那么创建。还有其他的请参考api)
以下是Service的实现方式,
[code lang=”java”]
package com.comcons.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
System.out.println("MyService onBind()方法调用—-> 服务成功绑定");
return new MyBinder();
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
System.out.println("MyService onCreate()方法调用—->服务被创建");
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
System.out.println("MyService onDestroy()方法调用—->服务被销毁");
}
// @Override
// public boolean onUnbind(Intent intent) {
// // TODO Auto-generated method stub
// System.out.println("MyService onUnbind()方法调用—->服务被解绑");
// return super.onUnbind(intent);
// }
private class MyBinder extends Binder {
}
}
[/code]
当在某个Activity中调用bindService方法的时候,会调用Service中的onBind(Intent intent)方法,然后onBind的方法返回一个IBinder接口的实现对象。这个对象会被传递到我们在上面的Activity中的自己实现的MyServiceConnection 类中的onServiceConnected(ComponentName name, IBinder service)方法,当做这个方法的第二个参数,传递。
由于IBinder是一个接口,不能直接传递对象所以必须要有自己的实现类,可是这个接口中未实现的方法太多了,我们用不那么多,Android工程师给了一个类似java中Adapter中的东西,叫做Binder类,这是一个类,它实现了IBinder接口,所以我们直接继承Binder类就行了,这样代码看起来比较简洁。
在上面的Service的代码中我实现的自己的子类MyBinder中没有任何方法,在稍后我们会丰富这个类中的方法。让这个类中的方法去调用其外部类(MyService)中的方法,把自己的对象传递个MyConnection中的方法,这样就可以把Service中的方法暴漏出去了。
很绕对吧,你也可能会想Google工程师为什么废了这么大的劲,饶了这么一个大圈才把Service中方法暴漏出去,为什么要通过IBinder来暴漏,如果把Service中的onBind(Intent intent)方法直接给实现成
[code lang=”java”]
public Service getService(Intent intent){
return this;
}
[/code]
然后再把Service传递个MyConnection对象中的那个方法,然后程序员在获得Service的引用后再强制类型转换成MyService对象,
这样岂不是更好吗,就不用多定义一个Ibinder对象了,直接获得了Service的引用,就可以调用Service中的方法了。
但是这样有个致命的缺陷,就是完全把Service中的公有方法暴漏出去了,只要调用者高兴,可以随便调用Service类中任意方法,包括onCreate和onDestory方法。如果这样那还了得。
所以这样设计的最大目的还是为了安全。当然Google虽然这样设计了这样的框架,但是如果我们高兴依然可以把Service的引用暴漏出去,不过很不安全,也没啥意思不过简单,呵呵。。。看下边的例子
2. 对外暴漏Service的引用
同样先给出Activity的代码,这里我就不再解释了,代码中的注释我感觉已经十分详细了
[code lang=”java”]
package com.comcons.activity;
import com.comcons.service.MyService;
import com.comcons.service.MyService.MyBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
public class MainActivity extends Activity {
private MyServiceConnection myServiceConnection;
//获取MyBinder的引用,这个类这MyService种被声明成内部类
//注意这样写存在安全问题,后面我们会改进
private MyBinder myBinder ;
//声明MyService引用的全局变量,注意这样很不安全,没人会这么实现
//仅仅只为了演示
private MyService myService ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* 初始化MyServiceConnection
* 以便于在绑定和解绑Service时使用
*/
myServiceConnection = new MyServiceConnection() ;
}
/**
* 绑定服务的Button的事件
* @param view
*/
public void bindMyService(View view){
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, myServiceConnection, BIND_AUTO_CREATE);
}
/**
* 调用服务中的方法的事件
* @param view
*/
public void callMyServiceMethod(View view){
//当然既然已经获得Service的引用你可以调用任何Service中方法
myService.letsCallMe();
}
private class MyServiceConnection implements ServiceConnection{
/**
* 当绑定成功后会调用这个方法
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
System.out.println("====连接成功====");
//对接收到IBinder对象进行强制类型转换
myBinder = (MyBinder)service;
//通过我们自己再MyBinder中实现的方法getService();获取MyService的引用
//注意要强制类型转换
myService = (MyService)myBinder.getService();
//好吧既然,得到了MyService的引用,我们就可以为所欲为了
//就现不在这里调用MyService中任何方法了,在上面的Button 事件中调用吧,见上面
}
/**
* 服务异常终止,也就是在没用调用unbindService(myServiceConnection);
* 方法时。比如内存清理,或者退出Service
* 只service的宿主进程崩溃或者被杀掉也可能是Service本身死掉了(此处感觉没解释清楚)
* 而且我也没发现,如何才能调用这个方法
*/
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
System.out.println("dis connect");
}
}
}
[/code]
实现的MyService代码如下:
[code lang=”java”]
package com.comcons.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
System.out.println("MyService onBind()方法调用—-> 服务成功绑定");
return new MyBinder();
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
System.out.println("MyService onCreate()方法调用—->服务被创建");
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
System.out.println("MyService onDestroy()方法调用—->服务被销毁");
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
System.out.println("MyService onUnbind()方法调用—->服务被解绑");
return super.onUnbind(intent);
}
/**
* 模拟Service中的方法被调用
*/
public void letsCallMe(){
System.out.println("成功发送消息到MyService");
}
public class MyBinder extends Binder {
/**
* 以为MyBinder类的实例会被传送出去
* 所以这个方法必然也能被调用到
* @return 外部类MyService的引用
*/
public Service getService(){
return MyService.this;
}
}
}
[/code]
以上实现方式纯属胡闹,我只是想表明Service的引用是可以被暴漏出来的,但是,这种方式几乎一点用都没有有,因为这是在Google的安全框架下的多此一举的实现方式,如果没有Google的安全框架,如果返回的不是IBinder对象而是Object的话,那么可以返回Service,因为这样简单。如果硬要说这种方法有一点点的优点的话,那么就是它少写了那么点代码,作为比较看下面的这个。通过比较你就知道少写的那些代码少些在哪了。
3. 只暴露IBinder的实现类的实例,通过这个类中的方法,调用外部类Service中的方法,不暴露Service引用
我依然先给出Activity的代码
[code lang=”java”]
package com.comcons.activity;
import com.comcons.service.MyService;
import com.comcons.service.MyService.MyBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
public class MainActivity extends Activity {
private MyServiceConnection myServiceConnection;
//获取MyBinder的引用,这个类这MyService种被声明成内部类
//注意这样写存在安全问题,后面我们会改进
private MyBinder myBinder ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* 初始化MyServiceConnection
* 以便于在绑定和解绑Service时使用
*/
myServiceConnection = new MyServiceConnection() ;
}
/**
* 绑定服务的Button的事件
* @param view
*/
public void bindMyService(View view){
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, myServiceConnection, BIND_AUTO_CREATE);
}
/**
* 调用服务中的方法的事件
* @param view
*/
public void callMyServiceMethod(View view){
//在这里我们只演示调用第一个方法,调用第二个方法同理
myBinder.callServiceMethod01();
}
private class MyServiceConnection implements ServiceConnection{
/**
* 当绑定成功后会调用这个方法
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
System.out.println("====连接成功====");
//对接收到IBinder对象进行强制类型转换
myBinder = (MyBinder)service;
}
/**
* 服务异常终止,也就是在没用调用unbindService(myServiceConnection);
* 方法时。比如内存清理,或者退出Service
* 只service的宿主进程崩溃或者被杀掉也可能是Service本身死掉了(此处感觉没解释清楚)
* 而且我也没发现,如何才能调用这个方法
*/
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
System.out.println("dis connect");
}
}
}
[/code]
Service 的代码实现如下:
[code lang=”java”]
package com.comcons.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
System.out.println("MyService onBind()方法调用—-> 服务成功绑定");
return new MyBinder();
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
System.out.println("MyService onCreate()方法调用—->服务被创建");
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
System.out.println("MyService onDestroy()方法调用—->服务被销毁");
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
System.out.println("MyService onUnbind()方法调用—->服务被解绑");
return super.onUnbind(intent);
}
/**
* 模拟Service中的方法被调用
*/
private void letsCallMe_01(){
System.out.println("成功发送消息到MyService第一个方法");
}
/**
* 模拟Service中的方法被调用
*/
private void letsCallMe_02(){
System.out.println("成功发送消息到MyService第二个方法");
}
public class MyBinder extends Binder {
/**
* 这个是真正暴露出去的方法,通过这个方法可以调用
* 外部类的letsCallMe_01();
*/
public void callServiceMethod01() {
letsCallMe_01();
}
public void callServiceMethod02() {
letsCallMe_02();
}
}
}
[/code]
通过对比我们就可以知道上面所说的少些的那些代码少些在哪了吧,对的,在第二种方式我们的IBinder对象只要是个方法就全搞定了,整个对象就一个任务,把Service对象的引用给我返回回去,以便于在Activity中调用Service中方法,但是这个就不一样了,着这个中,对应外部类Service中的每个要暴露的方法,在内部类MyBinder对象中都要有他的对应的一个向外暴漏的方法,这样就增加了很多冗余代码,其实也不能说是冗余,实在找不到什么合适的词来形容了,不过MyBinder的中的方法就起到以传递功能。
想想看如果MyService中有N多方法想暴露出去的话,就要在MyBinder中实现N多与其对应的方法。
好了写到这绑定的方式调用Service中的方法已经近乎完美,但是还只是近乎而已,我的MyBinder类在这里被实现成了公有的,但是它同时是MyService中的内部类,内部类实现成公有的总不是太好吧。
额,由于代码的原因这篇太长了,再分一篇吧 链接