zoukankan      html  css  js  c++  java
  • AIDL Service Android进程间通信机制

    转载出处:http://www.apkbus.com/home.php?mod=space&do=blog&uid=664680&id=59465

    我们知道,在Android app中的众多activity,service等组件可以运行在同一进程中,也可以运行在不同进程中。当组件运行在同一进程中进行通信就显得比较简单,在之前的Android线程间通信机制中已经讲过了;而当它们运行在不同的进程 中时,就需要使用我们本文中所要介绍的Binder机制了。

    Binder作为一种进程间通信机制,负责提供远程调用的功能(RPC),它的系统组件主要包括四种:Client, Server, ServiceManager, Binder Driver. 它们之间的关系如下图所示:

    1.gif

    从图中我们可以看出,Client, Server, ServiceManager运行在系统的用户态,而Binder Driver运行在内核态。为了完成Client端到Server端的通信任务,用户空间的需要操作Binder Driver提供的/dev/binder文件来完成交互。那么ServiceManager的工作是什么呢?ServiceManager负责管理Server并向Client端提供一个Server的代理接口(proxy)。通过代理接口中定义的方法,Client端就可以使用Server端提供的服务了。整个过程如下:

      • Client端调用代理接口的方法,将Client的参数打包为parcel对象发送给内核空间中BinderDriver;

      • Server端读取到BinderDriver中的请求数据,将parcel对象解包并处理;

      • 处理好后,将处理结果打包返回给BinderDriver,再交给Client端。

    另外,Client端与Server端的调用过程是同步的,即在Server返回结果之前,Client端是阻塞的。调用过程如下所示:

    OK,下面我们通过AIDL的一个例子来分析一下以上过程时如何进行的。

    AIDL小栗子

    首先,创建一个aidl文件“ICalculateAIDL.aidl”,这个接口里面定义了要对外提供的服务,我们这里定义了计算加法和减法的函数:

    package com.cqumonk.calculate.aidl;
    
    interface ICalculateAIDL {
        int add(int a,int b);    
        int minus(int a,int b);
    }

    创建好后,rebuild一下我们的项目,可以生成同名的java文件。这是一个接口,里面包含了一个名为Stub的静态抽象类,以及我们在aidl文件中定义的加减法函数。 里面详细代码在后面分析,接口文件结构如下:

    3.png

    然后我们需要实现服务端方面的功能,创建一个service,我们顺手把生命周期中各方法都打印出来,并完成加法和减法函数的实现:

    public class CalculateService extends Service {  
      private static final String TAG="SERVER";  
      public CalculateService() {  
      }  
      @Override  
      public void onCreate() {    
        super.onCreate();    
        Log.e(TAG,"OnCreate");  
      }  
      @Override  
      public int onStartCommand(Intent intent, int flags, int startId) {    
        Log.e(TAG,"onStartCommand");    
        return super.onStartCommand(intent, flags, startId);  
      }  
      @Override  
      public IBinder onBind(Intent intent) {    
        Log.e(TAG,"onBind");    
        return mBinder;  
      }  
      @Override  
      public void onDestroy() {    
        super.onDestroy();    
        Log.e(TAG,"onDestroy");  
      }  
      @Override  
      public boolean onUnbind(Intent intent) {    
        Log.e(TAG,"onUnbind");    
        return super.onUnbind(intent);  
      }  
      @Override  
      public void onRebind(Intent intent) {    
        Log.e(TAG,"onRebind");    
        super.onRebind(intent);  
      }  
      private final ICalculateAIDL.Stub mBinder=new ICalculateAIDL.Stub(){    
        @Override    
        public int add(int a, int b) throws RemoteException {      
          return a+b;    
        }    
        @Override    
        public int minus(int a, int b) throws RemoteException {      
          return a-b;    
        }  
      };
    }

    在service中,我们使用刚刚生成的ICalculateAIDL.Stub静态抽象类创建了一个Binder对象,实现了其中有关计算的业务方法,并在onBind方法中返回它。另外我们还需要在manifest文件中对该服务进行注册:

    <service  
      android:name=".CalculateService"  
      android:enabled="true"  
      android:exported="true" >  
      <intent-filter>    
        <action android:name="com.cqumonk.adil.calculate"/>    
        <category android:name="android.intent.category.DEFAULT"/>  
      </intent-filter>
    </service>

    这时候,我们的服务端工作就完成了。我们开始编写客户端的代码来调用这个服务。首先,我们定义一个activity,包含四个按钮:

    4.png

    然后我们可以在activity中启动之前定义好的service并调用它所提供的服务:

    public class MainActivity extends Activity implements View.OnClickListener { 
      Button mBind; 
      Button mUnbind;  
      Button mAdd;  
      Button mMinus;  
      TextView txt_res;  
      private static final String TAG="CLIENT";  
      private ICalculateAIDL mCalculateAIDL; 
      private boolean binded=false; 
      @Override  
      protected void onCreate(Bundle savedInstanceState) {    
        super.onCreate(savedInstanceState);    
        setContentView(R.layout.activity_main);    
        mBind= (Button) findViewById(R.id.btn_bind);    
        mUnbind= (Button) findViewById(R.id.btn_unbind);    
        mAdd= (Button) findViewById(R.id.btn_add);    
        mMinus= (Button) findViewById(R.id.btn_minus);    
        txt_res= (TextView) findViewById(R.id.txt_res);    
        mBind.setOnClickListener(this);    
        mUnbind.setOnClickListener(this);    
        mAdd.setOnClickListener(this);    
        mMinus.setOnClickListener(this);  
      } 
      @Override  
      protected void onStop() {    
        super.onStop();   
        unbind();  
      }  
      private void unbind(){    
        if (binded){      
          unbindService(mConnection);      
          binded=false;    
        }  
      }  
      @Override  
      public void onClick(View v) {    
        int id=v.getId();   
        switch (id){      
          case R.id.btn_bind:        
            Intent intent=new Intent();       
            intent.setAction("com.cqumonk.adil.calculate");        
            bindService(intent,mConnection, Context.BIND_AUTO_CREATE);        
            break;      
          case R.id.btn_unbind:        
            unbind();        
            break;      
          case R.id.btn_add:        
            if(mCalculateAIDL!=null){          
              try {            
                int res=mCalculateAIDL.add(3,3);            
                txt_res.setText(res+"");          
              } catch (RemoteException e) {            
                e.printStackTrace();          
              }        
            }else{          
              Toast.makeText(this,"please rebind",Toast.LENGTH_SHORT).show();        
            }        
            break;      
          case R.id.btn_minus:        
            if(mCalculateAIDL!=null){          
              try {           
              int res=mCalculateAIDL.minus(9,4);           
              txt_res.setText(res+"");          
             }catch (RemoteException e) {            
              e.printStackTrace();         
             }        
           }else{         
             Toast.makeText(this,"please rebind",Toast.LENGTH_SHORT).show();        
           }        
           break;    
        }  
      } 
      private ServiceConnection mConnection=new ServiceConnection() {    
        @Override    
        public void onServiceConnected(ComponentName name, IBinder service) {      
          Log.e(TAG,"connect");      
          binded=true;      
          mCalculateAIDL=ICalculateAIDL.Stub.asInterface(service);    
        }    
        @Override    
        public void onServiceDisconnected(ComponentName name) {     
          Log.e(TAG,"disconnect");      
          mCalculateAIDL=null;      
          binded=false;    
        }  
      };
    }

    当我们点击绑定按钮,观察日志:

    5.png

    点击加法和减法按钮,都可以完成计算并返回值。然后点击解绑按钮:

    6.png

    我们并未发现有连接断开的日志打印,这时候,我们继续点击加减法按钮,发现仍然可以完成计算功能,这又是为毛呢?我们看到unbind和destroy的日志打印,说明连接已经断开,service已经被销毁,但是我们返回的stub对象仍然是可以继续使用的。而并不是说service仍然在运行。

    过程分析

    首先,我们调用bindservice方法来启动了service:

    一方面连接成功时调用了serviceConnection的onServiceConnected方法。我们从该方法中获取到一个Binder对象(注意,这个binder并不是我们在service中实现的那个哦。当service时本apk中的service时,这里返回的是同一个binder),我们通过此binder来与server进行通信。为了区分,我们称为clientBinder。

    public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e(TAG,"connect");
                binded=true;
                mCalculateAIDL= ICalculateAIDL.Stub.asInterface(service);       
    }

    另一方面,onBind方法返回了我们实现的Stub对象,其实也是一个Binder,用于和Client进行通信。 (之前我们定义了一个aidl接口文件,并根据它生成了ICalculateAIDL接口。这个接口中有我们定义的两个方法以及一个 静态抽象内部类Stub,它是一个Binder的子类。)我们来看一下Stub是如何定义的:

    public static abstract class Stub extends android.os.Binder implements com.cqumonk.calculate.aidl.ICalculateAIDL

    它继承了Binder类也实现了ICalculateAIDL接口, 我们实现了定义的add和minus方法。我们仍然要强调一下它并不是clientBinder,在负责与clientBinder进行通信交互的同时,它也维护了service描述符与服务端service的映射。

    我们在Client中的onServiceConnected里调用了stub对象的asInterface方法,并将之前得到的clientBinder传入:

    public static com.cqumonk.calculate.aidl.ICalculateAIDL asInterface(android.os.IBinder obj) {            if ((obj == null)) {                
              return null;
                }
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);//根据包名获取本地实现的一个接口的实例,如果是本地service则可以获取到
                if (((iin != null) && (iin instanceof com.cqumonk.calculate.aidl.ICalculateAIDL))) {                    return ((com.cqumonk.calculate.aidl.ICalculateAIDL) iin);  //如果得到的实例是ICalculateAIDL的对象,则返回
                }            
                return new com.cqumonk.calculate.aidl.ICalculateAIDL.Stub.Proxy(obj);//如果无法得到本地实现的对象则会返回一个代理对象}

    在这个方法中,首先在系统中查找注册的的service,如果没有找到,那么一定是别的apk实现的service,于是返回一个此service的静态代理类对象供Client调用。 我们来看一下这个代理,创建时我们将clientBinder传入了,同时也它实现了ICalculateAIDL接口:

    private static class Proxy implements com.cqumonk.calculate.aidl.ICalculateAIDL {      
          private android.os.IBinder mRemote;      
          Proxy(android.os.IBinder remote) {        
            mRemote = remote;      
          }      
          @Override      
          public android.os.IBinder asBinder() {        
            return mRemote;      
          }      
          public java.lang.String getInterfaceDescriptor() {        
            return DESCRIPTOR;      }      
          @Override      
          public int add(int a, int b) 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);          
              _data.writeInt(a);  //将参数打包         
              _data.writeInt(b);          
              mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);  //调用binderDriver的提供的方法将参数发给服务端          
              _reply.readException();          
              _result = _reply.readInt();  //读取到返回结果        
            } finally {          
              _reply.recycle();          
              _data.recycle();        
            }        
            return _result;      
           }      
           @Override      
           public int minus(int a, int b) 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);          
               _data.writeInt(a);          
               _data.writeInt(b);          
               mRemote.transact(Stub.TRANSACTION_minus, _data, _reply, 0);          
               _reply.readException();          
               _result = _reply.readInt();        
             } finally {          
               _reply.recycle();         
               _data.recycle();        
             }        
             return _result;      
           }    
         }

    代理中也实现了ICalculateAIDL接口定义的方法,我们以add方法为例,里面将参数打包发送给Server端。在Server端收到请求后,会调用service中我们实现的那个stub对象(mBinder)的onTransact方法:

    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_add: {          
             data.enforceInterface(DESCRIPTOR);          
             int _arg0;          
             _arg0 = data.readInt();          
             int _arg1;          
             _arg1 = data.readInt();         
             int _result = this.add(_arg0, _arg1); //调用我们实现好的方法          
             reply.writeNoException();          
             reply.writeInt(_result); //把结果返回          
             return true;        
           }        
           case TRANSACTION_minus: {          
             data.enforceInterface(DESCRIPTOR);          
             int _arg0;          
             _arg0 = data.readInt();          
             int _arg1;          
             _arg1 = data.readInt();          
             int _result = this.minus(_arg0, _arg1);          
             reply.writeNoException();          
             reply.writeInt(_result);          
             return true;       
            }      
          }      
          return super.onTransact(code, data, reply, flags);    
        }

    调用完成后把结果打包返回给Poxy处理,最后返回给客户端。

    总结

    由上面的例子我们可以看出,在跨进程通信的时候,Client端使用的Poxy里面封装了一个binder与Server端的stub(也是一个binder对象)进行交互,两个binder作为接口调用BinderDriver的transact来发送数据包,以及onTransact接收处理数据包。

    失败是什么?没有什么,只是更走近成功一步;成功是什么?就是走过了所有通向失败的路,只剩下一条路,那就是成功的路。
  • 相关阅读:
    计算机
    产品经理
    Vue router-link路由不同的写法,不一样的效果
    elementui样式引入方法
    格式化启动盘win10
    删除mongdb中的数据库
    google浏览器快速清除历史记录
    js动画之弹球打砖块小游戏
    js动画之面向对象二
    js动画之面向对象一
  • 原文地址:https://www.cnblogs.com/android-blogs/p/4832975.html
Copyright © 2011-2022 走看看