绑定服务
https://developer.android.google.cn/guide/components/bound-services?hl=zh-cn
绑定服务是Service类的实现,可让其它应用与其进行绑定和交互。实现onBind()回调方法,此方法返回一个IBinder对象,该对象定义的编程接口可供客户端用来与服务进行交互。
你可以将多个客户端同时连接到某项服务,但是系统会缓存IBinder服务通信通道。就是第一个客户端绑定服务时,系统才会调用onBind生成Binder,然后接下来的都会用这个IBinder,无需再次调用onBind()。
创建提供绑定的服务时,您必须提供 IBinder,用以提供编程接口,供客户端与服务进行交互之用。
三种方式定义接口:
扩展Binder类
如果服务与客户端相同进程中进行,应该扩展Binder类并从onBind()返回该类的实例来创建接口。
使用Messenger
如果需要让接口跨不同进程工作,可以使用Messenger为服务创建接口。这种方式服务会定义一个Handler,用来处理不同类型的Message对象。Messenger可与客户端分享一个IBinder,客户端可以通过这个利用Message对象向服务发送命令。客户端也可以定义一个Messenger用来回复服务消息。
Messenger会在单个线程中创建包含所有请求的队列,不必做线程安全处理。
使用AIDL
Messenger的方式实际上是以AIDL作为其底层结构。如果想让服务同时处理多个请求,可以直接使用AIDL,这样的情况下,服务必须得达到线程安全的要求,并且能够多线程处理。
如需直接使用 AIDL,您必须创建用于定义编程接口的 .aidl 文件。Android SDK 工具会利用该文件生成实现接口和处理 IPC 的抽象类,您随后可在服务内对该类进行扩展。
从某种意义上说AIDL其实就是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此生产的一个Interface的实例代码,AIDL其实是为了避免我们重复写代码而出现的一个模板。
Binder
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(connection);
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() */
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
Messenger
public class MessengerService extends Service {
/**
* Command to the service to display a message
*/
static final int MSG_SAY_HELLO = 1;
/**
* Handler of incoming messages from clients.
*/
static class IncomingHandler extends Handler {
private Context applicationContext;
IncomingHandler(Context context) {
applicationContext = context.getApplicationContext();
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
Messenger mMessenger;
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
mMessenger = new Messenger(new IncomingHandler(this));
return mMessenger.getBinder();
}
}
绑定是异步操作,bindService方法立即返回。要接受IBinder的话,客户端要创建一个ServiceConnection实例,并将其传递给bindService
asInterface()会接收IBinder,并返回Stub接口的实例。
PRC是同步调用,不应在主线程去调用该服务。
引发的任何异常都不会回传给调用方
当客户端(如 Activity)调用 bindService() 以连接此服务时,客户端的 onServiceConnected() 回调会接收服务的 onBind() 方法所返回的 binder 实例。
当客户端在 onServiceConnected() 回调中收到 IBinder 时,它必须调用 YourServiceInterface.Stub.asInterface(service),以将返回的参数转换成 **YourServiceInterface ** 类型。
您可以通过 IPC 接口,将某个类从一个进程发送至另一个进程。不过,您必须确保 IPC 通道的另一端可使用该类的代码,并且该类必须支持 Parcelable 接口。支持 Parcelable 接口很重要,因为 Android 系统能通过该接口将对象分解成可编组至各进程的原语。
如要创建支持 Parcelable 协议的类,您必须执行以下操作:
让您的类实现 Parcelable 接口。
实现 writeToParcel,它会获取对象的当前状态并将其写入 Parcel。
为您的类添加 CREATOR 静态字段,该字段是实现 Parcelable.Creator 接口的对象。
最后,创建声明 Parcelable 类的 .aidl 文件(遵照下文 Rect.aidl 文件所示步骤)。
如果您使用的是自定义编译进程,请勿在您的构建中添加 .aidl 文件。此 .aidl 文件与 C 语言中的头文件类似,并未经过编译。
如果您的 AIDL 接口包含接收Bundle作为参数(预计包含 Parcelable 类型)的方法,则在尝试从Bundle读取之前,请务必通过调用 Bundle.setClassLoader(ClassLoader) 设置Bundle的类加载器。否则,即使您在应用中正确定义 Parcelable 类型,也会遇到 ClassNotFoundException。
bundle.setClassLoader(getClass().getClassLoader());
Rect rect = bundle.getParcelable("rect");
调用IPC方法
在项目的 src/ 目录中加入 .aidl 文件。
声明一个 IBinder 接口实例(基于 AIDL 生成)。
实现 ServiceConnection。
调用 Context.bindService(),从而传入您的 ServiceConnection 实现。
在 onServiceConnected() 实现中,您将收到一个 IBinder 实例(名为 service)。调用 YourInterfaceName.Stub.asInterface((IBinder)service),以将返回的参数转换为 YourInterface 类型。
调用您在接口上定义的方法。您应始终捕获 DeadObjectException 异常,系统会在连接中断时抛出此异常。您还应捕获 SecurityException 异常,当 IPC 方法调用中两个进程的 AIDL 定义发生冲突时,系统会抛出此异常。
如要断开连接,请使用您的接口实例调用 Context.unbindService()。
有关调用 IPC 服务的几点说明:
对象是跨进程计数的引用。
您可以方法参数的形式发送匿名对象。
如何调用AIDL创建的服务
public static class Binding extends Activity {
/** The primary interface we will be calling on the service. */
IRemoteService mService = null;
/** Another interface we use on the service. */
ISecondary secondaryService = null;
Button killButton;
TextView callbackText;
private InternalHandler handler;
private boolean isBound;
/**
* Standard initialization of this activity. Set up the UI, then wait
* for the user to poke it before doing anything.
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.remote_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(unbindListener);
killButton = (Button)findViewById(R.id.kill);
killButton.setOnClickListener(killListener);
killButton.setEnabled(false);
callbackText = (TextView)findViewById(R.id.callback);
callbackText.setText("Not attached.");
handler = new InternalHandler(callbackText);
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IRemoteService.Stub.asInterface(service);
killButton.setEnabled(true);
callbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
killButton.setEnabled(false);
callbackText.setText("Disconnected.");
// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
/**
* Class for interacting with the secondary interface of the service.
*/
private ServiceConnection secondaryConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// Connecting to a secondary interface is the same as any
// other interface.
secondaryService = ISecondary.Stub.asInterface(service);
killButton.setEnabled(true);
}
public void onServiceDisconnected(ComponentName className) {
secondaryService = null;
killButton.setEnabled(false);
}
};
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
// Establish a couple connections with the service, binding
// by interface names. This allows other applications to be
// installed that replace the remote service by implementing
// the same interface.
Intent intent = new Intent(Binding.this, RemoteService.class);
intent.setAction(IRemoteService.class.getName());
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
intent.setAction(ISecondary.class.getName());
bindService(intent, secondaryConnection, Context.BIND_AUTO_CREATE);
isBound = true;
callbackText.setText("Binding.");
}
};
private OnClickListener unbindListener = new OnClickListener() {
public void onClick(View v) {
if (isBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
unbindService(secondaryConnection);
killButton.setEnabled(false);
isBound = false;
callbackText.setText("Unbinding.");
}
}
};
private OnClickListener killListener = new OnClickListener() {
public void onClick(View v) {
// To kill the process hosting our service, we need to know its
// PID. Conveniently our service has a call that will return
// to us that information.
if (secondaryService != null) {
try {
int pid = secondaryService.getPid();
// Note that, though this API allows us to request to
// kill any process based on its PID, the kernel will
// still impose standard restrictions on which PIDs you
// are actually able to kill. Typically this means only
// the process running your application and any additional
// processes created by that app as shown here; packages
// sharing a common UID will also be able to kill each
// other's processes.
Process.killProcess(pid);
callbackText.setText("Killed service process.");
} catch (RemoteException ex) {
// Recover gracefully from the process hosting the
// server dying.
// Just for purposes of the sample, put up a notification.
Toast.makeText(Binding.this,
R.string.remote_call_failed,
Toast.LENGTH_SHORT).show();
}
}
}
};
// ----------------------------------------------------------------------
// Code showing how to deal with callbacks.
// ----------------------------------------------------------------------
/**
* This implementation is used to receive callbacks from the remote
* service.
*/
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
/**
* This is called by the remote service regularly to tell us about
* new values. Note that IPC calls are dispatched through a thread
* pool running in each process, so the code executing here will
* NOT be running in our main thread like most other things -- so,
* to update the UI, we need to use a Handler to hop over there.
*/
public void valueChanged(int value) {
handler.sendMessage(handler.obtainMessage(BUMP_MSG, value, 0));
}
};
private static final int BUMP_MSG = 1;
private static class InternalHandler extends Handler {
private final WeakReference<TextView> weakTextView;
InternalHandler(TextView textView) {
weakTextView = new WeakReference<>(textView);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
TextView textView = weakTextView.get();
if (textView != null) {
textView.setText("Received from service: " + msg.arg1);
}
break;
default:
super.handleMessage(msg);
}
}
}
}