Android远程服务绑定

通常我们会在一个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模式,客户端只能通过服务的代理来访问对方。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值