通常我们会在一个acitivity中通过调用bindService来绑定一个服务,但当绑定的服务和当前的activity属于不同的进程时,这种情况属于远程服务绑定。
1.所绑定的service在本包内,但是在AndroidManifest.xml中指定service为一个进程(运行时为单独一个进程),就是远程绑定。
2.所绑定的service在本包外(运行时为单独一个进程),也是远程绑定。
示例:
1.首先我们准备一个简单的AIDL文件,定义远程服务提供的功能接口。
@IHelloService.aidl
package android.hello.service;
interface IHelloService
{
void setVal(int val);
int getVal();
}
Android的编译系统会自动生成对应的IHelloService.java。Android的框架层中定义了很多AIDL文件,其编译后自动生成的java文件放到了out\target\common\obj\JAVA_LIBRARIES\framework_intermediates\src。比如frameworks\base\core\java\android\app\IServiceConnection.aidl对应的java文件out\target\common\obj\JAVA_LIBRARIES\framework_intermediates\src\core\java\android\app\IServiceConnection.java,前提是你有进行过编译。而Eclipse中就更简单了,直接在工程的gen目录下就能看到,和R.java同一目录。
@IHello.java
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: F:\\android\\workspace\\binder_remote_service\\helloService\\src\\android\\hello\\service\\IHelloService.aidl
*/
package android.hello.service;
public interface IHelloService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.hello.service.IHelloService
{
private static final java.lang.String DESCRIPTOR = "android.hello.service.IHelloService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an android.hello.service.IHelloService interface,
* generating a proxy if needed.
*/
public static android.hello.service.IHelloService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof android.hello.service.IHelloService))) {
return ((android.hello.service.IHelloService)iin);
}
return new android.hello.service.IHelloService.Stub.Proxy(obj);
}
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_setVal:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
this.setVal(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getVal:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getVal();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements android.hello.service.IHelloService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
public void setVal(int val) 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(val);
mRemote.transact(Stub.TRANSACTION_setVal, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public int getVal() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getVal, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_setVal = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getVal = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void setVal(int val) throws android.os.RemoteException;
public int getVal() throws android.os.RemoteException;
}
如果你对binder熟悉的话,很快就能看懂上面文件的内容,因为服务stub和代理proxy是一对好基友。AIDL为我们省了很多事,它让我们在对binder似懂非懂的情况下也来去自如。
2.为了方便描述,我就称服务所在的一边为“服务端”吧。
@HelloService.java
package android.hello.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class HelloService extends Service{
private int mVal = 0;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return myRemoteServiceStub;//null;
}
private IHelloService.Stub myRemoteServiceStub = new IHelloService.Stub(){
@Override
public void setVal(int val) throws RemoteException {
// TODO Auto-generated method stub
Log.i("HELLO_SERVICE", "setVal(5)");
mVal = val;
}
@Override
public int getVal() throws RemoteException {
// TODO Auto-generated method stub
return mVal;
}
};
public void onCreate(){
super.onCreate();
Log.i("HelloService", "-----------onCreate()----------");
}
}
请注意这里的onBind()里返回了我们的服务,之后AMS会将其传给接下来的“客户端”。
参考:
@activityThread.java(实际上是后面的客户端的主线程,AMS之所以能访问,只要在看下ApplicationThread的设计就能很快发现这又是binder的功劳)
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
try {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to bind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}
@ActivityManagerService.java
public void publishService(IBinder token, Intent intent, IBinder service) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
throw new IllegalArgumentException("Invalid service token");
}
ServiceRecord r = (ServiceRecord)token;
final long origId = Binder.clearCallingIdentity();
if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r
+ " " + intent + ": " + service);
if (r != null) {
Intent.FilterComparison filter
= new Intent.FilterComparison(intent);
IntentBindRecord b = r.bindings.get(filter);
if (b != null && !b.received) {
b.binder = service;
b.requested = true;
b.received = true;
if (r.connections.size() > 0) {
Iterator<ArrayList<ConnectionRecord>> it
= r.connections.values().iterator();
while (it.hasNext()) {
ArrayList<ConnectionRecord> clist = it.next();
for (int i=0; i<clist.size(); i++) {
ConnectionRecord c = clist.get(i);
if (!filter.equals(c.binding.intent.intent)) {
if (DEBUG_SERVICE) Slog.v(
TAG, "Not publishing to: " + c);
if (DEBUG_SERVICE) Slog.v(
TAG, "Bound intent: " + c.binding.intent.intent);
if (DEBUG_SERVICE) Slog.v(
TAG, "Published intent: " + intent);
continue;
}
if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
try {
c.conn.connected(r.name, service);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + r.name +
" to connection " + c.conn.asBinder() +
" (in " + c.binding.client.processName + ")", e);
}
}
}
}
}
serviceDoneExecutingLocked(r, mStoppingServices.contains(r));
Binder.restoreCallingIdentity(origId);
}
}
}
@LoadedApk.java
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
}
public void connected(ComponentName name, IBinder service) throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service);
}
}
}
public void connected(ComponentName name, IBinder service) {
if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 0));
} else {
doConnected(name, service);
}
}
public void doConnected(ComponentName name, IBinder service) {
ServiceDispatcher.ConnectionInfo old;
ServiceDispatcher.ConnectionInfo info;
synchronized (this) {
if (mForgotten) {
// We unbound before receiving the connection; ignore
// any connection received.
return;
}
old = mActiveConnections.get(name);
if (old != null && old.binder == service) {
// Huh, already have this one. Oh well!
return;
}
if (service != null) {
// A new service is being connected... set it all up.
mDied = false;
info = new ConnectionInfo();
info.binder = service;
info.deathMonitor = new DeathMonitor(name, service);
try {
service.linkToDeath(info.deathMonitor, 0);
mActiveConnections.put(name, info);
} catch (RemoteException e) {
// This service was dead before we got it... just
// don't do anything with it.
mActiveConnections.remove(name);
return;
}
} else {
// The named service is being disconnected... clean up.
mActiveConnections.remove(name);
}
if (old != null) {
old.binder.unlinkToDeath(old.deathMonitor, 0);
}
}
// If there was an old service, it is not disconnected.
if (old != null) {
mConnection.onServiceDisconnected(name);
}
// If there is a new service, it is now connected.
if (service != null) {
mConnection.onServiceConnected(name, service);
}
}
3.同样称绑定服务的Activity这边为“客户端”吧。
@testActivity.java
package android.test;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.hello.service.IHelloService;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
public class Binder_testActivity extends Activity {
/** Called when the activity is first created. */
private LinearLayout layout;
private TextView tv;
private IHelloService mIHelloService;
private ServiceConnection conn = new ServiceConnection(){
@Override
synchronized public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
mIHelloService = IHelloService.Stub.asInterface(service);
Log.i("BINDER_TEST", "onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
mIHelloService = null;
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
setContentView(layout);
Intent intent = new Intent();
intent.setAction("android.hello.IHelloService");
bindService(intent, conn, Service.BIND_AUTO_CREATE);
Button bt1 = new Button(this);
bt1.setText("RPC:setVal(5)");
bt1.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(mIHelloService != null){
try {
Log.i("BINDER_TEST", "mIHelloService = " + mIHelloService);
mIHelloService.setVal(5);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
Button bt2 = new Button(this);
bt2.setText("RPC:getVal()");
bt2.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(mIHelloService != null){
try {
//mIHelloService.getVal();
tv.setText("the value from remote service: " + mIHelloService.getVal());
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
tv = new TextView(this);
tv.setText("the value from remote service: ... ");
layout.addView(bt1);
layout.addView(bt2);
layout.addView(tv);
}
}
上面的ServiceConnection比较厉害,用于自身和AMS的通信(也是binder机制)。而AMS通过binder,最终以onServiceConnected(ComponentName name, IBinder service)中的参数service将前面获得的服务传到了当前的Acitivity,但我们还需要调用IHelloService.Stub.asInterface(service)进一步转化成服务的代理才能使用。至此符合了不同java进程间是不能直接访问的,而作为提供java进程间通信机制的binder,采用了C-S模式,客户端只能通过服务的代理来访问对方。