Android Service那些不得不说的事-之二(Bound Service的实现方式)

To provide binding for service, you must implement the onBind() method. This method returns anIBinder object that defines the interface that clients can use to interact with the service.

三种不同的方式,说白了就是通过onBind()返回三种不同类型的IBinder object。
一、可以返回自定义的Binder(继承自Binder),并且在其中提供public methods that the client can call。
通常情况下,we returns the current Service instance, or an instance of another class hosted by the service with public methods theclient can call.
[html] view plaincopy
private final IBinder mBinder = new LocalBinder(); 
public class LocalBinder extends Binder { 
        LocalService getService() { 
            // Return this instance of LocalService so clients can call public methods 
            return LocalService.this; 
        } 
    } 
优点:

1. 使用简单,client在onServiceConnected()拿到Binder之后,可以直接cast到原本的类型,进行使用。

缺点:

1. 必须在同一个进程中使用,因为一般自定义的Binder不会按照IPC的要求实现Binder中的接口,如何支持IPC,请参看AIDL实现的Binder类。
注意:

1. 此种方法调用service的function时,就跟一个普通的函数调用一样,在当前线程执行,而不像IPC那样,service端的function都是在binder thread中执行。

三、使用AIDL,onBind()返回ServiceInterface.Stub mBinder = new ServiceInterface.Stub(){ }。

编译工具会根据.aidl文件生成一个.java文件,里面有AIDL实现的Binder类。例如,我们提供的.aild文件如下:

[html] view plaincopy
package com.baidu.test.aidl; 
import com.baidu.test.aidl.ClientInterface; 
 
interface ServiceInterface {   
    void register(ClientInterface client);   
    void serviceFun(); 
    void invokeClientCallBack(); 


系统帮我们生成的.java文件如下:
[html] view plaincopy
package com.baidu.test.aidl; 
 
public interface ServiceInterface extends android.os.IInterface { 
    public void register(com.baidu.test.aidl.ClientInterface client) throws android.os.RemoteException; 
    public void serviceFun() throws android.os.RemoteException; 
    public void invokeClientCallBack() throws android.os.RemoteException; 
 
    // ========================  Stub用在service端  ================================ 
    public static abstract class Stub extends android.os.Binder implements com.baidu.test.aidl.ServiceInterface { 
        private static final java.lang.String DESCRIPTOR = "com.baidu.test.aidl.ServiceInterface"; 
         
        static final int TRANSACTION_register = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 
        static final int TRANSACTION_serviceFun = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); 
        static final int TRANSACTION_invokeClientCallBack = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); 
         
        public Stub() { 
            // from Binder, Convenience method for associating a specific interface with the Binder.  
            // After calling, queryLocalInterface() will return the given owner IInterface  
            // when the corresponding descriptor is requested.  
            <span style="color:#FF0000;"><strong>this.attachInterface(this, DESCRIPTOR);</strong></span>  
<pre name="code" class="html">            // public void attachInterface (IInterface owner, String descriptor) 
        } 
 
        public static com.baidu.test.aidl.ServiceInterface asInterface(android.os.IBinder obj) { 
            if ((obj==null)) { 
                return null; 
            } 
             
            //由于attachInterface()是在service调用的,所以client调用queryLocalInterface()会返回null,从而在client端使用的是Proxy。 
            <span style="color:#FF0000;"><strong>android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);</strong></span> 
            if (((iin!=null)&&(iin instanceof com.baidu.test.aidl.ServiceInterface))) { 
                return ((com.baidu.test.aidl.ServiceInterface)iin); 
            } 
             
            <span style="color:#FF0000;"><strong>return new com.baidu.test.aidl.ServiceInterface.Stub.Proxy(obj);</strong></span> 
        } 
         
        @Override // from IInterface 
        public android.os.IBinder asBinder() { 
            return this; 
        } 
     
        @Override // from Binder 
        // <span style="color:#FF0000;"><strong>service端的Stub实现了onTransact(),service在其中处理client发出的调用, 
