一步步解析用绑定的方式开启Service(一)

By | 2014/08/09

我们都知道要开启一个Android的Service有两种方式,一种是startService(Intent)的方式,

另一种就是绑定的方式。

至于为什么会有两种呢,因为我们有不同的需求,有时候我们仅仅是想开启一个Service作为后台进程,

但有时我们想给Service发送一个消息,所谓的消息就是要调用Service的方法执行某个操作,

这时候要想用某个对象的方法,首先就要获得这个对象,可惜的是,这个Service对象不是我们创建的而是系统

创建的我们没法直接获得这个对象引用,所以需要一种框架,或者说是某种设计模式,将我们想要调用的方法

暴漏出来,能够供其他的类(Activity或者Service)调用。

在最后我会给出程序的源码下载地址

1.首先实现一个固定的套路也就是实现绑定方式开启Service的固定代码

以下是Activity的代码,至于在manifest文件中的配置和Activity的布局我就不再给出来,为了代码简洁删除了很多和此程序无关的

应该实现的方法。

device-2014-08-09-130401


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");
		}

	}
}

可以看出通过以下两行代码可以绑定开启一个服务
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的实现方式,


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 {

	}
}

当在某个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)方法直接给实现成


public Service getService(Intent intent){

return this;

}

然后再把Service传递个MyConnection对象中的那个方法,然后程序员在获得Service的引用后再强制类型转换成MyService对象,

这样岂不是更好吗,就不用多定义一个Ibinder对象了,直接获得了Service的引用,就可以调用Service中的方法了。

但是这样有个致命的缺陷,就是完全把Service中的公有方法暴漏出去了,只要调用者高兴,可以随便调用Service类中任意方法,包括onCreate和onDestory方法。如果这样那还了得。

所以这样设计的最大目的还是为了安全。当然Google虽然这样设计了这样的框架,但是如果我们高兴依然可以把Service的引用暴漏出去,不过很不安全,也没啥意思不过简单,呵呵。。。看下边的例子

2. 对外暴漏Service的引用

同样先给出Activity的代码,这里我就不再解释了,代码中的注释我感觉已经十分详细了


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");
		}

	}
}

实现的MyService代码如下:


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;
		}
	}
}

以上实现方式纯属胡闹,我只是想表明Service的引用是可以被暴漏出来的,但是,这种方式几乎一点用都没有有,因为这是在Google的安全框架下的多此一举的实现方式,如果没有Google的安全框架,如果返回的不是IBinder对象而是Object的话,那么可以返回Service,因为这样简单。如果硬要说这种方法有一点点的优点的话,那么就是它少写了那么点代码,作为比较看下面的这个。通过比较你就知道少写的那些代码少些在哪了。

3. 只暴露IBinder的实现类的实例,通过这个类中的方法,调用外部类Service中的方法,不暴露Service引用

我依然先给出Activity的代码


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");
		}

	}
}

Service 的代码实现如下:

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();
		}
	}
}

通过对比我们就可以知道上面所说的少些的那些代码少些在哪了吧,对的,在第二种方式我们的IBinder对象只要是个方法就全搞定了,整个对象就一个任务,把Service对象的引用给我返回回去,以便于在Activity中调用Service中方法,但是这个就不一样了,着这个中,对应外部类Service中的每个要暴露的方法,在内部类MyBinder对象中都要有他的对应的一个向外暴漏的方法,这样就增加了很多冗余代码,其实也不能说是冗余,实在找不到什么合适的词来形容了,不过MyBinder的中的方法就起到以传递功能。
想想看如果MyService中有N多方法想暴露出去的话,就要在MyBinder中实现N多与其对应的方法。

好了写到这绑定的方式调用Service中的方法已经近乎完美,但是还只是近乎而已,我的MyBinder类在这里被实现成了公有的,但是它同时是MyService中的内部类,内部类实现成公有的总不是太好吧。

额,由于代码的原因这篇太长了,再分一篇吧 链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注