IBinder
android.os.IBinder
IBinder接口是对跨进程的对象的抽象。
Base interface for a remotable object, the core part of a lightweight remote procedure call mechanism designed for high performance when performing in-process and cross-process calls. This interface describes the abstract protocol for interacting with a remotable object. Do not implement this interface directly, instead extend fromBinder
.
远程对象的基础接口,是一个为了在执行进程中和进程间调用时的高性能,而设计的轻量级远程调用机制的核心部分。这个接口描述了和远程对 象交互的抽象协议。不要直接实现这个接口,而是通过继承 Binder来 实现。
The key IBinder API is transact()
matched by Binder.onTransact()
. These methods allow you to send a call to an IBinder object and receive a call coming in to a Binder object, respectively. This transaction API is synchronous, such that a call to transact()
does not return until the target has returned from Binder.onTransact()
; this is the expected behavior when calling an object that exists in the local process, and the underlying inter-process communication (IPC) mechanism ensures that these same semantics apply when going across processes.
IBinder的关键API是与 Binder.on
The data sent through transact() is a Parcel
, a generic buffer of data that also maintains some meta-data about its contents. The meta data is used to manage IBinder object references in the buffer, so that those references can be maintained as the buffer moves across processes. This mechanism ensures that when an IBinder is written into a Parcel and sent to another process, if that other process sends a reference to that same IBinder back to the original process, then the original process will receive the same IBinder object back. These semantics allow IBinder/Binder objects to be used as a unique identity (to serve as a token or for other purposes) that can be managed across processes.
通过transact()送出的数据是一个 Parcel, 一个通常的数据缓冲器,并且包含一些关于他内容的元数据。这些元数据用来在缓冲期内管理IBinder对象的引用,以至于那些引用可以在缓冲器穿越进程时 被包含进来。这个机制可以确保了当一个IBinder被写入一个Parcel被传送给其他进程时,如果其他进程返回给原先进程同样的IBinder引用, 原 先的进程接收同样的IBinder对象。这些语义允许IBinder/Binder对象被用作进程间切换时管理的唯一的身份(作为一个标志或其他目的来服 务)。
The system maintains a pool of transaction threads in each process that it runs in. These threads are used to dispatch all IPCs coming in from other processes. For example, when an IPC is made from process A to process B, the calling thread in A blocks in transact() as it sends the transaction to process B. The next available pool thread in B receives the incoming transaction, calls Binder.onTransact() on the target object, and replies with the result Parcel. Upon receiving its result, the thread in process A returns to allow its execution to continue. In effect, other processes appear to use as additional threads that you did not create executing in your own process.
系统在每一个事务线程运行的进程里维护了一个事务线程池。这些线程用来分发所有来自其他进程的IPCs。例如,当一个IPC从A进程到 B进程被建立,在A里的调用线程用transact()向进程B发出一个事务。在B中下一个有效的线程池接收到进入的事务,对目标对象调用 Binder.on
The Binder system also supports recursion across processes. For example if process A performs a transaction to process B, and process B while handling that transaction calls transact() on an IBinder that is implemented in A, then the thread in A that is currently waiting for the original transaction to finish will take care of calling Binder.onTransact() on the object being called by B. This ensures that the recursion semantics when calling remote binder object are the same as when calling local objects.
Binder系统也支持进程间的递归调用。例如,如果进程A执行一个到进程B的事务,并且进程B在处理这个事务时对一个在A里实现的 IBinder调用transact(),然后在A里的正在等待原先事务结束的线程,将会关心对被B调用的对象调用Binder.on
当和远程对象打交道时,你经常想要找出何时他们不再有效。下面有三条可以被确定的方式:
- 当你试图对一个所属进程已经不存在的IBinder调用transact() 方法时,会抛出一个RemoteException 异常。
- pingBinder() 可以被调用,并且如果远程进程不再存在会返回假。
- linkToDeath() 方法可以用来用这个IBinder注册一个 IBinder.DeathRecipient , 当他包含的进程消失的时候被调用。
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.apis.app;
import com.example.android.apis.R;
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.RemoteException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class RemoteServiceBinding extends Activity {
/** The primary interface we will be calling on the service. */
IRemoteService mService = null;
/** Another interface we use on the service. */
ISecondary mSecondaryService = null;
Button mKillButton;
TextView mCallbackText;
private boolean mIsBound;
/**
* 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(mUnbindListener);
mKillButton = (Button)findViewById(R.id.kill);
mKillButton.setOnClickListener(mKillListener);
mKillButton.setEnabled(false);
mCallbackText = (TextView)findViewById(R.id.callback);
mCallbackText.setText("Not attached.");
}
/**
* 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);
mKillButton.setEnabled(true);
mCallbackText.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(RemoteServiceBinding.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;
mKillButton.setEnabled(false);
mCallbackText.setText("Disconnected.");
// As part of the sample, tell the user what happened.
Toast.makeText(RemoteServiceBinding.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
/**
* Class for interacting with the secondary interface of the service.
*/
private ServiceConnection mSecondaryConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// Connecting to a secondary interface is the same as any
// other interface.
mSecondaryService = ISecondary.Stub.asInterface(service);
mKillButton.setEnabled(true);
}
public void onServiceDisconnected(ComponentName className) {
mSecondaryService = null;
mKillButton.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.
bindService(new Intent(IRemoteService.class.getName()),
mConnection, Context.BIND_AUTO_CREATE);
bindService(new Intent(ISecondary.class.getName()),
mSecondaryConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mCallbackText.setText("Binding.");
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
if (mIsBound) {
// 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(mSecondaryConnection);
mKillButton.setEnabled(false);
mIsBound = false;
mCallbackText.setText("Unbinding.");
}
}
};
private OnClickListener mKillListener = 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 (mSecondaryService != null) {
try {
int pid = mSecondaryService.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);
mCallbackText.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(RemoteServiceBinding.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) {
mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
}
};
private static final int BUMP_MSG = 1;
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
mCallbackText.setText("Received from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}
};
}
下面是service
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.apis.app;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.RemoteException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.widget.Toast;
import java.util.HashMap;
// Need the following import to get access to the app resources, since this
// class is in a sub-package.
import com.example.android.apis.R;
/**
* This is an example of implementing an application service that runs in a
* different process than the application. Because it can be in another
* process, we must use IPC to interact with it. The
* {@link RemoteServiceController} and {@link RemoteServiceBinding} classes
* show how to interact with the service.
*/
public class RemoteService extends Service {
/**
* This is a list of callbacks that have been registered with the
* service. Note that this is package scoped (instead of private) so
* that it can be accessed more efficiently from inner classes.
*/
final RemoteCallbackList<IRemoteServiceCallback> mCallbacks
= new RemoteCallbackList<IRemoteServiceCallback>();
int mValue = 0;
NotificationManager mNM;
@Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// Display a notification about us starting.
showNotification();
// While this service is running, it will continually increment a
// number. Send the first message that is used to perform the
// increment.
mHandler.sendEmptyMessage(REPORT_MSG);
}
@Override
public void onDestroy() {
// Cancel the persistent notification.
mNM.cancel(R.string.remote_service_started);
// Tell the user we stopped.
Toast.makeText(this, R.string.remote_service_stopped, Toast.LENGTH_SHORT).show();
// Unregister all callbacks.
mCallbacks.kill();
// Remove the next pending message to increment the counter, stopping
// the increment loop.
mHandler.removeMessages(REPORT_MSG);
}
@Override
public IBinder onBind(Intent intent) {
// Select the interface to return. If your service only implements
// a single interface, you can just return it here without checking
// the Intent.
if (IRemoteService.class.getName().equals(intent.getAction())) {
return mBinder;
}
if (ISecondary.class.getName().equals(intent.getAction())) {
return mSecondaryBinder;
}
return null;
}
/**
* The IRemoteInterface is defined through IDL
*/
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public void registerCallback(IRemoteServiceCallback cb) {
if (cb != null) mCallbacks.register(cb);
}
public void unregisterCallback(IRemoteServiceCallback cb) {
if (cb != null) mCallbacks.unregister(cb);
}
};
/**
* A secondary interface to the service.
*/
private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() {
public int getPid() {
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
}
};
private static final int REPORT_MSG = 1;
/**
* Our Handler used to execute operations on the main thread. This is used
* to schedule increments of our value.
*/
private final Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
// It is time to bump the value!
case REPORT_MSG: {
// Up it goes.
int value = ++mValue;
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).valueChanged(value);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
// Repeat every 1 second.
sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);
} break;
default:
super.handleMessage(msg);
}
}
};
/**
* Show a notification while this service is running.
*/
private void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.remote_service_started);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.stat_sample, text,
System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, LocalServiceController.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.remote_service_label),
text, contentIntent);
// Send the notification.
// We use a string id because it is a unique number. We use it later to cancel.
mNM.notify(R.string.remote_service_started, notification);
}
}
剩下的是两个aidl的,
package com.example.android.apis.app;
import com.example.android.apis.app.IRemoteServiceCallback;
/**
* Example of defining an interface for calling on to a remote service
* (running in another process).
*/
interface IRemoteService {
/**
* Often you want to allow a service to call back to its clients.
* This shows how to do so, by registering a callback interface with
* the service.
*/
void registerCallback(IRemoteServiceCallback cb);
/**
* Remove a previously registered callback interface.
*/
void unregisterCallback(IRemoteServiceCallback cb);
}
package com.example.android.apis.app;
/**
* Example of a callback interface used by IRemoteService to send
* synchronous notifications back to its clients. Note that this is a
* one-way interface so the server does not block waiting for the client.
*/
oneway interface IRemoteServiceCallback {
/**
* Called when the service has a new value for you.
*/
void valueChanged(int value);
}
上述的例子中,首先在activity中拿到了service进程中的binder,然后通过跨进程将activity的binder通过方法设置过去,随后在service调用activity的binder中的方法,RemoteCallbackList<>,这个作为一个listener
http://developer.android.com/reference/android/os/IBinder.html
http://developer.android.com/reference/android/os/Binder.html
http://home.lupaworld.com/home-space-uid-42237-do-blog-id-232429.html