</strong><span style="color:#000000;">        //</span><strong> 而client端用的Proxy就没有实现onTransact(),而只是调用了transact()。</strong></span> 
        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_register: 
                { 
                    data.enforceInterface(DESCRIPTOR); 
                    com.baidu.test.aidl.ClientInterface _arg0; 
                    _arg0 = com.baidu.test.aidl.ClientInterface.Stub.asInterface(data.readStrongBinder()); 
                    this.register(_arg0); 
                    reply.writeNoException(); 
                    return true; 
                } 
                case TRANSACTION_serviceFun: 
                { 
                    data.enforceInterface(DESCRIPTOR); 
                    this.serviceFun(); 
                    reply.writeNoException(); 
                    return true; 
                } 
                case TRANSACTION_invokeClientCallBack: 
                { 
                    data.enforceInterface(DESCRIPTOR); 
                    this.invokeClientCallBack(); 
                    reply.writeNoException(); 
                    return true; 
                } 
            } 
            return super.onTransact(code, data, reply, flags); 
        } 
         
        // ========================  Proxy用在client端  ================================ 
        private static class Proxy implements com.baidu.test.aidl.ServiceInterface { 
            private android.os.IBinder mRemote; 
             
            Proxy(android.os.IBinder remote) { 
                mRemote = remote; 
            } 
             
            @Override // from IInterface 
            public android.os.IBinder asBinder() { 
                return mRemote; 
            } 
             
            @Override // from IBinder, Get the canonical name of the interface supported by this binder. 
            public java.lang.String getInterfaceDescriptor() { 
                return DESCRIPTOR; 
            } 
             
            public void register(com.baidu.test.aidl.ClientInterface client) 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.writeStrongBinder((((client!=null))?(client.asBinder()):(null))); 
                    <span style="color:#FF0000;"><strong>mRemote.transact(Stub.TRANSACTION_register, _data, _reply, 0);</strong></span> 
                    _reply.readException(); 
                } finally { 
                    _reply.recycle(); 
                    _data.recycle(); 
                } 
            } 
             
            public void serviceFun() throws android.os.RemoteException { 
                android.os.Parcel _data = android.os.Parcel.obtain(); 
                android.os.Parcel _reply = android.os.Parcel.obtain(); 
                try { 
                    _data.writeInterfaceToken(DESCRIPTOR); 
                    <strong><span style="color:#FF0000;">mRemote.transact(Stub.TRANSACTION_serviceFun, _data, _reply, 0);</span></strong> 
                    _reply.readException(); 
                } finally { 
                    _reply.recycle(); 
                    _data.recycle(); 
                } 
            } 
 
            public void invokeClientCallBack() throws android.os.RemoteException { 
                android.os.Parcel _data = android.os.Parcel.obtain(); 
                android.os.Parcel _reply = android.os.Parcel.obtain(); 
                try { 
                _data.writeInterfaceToken(DESCRIPTOR); 
                <strong><span style="color:#FF0000;">mRemote.transact(Stub.TRANSACTION_invokeClientCallBack, _data, _reply, 0);</span></strong> 
                _reply.readException(); 
                } finally { 
                    _reply.recycle(); 
                    _data.recycle(); 
                } 
            } 
        } 
        // ========================  Proxy用在client端  end ================================ 
    } 
    // ========================  Stub用在service端 end ================================ 
}</pre> 
<pre></pre> 
service端相关代码如下:<pre name="code" class="html">    <span style="color:#FF0000;"><strong>private final ServiceInterface.Stub mBinder = new ServiceInterface.Stub()</strong></span> {   
        @Override   
        public void register(ClientInterface client) throws RemoteException { 
        }  
         
        @Override   
        public void serviceFun() throws RemoteException {   
        }   
           
        @Override   
        public void invokeClientCallBack() throws RemoteException { 
        }           
    };  
 
    @Override   
    public IBinder onBind(Intent t) {  
        <strong><span style="color:#FF0000;">return mBinder;</span></strong>   
    } </pre>client端的相关代码如下: 
<p></p> 
<p></p> 
<pre name="code" class="html">    <strong><span style="color:#FF0000;">private ServiceInterface mService; </span></strong>    
    private ServiceConnection mConnection = new ServiceConnection() {   
        public void onServiceConnected(ComponentName className, IBinder service) { 
            <strong><span style="color:#FF0000;">mService = ServiceInterface.Stub.asInterface(service);</span></strong>   
            try {   
                mService.register(mClient); 
            } catch (RemoteException e) { 
                e.printStackTrace(); 
            } 
        } 
         
        public void onServiceDisconnected(ComponentName className) { 
            mService = null; 
        }   
    };</pre>优点: 
