• IPC机制之基本介绍


    Android的IPC机制(一)——AIDL的使用 

    IPC(interprocess communication):

    是指进程间通信,不同操作系统都有自己的一套IPC机制。
    如在Linux操作系统可以通过管道、信号量、消息队列、内存共享、套接字等进行进程间通信。Android中可通过Binder和socket来进行进程间的通信。
    既然需要进程通信,那么就必须有多个进程,在两个应用交互中必然出现多进程的情况。
    若在一个应用,可给四大组件在AndroidMenifest中指定android:process属性来实现不同的组件在不同进程中运行。

    AIDL简介:
     Android Interface Definition Language的缩写,AIDL 是一种IDL接口定义语言,用于生成可在Android设备上两个进程之间进行IPC的代码。如在一个进程中(如Activity)要调用另一个进程中(如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
    AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

    AIDL语法规则:
    默认情况下AIDL支持以下数据类型:
    • 所有Java的基本数据类型(例如: int, long,double, char, boolean等)
    • String和CharSequence
    • List:AIDL实际接收到的是ArrayList,并且List里面所有元素都必须被AIDL支持
    • Map: AIDL实际接收到的是HashMap,并且Map里面所有元素都必须被AIDL支持
    如果不是上面所述类型,我们必须要显示import进来,即使他们在同一个包中。
    当使用自定义的对象时必须实现Parcelable接口,Parcelable为对象序列化接口,效率比实现Serializable接口高。并且新建一个与该类同名的AIDL文件,声明他为Parcelable类型。
    定义AIDL接口还需要注意以下几点:
    • 方法可以有多个或没有参数,可以有返回值也可以为void
    • 在参数中,除了基本类型以外,我们必须为参数标上方向in, out, 或者 inout
    • 在AIDL文件中只支持方法,不支持静态常量

    一. AIDL用法:
     首先我们创建一个AIDL文件,在AndroidStudio中当我们创建一个AIDL文件时会自动为我们创件一个AILD文件夹,用于存放AIDL文件。创建完之后重新rebuild会自动生成aidl实现类。
    在下面例子中,将Service单独作为一个应用在系统中运行,在另一个用于访问Service的client也单独作为一个应用运行在系统中。这样保证了两个程序分别运行在两个进程中。

    AIDL简单用法:
      在Service中我们对客户端传来的两个整数做了一次加法运算并返回到客户端中。

    AIDL代码
    // ICalculate.aidl
    package com.ljd.aidl;
    // Declare any non-default types here with import statements
    interface ICalculate {
      /**
      * Demonstrates some basic types that you can use as parameters
      * and return values in AIDL.
      */
      int add(int first, int second);
    }
    服务端代码:
    package com.ljd.aidl.service;
    import android.app.Service;
    import android.content.Intent;
    import android.os.Binder;
    import android.os.IBinder;
    import android.os.RemoteException;
    import com.ljd.aidl.ICalculate;
    public class CalculateService extends Service {
        public CalculateService() {
        }
        private Binder mBinder = new ICalculate.Stub(){
            @Override
            public int add(int first, int second) throws RemoteException {
                return first + second;
            }
        };
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    }
    客户端代码
    package com.ljd.aidl.activity;
    import android.content.ComponentName;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import com.ljd.aidl.ICalculate;
    import com.ljd.aidl.client.R;
    public class Demo1Activity extends AppCompatActivity {
        private final String TAG = "DEMO1";
       
        private boolean mIsBindService;
        private ICalculate mCalculate;
        private ServiceConnection mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG,"bind success");
                Toast.makeText(Demo1Activity.this,"bind service success",Toast.LENGTH_SHORT).show();
                mCalculate = ICalculate.Stub.asInterface(service);
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {
            //重新绑定Service防止系统将服务进程杀死而产生的调用错误。
                bindService();
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_demo1);
            ButterKnife.bind(this);
            mIsBindService = false;
        }
        @Override
        protected void onDestroy() {
            unbindService();
            ButterKnife.unbind(this);
            super.onDestroy();
        }
        @OnClick({ R.id.bind_demo1_btn,R.id.unbind_demo1_btn,R.id.calculate_btn})
        public void onClickButton(View v) {
            switch (v.getId()){
                case R.id.bind_demo1_btn:
                    bindService();
                    break;
                case R.id.unbind_demo1_btn:
           Toast.makeText(this,"unbind service success").show();
                    unbindService();
                    break;
                case R.id.calculate_btn:
                    if (mIsBindService && mCalculate != null ){
                        try {
                            int result = mCalculate.add(2,4);
                            Log.d(TAG,String.valueOf(result));
            Toast.makeText(this,String.valueOf(result),).show();
                        } catch (RemoteException e) {
                        }
                    } else {
                        Toast.makeText(this,"not bind service",Toast.LENGTH_SHORT).show();
                    }
                    break;
            }
        }
        private void bindService(){
            Intent intent = new Intent();
            intent.setAction("com.ljd.aidl.action.CALCULATE_SERVICE");
            bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
            mIsBindService = true;
        }
        private void unbindService(){
            if(mIsBindService){
                mIsBindService = false;
                unbindService(mConnection);
            }
        }
    }

    2. AIDL高级用法:
      对于上面的例子,在AIDL接口中只是使用了一些Java的基本类型,对于AIDL文件并不是所有的类型都是可用的,那么在AIDL中究竟有哪些类型可以使用呢?
    演示:
      在计算机商店中需要采购笔记本进行销售,在服务端中我们添加两台笔记本,在客户端中我们为商店加购一台dell笔记本。

    实体类代码
      首先构建一个计算机实体类,包含笔记本的id,品牌,型号,并且实现Parcelable接口,在AndroidStudio中会为我们自动构造代码。 
    package com.ljd.aidl.entity;
    import android.os.Parcel;
    import android.os.Parcelable;
    public class ComputerEntity implements Parcelable{
        public int computerId;     //id
        public String brand;       //品牌
        public String model;       //型号
        public ComputerEntity(int computerId, String brand, String model) {
            this.brand = brand;
            this.computerId = computerId;
            this.model = model;
        }
        protected ComputerEntity(Parcel in) {
            computerId = in.readInt();
            brand = in.readString();
            model = in.readString();
        }
        public static final Creator<ComputerEntity> CREATOR = new Creator<ComputerEntity>() {
            @Override
            public ComputerEntity createFromParcel(Parcel in) {
                return new ComputerEntity(in);
            }
            @Override
            public ComputerEntity[] newArray(int size) {
                return new ComputerEntity[size];
            }
        };
        @Override
        public int describeContents() {
            return 0;
        }
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(computerId);
            dest.writeString(brand);
            dest.writeString(model);
        }
    }
    AIDL代码   在AIDL中对实体类进行声明,包名和文件名必须与实体类一致。在AndroidStudio中新建一个与实体类同名的AIDL文件会报错,需要先用一个其它名字,然后修改与实体类名一致即可。
    package com.ljd.aidl.entity; //包名必须和对用实体类的包名一致 // Declare any non-default types here with import statements parcelable ComputerEntity;  
    // 添加两个接口分别为添加一台笔记本和获取全部笔记本,在该文件中使用到了ComputerEntity类,显示的import进来。
    package com.ljd.aidl; import com.ljd.aidl.entity.ComputerEntity; // Declare any non-default types here with import statements interface IComputerManager { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void addComputer(in ComputerEntity computer); List<ComputerEntity> getComputerList(); }
    服务端代码
    package com.ljd.aidl.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import com.ljd.aidl.IComputerManager; import com.ljd.aidl.entity.ComputerEntity; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class ComputerService extends Service { private CopyOnWriteArrayList<ComputerEntity> mComputerList = new CopyOnWriteArrayList<>(); public ComputerService() { } private final IComputerManager.Stub mBinder = new IComputerManager.Stub() { @Override public void addComputer(ComputerEntity computer) throws RemoteException { mComputerList.add(computer); } @Override public List<ComputerEntity> getComputerList() throws RemoteException { return mComputerList; } }; @Override public void onCreate() { super.onCreate(); mComputerList.add(new ComputerEntity(0,"apple","macbookpro")); mComputerList.add(new ComputerEntity(1,"microsoft","surfacebook")); mComputerList.add(new ComputerEntity(2,"dell","XPS13")); } @Override public IBinder onBind(Intent intent) { return mBinder; } }   注意:在该类中使用了CopyOnWriteArrayList,CopyOnWriteArrayList能够自动进行线程同步。可是在AIDL中接收和返回的只能是ArrayList,其实AIDL支持的是抽象的List, 在Binder中会按照List访问数据并最终形成一个ArrayList,所以在AIDL中返回的还是一个ArrayList。 客户端代码 package com.ljd.aidl.activity;import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.os.Bundle;import com.ljd.aidl.IComputerManager; import com.ljd.aidl.client.R; import com.ljd.aidl.entity.ComputerEntity;public class Demo2Activity extends AppCompatActivity{ @Bind(R.id.show_linear) LinearLayout mShowLinear; private boolean mIsBindService; private IComputerManager mRemoteComputerManager; private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { if(mRemoteComputerManager != null){ mRemoteComputerManager.asBinder().unlinkToDeath(mDeathRecipient,0); mRemoteComputerManager = null; bindService(); } } }; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mIsBindService = true; Toast.makeText(Demo2Activity.this,"bind service success",Toast.LENGTH_SHORT).show(); mRemoteComputerManager = IComputerManager.Stub.asInterface(service); try { mRemoteComputerManager.asBinder().linkToDeath(mDeathRecipient,0); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { mRemoteComputerManager = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_demo2); ButterKnife.bind(this); mIsBindService = false; } @Override protected void onDestroy() { unbindService(); ButterKnife.unbind(this); super.onDestroy(); } @OnClick({R.id.bind_demo2_btn,R.id.unbind_demo2_btn,R.id.test_demo2_btn,R.id.clear_demo2_btn}) public void onClickButton(View v) { switch (v.getId()){ case R.id.bind_demo2_btn: bindService(); break; case R.id.unbind_demo2_btn: Toast.makeText(this,"unbind service success",Toast.LENGTH_SHORT).show(); unbindService(); break; case R.id.test_demo2_btn: if (!mIsBindService || mRemoteComputerManager == null){ Toast.makeText(this,"not bind service",Toast.LENGTH_SHORT).show(); return; } try { List<ComputerEntity> computerList = mRemoteComputerManager.getComputerList(); for (int i =0;i<computerList.size();i++){ String str = "computerId:" + String.valueOf(computerList.get(i).computerId) + " brand:" + computerList.get(i).brand + " model:" + computerList.get(i).model ; TextView textView = new TextView(this); textView.setText(str); mShowLinear.addView(textView); } } catch (RemoteException e) { e.printStackTrace(); } break; case R.id.clear_demo2_btn: mShowLinear.removeAllViews(); break; } } private void bindService(){ Intent intent = new Intent(); intent.setAction("com.ljd.aidl.action.COMPUTER_SERVICE"); mIsBindService = bindService(intent,mConnection, Context.BIND_AUTO_CREATE); } private void unbindService(){ if(!mIsBindService){ return; } mIsBindService = false; unbindService(mConnection); } }  由于Binder是有可能会意外死亡的,也就是Service所在进程被系统杀死,这时候我们调用Service的方法就会失败。 在第一个例子中我们通过onServiceDisconnected方法中重新绑定服务。在这个例子中我们采用了另外一种方法, 由于在Binder中提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath可以给Binder设置一个死亡代理,Binder死亡时回调binderDied方法, 在binderDied方法中我们重新绑定服务即可。

    3. AIDL用法拓展:

      当我们需要一种笔记本的时候,由于商店缺货,这时候我们会给卖家说一声,我所需要的这款笔记本到货后通知我。也就成了所谓的观察者模式。
      在Android系统中为我们提供了一个RemoteCallbackList,RemoteCallbackList是系统专门用来删除跨进程的listener接口,并且在RemoteCallbackList中自动实现了线程同步功能,下面看一下它的用法。
    演示: 
      客户端注册服务以后,服务端每隔三秒会添加一台笔记本,并通知给客户端显示。

    AIDL代码
      到货后的AIDL监听接口 
    package com.ljd.aidl;
    import com.ljd.aidl.entity.ComputerEntity;
    // Declare any non-default types here with import statements
    interface IOnComputerArrivedListener {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        void onComputerArrived(in ComputerEntity computer);
     }
    
     在IComputerManager接口中添加两个方法。显示importIOnComputerArrivedListener ,即使在同一个包下面。 
    // IComputerManagerObserver.aidl
    package com.ljd.aidl;
    import com.ljd.aidl.entity.ComputerEntity;
    import com.ljd.aidl.IOnComputerArrivedListener;
    // Declare any non-default types here with import statements
    interface IComputerManagerObserver {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
         void addComputer(in ComputerEntity computer);
         List<ComputerEntity> getComputerList();
         void registerUser(IOnComputerArrivedListener listener);
         void unRegisterUser(IOnComputerArrivedListener listener);
    }
    
    服务端代码
    import android.app.Service;
    import android.content.Intent;
    import android.os.Binder;
    import android.os.IBinder;
    import android.os.RemoteCallbackList;
    import android.os.RemoteException;
    import com.ljd.aidl.IComputerManagerObserver;
    import com.ljd.aidl.IOnComputerArrivedListener;
    import com.ljd.aidl.entity.ComputerEntity;
    import java.util.List;
    import java.util.concurrent.CopyOnWriteArrayList;
    import java.util.concurrent.atomic.AtomicBoolean;
    public class ComputerObserverService extends Service{
        public ComputerObserverService() {
        }
        private CopyOnWriteArrayList<ComputerEntity> mComputerList = new CopyOnWriteArrayList<>();
        private RemoteCallbackList<IOnComputerArrivedListener> mComputerArrivedListenerList = new RemoteCallbackList<>();
        private AtomicBoolean mIsServiceDestroy = new AtomicBoolean(false);
        private Binder mBinder = new IComputerManagerObserver.Stub(){
            @Override
            public void addComputer(ComputerEntity computer) throws RemoteException {
                mComputerList.add(computer);
            }
            @Override
            public List<ComputerEntity> getComputerList() throws RemoteException {
                return mComputerList;
            }
            @Override
            public void registerUser(IOnComputerArrivedListener listener) throws RemoteException {
                mComputerArrivedListenerList.register(listener);
            }
            @Override
            public void unRegisterUser(IOnComputerArrivedListener listener) throws RemoteException {
                mComputerArrivedListenerList.unregister(listener);
            }
        };
        @Override
        public void onCreate() {
            super.onCreate();
            mComputerList.add(new ComputerEntity(0,"apple","macbookpro"));
            mComputerList.add(new ComputerEntity(1,"microsoft","surfacebook"));
            mComputerList.add(new ComputerEntity(2,"dell","XPS13"));
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!mIsServiceDestroy.get()){
                        try {
                            Thread.currentThread().sleep(3000);
                            ComputerEntity computer = new ComputerEntity(mComputerList.size(),"******","******");
                            mComputerList.add(computer);
                            final int COUNT = mComputerArrivedListenerList.beginBroadcast();
                            //通知所有注册过的用户
                            for (int i=0;i<COUNT;i++){
                                IOnComputerArrivedListener listener = mComputerArrivedListenerList.getBroadcastItem(i);
                                if (listener != null){
                                    listener.onComputerArrived(computer);
                                }
                            }
                            mComputerArrivedListenerList.finishBroadcast();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
        @Override
        public void onDestroy() {
            super.onDestroy();
            mIsServiceDestroy.set(true);
        }
        }
      注意:RemoteCallbackList并不是一个List,所以我们不能像操作List一样操作RemoteCallbackList。
    并且遍历RemoteCallbackList时,beginBroadcast和finishBroadcast是配对使用的。
    
    客户端代码
    import android.content.ComponentName;import android.content.ServiceConnection;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.Message;
    import android.os.RemoteException;import android.widget.LinearLayout;
    import android.widget.TextView;
    import android.widget.Toast;
    import com.ljd.aidl.IComputerManagerObserver;
    import com.ljd.aidl.IOnComputerArrivedListener;
    import com.ljd.aidl.client.R;
    import com.ljd.aidl.entity.ComputerEntity;
    import java.util.List;
    import butterknife.Bind;
    import butterknife.ButterKnife;
    import butterknife.OnClick;
    public class Demo3Activity extends AppCompatActivity {
        @Bind(R.id.show_demo3_linear)
        LinearLayout mShowLinear;
        private boolean mIsBindService;
        private static final int MESSAGE_COMPUTER_ARRIVED = 1;
        private IComputerManagerObserver mRemoteComputerManager;
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case MESSAGE_COMPUTER_ARRIVED:
                        ComputerEntity computer = (ComputerEntity)msg.obj;
                        String str = "computerId:" + String.valueOf(computer.computerId) +
                                " brand:" + computer.brand +
                                " model:" + computer.model ;
                        TextView textView = new TextView(Demo3Activity.this);
                        textView.setText(str);
                        mShowLinear.addView(textView);
                        break;
                    default:
                        super.handleMessage(msg);
                        break;
                }
            }
        };
        private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
            @Override
            public void binderDied() {
                if(mRemoteComputerManager != null){
                    mRemoteComputerManager.asBinder().unlinkToDeath(mDeathRecipient,0);
                    mRemoteComputerManager = null;
                    bindService();
                }
            }
        };
        private ServiceConnection mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mIsBindService = true;
                Toast.makeText(Demo3Activity.this,"bind service success",Toast.LENGTH_SHORT).show();
                mRemoteComputerManager = IComputerManagerObserver.Stub.asInterface(service);
                try {
                    mRemoteComputerManager.asBinder().linkToDeath(mDeathRecipient,0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {
                mRemoteComputerManager = null;
            }
        };
        private IOnComputerArrivedListener mOnComputerArrivedListener = new IOnComputerArrivedListener.Stub(){
            @Override
            public void onComputerArrived(ComputerEntity computer) throws RemoteException {
                mHandler.obtainMessage(MESSAGE_COMPUTER_ARRIVED,computer).sendToTarget();
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_demo3);
            ButterKnife.bind(this);
            mIsBindService = false;
        }
        @Override
        protected void onDestroy() {
            unbindService();
            ButterKnife.unbind(this);
            super.onDestroy();
        }
        @OnClick({R.id.bind_demo3_btn,R.id.unbind_demo3_btn,R.id.test_demo3_btn,R.id.clear_demo3_btn})
        public void onClickButton(View v){
            switch (v.getId()){
                case R.id.bind_demo3_btn:
                    bindService();
                    break;
                case R.id.unbind_demo3_btn:
                    Toast.makeText(this,"unbind service success",Toast.LENGTH_SHORT).show();
                    unbindService();
                    break;
                case R.id.test_demo3_btn:
                    if (!mIsBindService || mRemoteComputerManager == null){
                        Toast.makeText(this,"not bind service",Toast.LENGTH_SHORT).show();
                        return;
                    }
                    try {
                        ComputerEntity computer = new ComputerEntity(3,"hp","envy13");
                        mRemoteComputerManager.addComputer(computer);
                        List<ComputerEntity> computerList = mRemoteComputerManager.getComputerList();
                        for (int i =0;i<computerList.size();i++){
                            String str = "computerId:" + String.valueOf(computerList.get(i).computerId) +
                                    " brand:" + computerList.get(i).brand +
                                    " model:" + computerList.get(i).model ;
                            TextView textView = new TextView(this);
                            textView.setText(str);
                            mShowLinear.addView(textView);
                        }
                        mRemoteComputerManager.registerUser(mOnComputerArrivedListener);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                case R.id.clear_demo3_btn:
                    mShowLinear.removeAllViews();
                    break;
            }
        }
        private void bindService(){
            Intent intent = new Intent();
            intent.setAction("com.ljd.aidl.action.COMPUTER_OBSERVER_SERVICE");
            mIsBindService = bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
        }
        private void unbindService(){
            if(!mIsBindService){
                return;
            }
            if (mRemoteComputerManager != null && mRemoteComputerManager.asBinder().isBinderAlive()){
                try {
                    mRemoteComputerManager.unRegisterUser(mOnComputerArrivedListener);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            unbindService(mConnection);
            mIsBindService = false;
        }
    }

    源码下载

  • 相关阅读:
    MySQL索引原理及慢查询优化
    MySQL单表百万数据记录分页性能优化
    linux下crontab命令的使用
    php递归读取目录
    php实现函数重载
    php数组常见的几种遍历方法
    ArtTemplate 使用笔记
    打算换工作的伙伴们,快来看啦,各种职位,随便挑咯...
    看看国外的javascript题目,你能全部做对吗?(分享)
    逛园子,看到个练习题,小试了一把(淘宝ued的两道小题)
  • 原文地址:https://www.cnblogs.com/awkflf11/p/16357232.html
Copyright © 2020-2023  润新知