Bound Service是客户端-服务器模式的Service。Bound Service允许组件(Activity,Service,Content Provider,广播不能绑定服务)对其进行绑定、发送请求、接收响应、甚至进行进程间通信(interprocess communication IPC)。 Bound Service一般只在为其它应用程序组件服务期间才是存活的,而不会一直在后台保持运行。
Bound Service是 Service 类的一种实现,它允许其它应用程序与其绑定并交互。为了让服务支持绑定,你必须实现 onBind(Intent)
回调方法。这个方法返回一个 IBinder 对象,该对象定义了客户端与服务进行交互时所需的编程接口。客户端可以通过调用bindService()
方法来绑定服务。
应用程序组件(客户端)可以通过调用 bindService()
方法来绑定服务,系统会调用服务的 onBind(Intent)
回调方法,返回一个用于和服务进行交互的 IBinder 对象。绑定是异步进行的。 bindService() 将立即返回,并不会立即向客户端返回 IBinder 。为了接收 IBinder对象,客户端必须创建一个ServiceConnection 的实例,并把它传给 bindService() 。ServiceConnection 包含了一个回调方法,系统将会调用该方法来传递客户端所需的那个 IBinder 。
注
Bound Service绑定过程如下:
onBind(Intent)
方法,此方法中需要返回IBinder对象。onServiceConnected(ComponentName, IBinder)
方法(调用该方法来处理服务的onBind()
方法返回的 IBinder 对象)和onServiceDisconnected
方法(当与服务的绑定发生意外中断时,比如服务崩溃或者被杀死时,系统将会调用该方法,客户端解除绑定时,不会调用该方法)。unbindService(ServiceConnection)
方法。当客户端被销毁时,与服务的绑定也将解除。但与服务交互完毕后,或者你的activity进入pause状态时,你都应该确保解除绑定,以便服务能够在用完后及时关闭。onBind()
方法来获取 IBinder 对象。然后,系统会向后续请求绑定的客户端传送这同一个 IBinder对象,而不再调用onBind()
方法。当最后一个客户端解除绑定后,系统会销毁服务(除非服务先通过 startService()
启动的,且绑定时不再调用onCreate()
方法)。下面具体分解以上步骤
在自定义Service类中根据onBind(Intent)
方法返回的对象,分为三种通信方式:
onBind()
返回一个它的实例。客户端通过该 Binder 对象获取Service实例并处理业务功能。如果服务只是为该应用程序执行一些后台工作,那这就是首选的技术方案。若服务要被其它应用程序使用或者要跨多个进程使用,则这种方式不行。根据官方文档中的示例代码修改代码如下:
package com.sywyg.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import java.util.Random;
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/** * Class used for the client Binder. Because we know this service always * runs in the same process as its clients, we don't need to deal with IPC. * 在同一进程中通信,并非IPC */
public class LocalBinder extends Binder {
/** * 通过该方法获取LocalService对象 */
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
Log.d("result","getService executed");
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
Log.d("result","onBind...");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("result","onUnbind...");
return super.onUnbind(intent);
}
/** * method for clients * 模拟客户端需要处理的方法 */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
对应activity中的代码如下:
package com.sywyg.servicetest;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binding);
}
@Override
protected void onStart() {
super.onStart();
// Bind to LocalService
//绑定服务
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
//解除绑定
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** Called when a button is clicked (the button in the layout file attaches to * this method with the android:onClick attribute) * 客户端处理服务对象业务方法 */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
//若耗时则应在一个新的线程中运行
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** Defines callbacks for service binding, passed to bindService() * 若onBind(Intent intent)方法不返回对象则不调用该ServiceConnection实例 */
private ServiceConnection mConnection = new ServiceConnection() {
//执行该方法表示绑定成功,并可以调用服务了。
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
mService = binder.getService();
Log.d("result", "onServiceConnected...");
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
Log.d("result", "onServiceDisconnected...");
mBound = false;
}
};
}
就不演示结果了。
官方文档中给出的示例果然不错以后要多考虑文档上的例子。
可以看到通过上面这种方式可以实现同一进程间客户端(Client)和Bound Service之间的通信。局限:客户端与Service必须属于同一个进程,不能实现进程间通信(IPC)。否则会出现类似于“android.os.BinderProxy cannot be cast to xxx”错误。
注:在四大基本组件中,BroadcastReceiver不能作为Bound Service的客户端,因为BroadcastReceiver的生命周期很短,当执行完onReceive()
回调时,BroadcastReceiver生命周期完结。而Bound Service又与Client本身的生命周期相关,因此,Android中不允许BroadcastReceiver去bindService()
,当有此类需求时,可以考虑通过startService()
(四大组件Started Service 都是通过startService()
)。
如果需要远程通信,可以使用一个 Messenger 来提供服务的接口。这种技术能无需使用AIDL就能进行进程间通信(IPC)。
Messenger具体使用步骤如下:
getBinder()
方法在onBind()
中返回Binder对象。onServiceConncet()
方法,中传递过来的IBinder对象创建一个Messenger对象。这样两个Messenger对象关联到同一个IBinder对象上,从而实现IPC。send()
方法将消息发送到Service的Messenger对象中。具体代码如下。
自定义服务MyMessengerService如下:
package com.sywyg.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.widget.Toast;
public class MyMessengerService extends Service {
public static final int SAY_HELLO = 1;
public MyMessengerService() {
}
private Handler handler = new Handler(){
@Override
public void handleMessage(Message message) {
switch (message.what) {
case SAY_HELLO: Toast.makeText(MyMessengerService.this,"MyMessengerService",Toast.LENGTH_LONG).show();
break;
}
}
};
private Messenger messenger = new Messenger(handler);
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
对应的Activity代码如下:
package com.sywyg.servicetest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
public class MessengerActivity extends ActionBarActivity {
private Messenger messenger;
private boolean mbound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.messenger_main);
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(MessengerActivity.this,MyMessengerService.class);
bindService(intent,mconn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if(mbound){
unbindService(mconn);
mbound = false;
}
}
public void onButtonClick(View view){
Message message = Message.obtain();
message.what = MyMessengerService.SAY_HELLO;
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private ServiceConnection mconn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("result","onServiceConnected executed");
messenger = new Messenger(service);
mbound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mbound = false;
}
};
}
AIDL(Android Interface Definition Language)是Android接口定义语言的意思,可以用于多个应用程序组件与Bound Service之间进行进程间通信,从而可以实现多个应用程序共享同一个Service的功能。
具体使用步骤如下:
首先需要新建一个AIDL文件,在这个文件中定义好组件(如Activity)需要与Service进行通信的业务对象。新建IPerson.aidl文件,代码如下所示:
// IPerson.aidl
//包名是必须的
package com.sywyg.servicetest;
/** *aidl文件内不能使用修饰符 *支持的数据类型:基本数据类型,String,CharSequence,List,Map,自定义 */
interface IPerson {
void setName(String name);
void setAge(int age);
String getPerson();
}
如果你使用eclipse的话会在gen目录下自动生成一个对应的java文件;若你使用的是android studio,则请看1。
打开对应的IPerson.java代码如下:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\AndroidStudioProjects\\ServiceTest\\app\\src\\main\\aidl\\com\\sywyg\\servicetest\\IPerson.aidl */
package com.sywyg.servicetest;
/** *aidl内不能使用修饰符 *支持的数据类型:基本数据类型,String,CharSequence,List,Map,自定义 */
public interface IPerson extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.sywyg.servicetest.IPerson {
private static final java.lang.String DESCRIPTOR = "com.sywyg.servicetest.IPerson";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/** * Cast an IBinder object into an com.sywyg.servicetest.IPerson interface, * generating a proxy if needed. */
public static com.sywyg.servicetest.IPerson asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.sywyg.servicetest.IPerson))) {
return ((com.sywyg.servicetest.IPerson)iin);
}
return new com.sywyg.servicetest.IPerson.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_setName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setName(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_setAge:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
this.setAge(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getPerson:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getPerson();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.sywyg.servicetest.IPerson {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public void setName(java.lang.String name) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void setAge(int age) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(age);
mRemote.transact(Stub.TRANSACTION_setAge, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.lang.String getPerson() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setAge = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
public void setName(java.lang.String name) throws android.os.RemoteException;
public void setAge(int age) throws android.os.RemoteException;
public java.lang.String getPerson() throws android.os.RemoteException;
}
这是一个自动生成的java文件,提示不能修改。其中,该接口中有一个内部抽象类Stub,public static abstract class Stub extends android.os.Binder implements com.sywyg.servicetest.IPerson
。可以看到Stub类继承了Binder并实现aidl接口。Stub英文表示存根的意思,该类在服务端进程,我们必须继承该类并实现aidl接口中的方法。具体代码如下:
package com.sywyg.servicetest;
import android.os.RemoteException;
/** * 实现自己的业务功能 * Created by sywyg on 2015/5/4. */
public class PersonImpl extends IPerson.Stub{
private String name;
private int age;
@Override
public void setName(String name) throws RemoteException {
this.name = name;
}
@Override
public void setAge(int age) throws RemoteException {
this.age = age;
}
@Override
public String getPerson() throws RemoteException {
return "name:" + name + ",age:" + age;
}
}
服务端代码如下:
package com.sywyg.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
/** * 实现一个绑定服务类,这里实现本地通信 * 这里是B应用,和aidl属于一个业务,因此可以直接使用自定义的业务对象 * * @author sywyg * @since 2015/5/4 */
public class MyBindService extends Service {
private PersonImpl person;
public MyBindService() {
}
@Override
public IBinder onBind(Intent intent) {
Log.d("result","onBind...");
person = new PersonImpl();
return person;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("result","onUnbind...");
return super.onUnbind(intent);
}
}
对应的Activity代码如下:
package com.sywyg.servicetest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity2 extends ActionBarActivity implements View.OnClickListener{
private Button btn_bind;
private Button btn_unbind;
private Button btn_call_person;
private IPerson person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
btn_bind = (Button)findViewById(R.id.btn_bind);
btn_unbind = (Button)findViewById(R.id.btn_unbind);
btn_call_person = (Button)findViewById(R.id.btn_call_person);
btn_bind.setOnClickListener(this);
btn_unbind.setOnClickListener(this);
btn_call_person.setOnClickListener(this);
}
//服务连接对象
private ServiceConnection conn = new ServiceConnection() {
/** * * 绑定成功后会调用该方法 * @param name * @param service */
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("result","onServiceConnected...");
//判断本地调用还是远程调用关键代码
person = IPerson.Stub.asInterface(service);
}
/** * 当服务异常终止后调用,解除绑定时不会调用 * @param name */
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("result","onServiceDisconnected...");
}
};
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.btn_bind:
Intent intent = new Intent(this,MyBindService.class);
/** * 参数分别为:Intent对象,服务连接对象,绑定服务的标记(Context.BIND_AUTO_CREATE若服务没启动则先启动), * 后续详解 */
bindService(intent,conn,Context.BIND_AUTO_CREATE);
break;
case R.id.btn_unbind:
unbindService(conn);
break;
case R.id.btn_call_person:
//调用远程对象或本地对象
try {
person.setName("sywyg");
person.setAge(26);
Toast.makeText(this,person.getPerson(),Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
其中,关键代码是:onServiceConnect()
方法中的person = IPerson.Stub.asInterface(service)
。而asInterface()
方法在自动生成的IPerson.java中的源码如下:
public static com.sywyg.servicetest.IPerson asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.sywyg.servicetest.IPerson))) {
return ((com.sywyg.servicetest.IPerson)iin);
}
return new com.sywyg.servicetest.IPerson.Stub.Proxy(obj);
}
该方法无论如何都会返回一个IPerson对象,该方法实现了远程通信和本地通信。
以上是使用AIDL实现本地通信。对于远程通信,你可以再新建一个应用程序或者在AndroidMainfest.xml
中<service>
标签中添加属性android:process=":remote"
则Service就会运行在以包名加remote命名的进程中(而应用组件则是运行在包名命名的进程中),PersonImpl 类和Service在同一个进程中。当android:process=”:remote”使用“:”分号时,代表在应用程序里,当需要该service时,会自动创建新的进程。而如果是android:process=”remote”,没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。
若要在aidl接口中使用自定义类型,
首先该自定义类应实现Parcelable接口,其次新建一个和自定义类同名的aidl文件对自定义类进行声明,文件内容如下填写:
//包名也是必须的,否则出错
package com.sywyg.servicetest;
//Man为自定义类的类名
parcelable Man;
最后需在使用该自定义类的aidl文件中导入该类import com.sywyg.servicetest.Man;
。
当需要进行IPC时,使用 Messenger 要比用AIDL实现接口要容易些,因为 Messenger 会把所有调用服务的请求放入一个队列。而纯粹的AIDL接口会把这些请求同时发送给服务,这样服务就必须要能够多线程运行。
对于绝大多数应用程序而言,服务没有必要多线程运行,因此利用 Messenger 可以让服务一次只处理一个调用。如果 你的服务非要多线程运行,那你就应该用 AIDL 来定义接口。
使用aidl来定义接口,通过ADT工具生成一个对应的Java类,此类实现了进程间远程通讯的代理;编写自己的业务类(继承自动生成的类中的Stub抽象类)实现业务接口功能;再通过绑定Service的方式暴露此业务对象,给其它组件提供功能,以此实现进程间通信。
调用者可以通过bindService()方法绑定服务,从而可以获得绑定成功的远程业务对象或本地业务对象,可以调用相关功能。
onDestroy()
方法自动销毁。(注意解除绑定并未销毁该Service,前提是绑定的Service没提前启动)stopService()
方法一样可以解绑并销毁Service,同样在启动之后再绑定也是一样。unbind(ServiceConnection)
时,都需要注意当前Service是否处于已经绑定状态,否则可能会因为当前Service已经解绑后继续执行unbind(ServiceConnection)
会导致崩溃。因此通常设置一个boolean型标记判断是否已经绑定或解绑 stopService()
无需做当前Service是否有效的判断)。onBind()
方法来获取 IBinder 对象。然后,系统会向后续请求绑定的客户端传送这同一个 IBinder对象,而不再调用onBind()
方法。当最后一个客户端解除绑定后,系统会销毁服务(除非服务先通过 startService()
启动的,且绑定时不再调用onCreate()
方法)。http://blog.csdn.net/huangbiao86/article/details/7035920
http://www.cnblogs.com/lwbqqyumidi/p/4181185.html
http://blog.csdn.net/guolin_blog/article/details/11952435
http://blog.sina.com.cn/s/blog_48d4913001010696.html
威哥视频
第一行代码