<p></p> 
<p>1. 可以进行IPC。</p> 
<p>缺点:</p> 
<p>1. 使用复杂,需要编写.aidl文件,而且client端和service端都需要copy一份.aidl文件,以便编译生成对应的.java文件。</p> 
<p>2. 由于service端的函数是在binder thread中执行,而系统又帮我们维护了一个binder thread的线程池,所以我们需要在service的函数中考虑线程安全。<br> 
</p> 
<p>注意:</p> 
<p>1. 如果client和service在同一个进程,那么即使我们采用了AIDL,由系统生成的代码可以看到通过<span style="color:#000000"><strong>ServiceInterface.Stub.asInterface(service)</strong>取得的实例实际上是本地的instance,所以ADIL调用就会变得和一个普通函数调用一样了,也就意味了service端的函数不会在binder thread中执行,而是在当前线程执行了。</span></p> 
<p><span style="color:#000000">2. Objects are reference counted across processes.也就是说可能出现内存泄露。<br> 
</span></p> 
<p></p> 
<p><span style="font-size:18px"><strong>二、使用Messager,onBind()返回Messager.getBinder()。</strong></span></p> 
<p>Messager可以使用两中不同的方法创建:</p> 
<p></p> 
<pre name="code" class="html">public Messenger (Handler target) 
public Messenger (IBinder target)</pre> 
<p></p> 
<p>1. 通过传入一个handler创建,service端使用;</p> 
<p>2. 通过传入一个binder创建,client端使用。</p> 
<p>其实Messager的底层实现还是AIDL,只不过帮我们做了封装,使用更方便。</p> 
<p>service端的相关代码如下:</p> 
<p></p> 
<pre name="code" class="html">    <span style="color:#FF0000;"><strong>private ArrayList<Messenger> mClients = new ArrayList<Messenger>();</strong></span> 
     
    <strong><span style="color:#FF0000;">private Messenger mMessenger = new Messenger(new IncomingHandler()); </span></strong>        
 
    @Override   
    public IBinder onBind(Intent t) { 
        <strong><span style="color:#FF0000;">return mMessenger.getBinder();</span></strong>   
    }  
         
    class IncomingHandler extends Handler { 
        @Override 
        public void handleMessage(Message msg) { 
            Log("===> handleMessage()"); 
            LogThreadInfo(); 
            switch (msg.what) { 
                case MSG_REGISTER_CLIENT: 
                    <strong><span style="color:#FF0000;">mClients.add(msg.replyTo);</span></strong> 
                    break; 
                case MSG_UNREGISTER_CLIENT: 
                    <strong><span style="color:#FF0000;">mClients.remove(msg.replyTo);</span></strong> 
                    break; 
                case MSG_SERVICE_FUN_ALPHA: 
                    for (int i=mClients.size()-1; i>=0; i--) { 
                        try { 
                            <strong><span style="color:#FF0000;">mClients.get(i).send(Message.obtain(null,MSG_SERVICE_FUN_ALPHA));</span></strong> 
                        } catch (RemoteException e) { 
                            // The client is dead.  Remove it from the list; 
                            mClients.remove(i); 
                        } 
                    } 
                    break; 
                default: 
                    super.handleMessage(msg); 
            } 
        } 
    } </pre> 
<p></p> 
<p>client端的相关代码如下:</p> 
<p></p> 
<pre name="code" class="html">    <span style="color:#FF0000;"><strong>private Messenger mService = null;</strong></span> 
    <strong><span style="color:#FF0000;">final Messenger mMessenger = new Messenger(new IncomingHandler());</span></strong> 
     
    class IncomingHandler extends Handler { 
        @Override 
        public void handleMessage(Message msg) { 
            switch (msg.what) { 
                case MSG_SERVICE_FUN_ALPHA: 
                    break; 
                default: 
                    super.handleMessage(msg); 
            } 
        } 
    }           
  
    private ServiceConnection mConnection = new ServiceConnection() {   
        public void onServiceConnected(ComponentName className, IBinder service) { 
            <strong><span style="color:#FF0000;">mService = new Messenger(service);</span></strong> 
            try { 
                <span style="color:#FF0000;"><strong>Message msg = Message.obtain(null,MSG_REGISTER_CLIENT);</strong></span> 
                <strong><span style="color:#FF0000;">msg.replyTo = mMessenger;</span></strong> // 可选项,主要是用来向service注册client,从而实现双向IPC。 
                <span style="color:#FF0000;"><strong>mService.send(msg);</strong></span> 
            } catch (RemoteException e) { 
                e.printStackTrace(); 
            } 
        } 
         
        public void onServiceDisconnected(ComponentName className) {   
            mService = null; 
        }   
    }; </pre> 
<p></p> 
<p>优点:</p> 
<p>1. 比较方便,而且可以实现进程间通信。<br> 
</p> 
<p>缺点:</p> 
<p>1. 必须想办法将Message Id,在service和client之间共享。<br> 
</p> 
<p>注意:</p> 
<p>1. 由于service是在main thread中创建,所以我们的Messager(包括构造Messager所需要提供的Handler)都是在main thread中创建的,所以一般情况下,service端的handleMessage()是在main thread中执行的。当然由于Handler创建时可以自定义所用的Looper,所以我们可以在service的onCreate()里面创建自己的线程,并prepare()线程自己的Looper,然后就可以让service端的handleMessage()运行在我们自己的线程中了。<br> 
</p> 
<p><br> 
</p> 
<br> 

你可能感兴趣的:(android)