IPC机制(一)

来源:互联网 发布:淘宝dota2齿樱价格 编辑:程序博客网 时间:2024/04/27 13:59

一、概念

IPC:inter-Process Communication,进程间通信或者跨进程通信。
例如:Bundle、文件共享、基于Binder的AIDL和Messenger、ContentProvider、Socket等
1、线程:CPU调度的最小单位
进程:一个执行单元,一般指一个程序或应用。一个进程可包含多个线程。
2、应用场景:
一个应用采用多线程,两个应用间通信。
3、开始多进程
(1)给四大组件在清单文件中指定Android:process属性,开启多进程。 默认进程的进程名是包名。 系统为每个应用分配一个唯一UID,UID相同的应用才能共享数据。 (拥有独立虚拟机,Application,内存空间)
(2)通过JNI在native层fork一个新的进程
通过adb shell ps | grep 包名 查看一个包名下所存在的进程信息
4、多进程的问题:
每个进程分配独立的虚拟机,不同虚拟机在内存分配上有不同地址空间,所以在访问同一个类对象产生多个副本。如果多进程之间通过内存同享数据会同享失败。
(1)静态成员和单例模式失效
(2)线程同步机制失效 (不在同一内存,无法锁住同一对象)
(3)SharedPreferences可靠性下降(sp不支持两个进程同时执行写操作)
(4)Application多次创建(同一进程、同一虚拟机、同一Application)
5、Android中的IPC方式:
(1)、使用Bundle :Activity、Service和Receive都支持Intent中传递Bundle数据,由于Bundle实现了Parcelable接口,可在不同进程中进行通信。
(2)、使用文件共享(但是两个线程同时操作可能会出问题,sharedPrefrences有缓存策略,读写不可靠)
(3)、使用Messenger : 不同进程中传递Message对象,底层实现是AIDL
(4)、使用AIDL:
(5)、使用ContentProvider: 不同应用中进行数据共享,底层实现是Binder
(6)、Socket:通过网络传输字实现实时通信
Messenger和AIDL区别:
Messenger以串行方式处理消息,如果有大量的并发请求,只能一个个处理,不适合使用Messenger。
AIDL可以跨进程调用方法,Messenger只能传递消息
二、序列化
序列化,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。
通过Intent和Binder传输数据时需要使用序列化(内存和内存之间传输的数据需要序列化以后的行式)
 1、Serializable接口:序列化接口
声明一个serialVersionUID。在反序列过程中,会对两个UID进行对比,如果一致才可以进行反序列化。
如果不声明UID,在反序列时,当前类发生改变(增加或删除了变量),会反序列化失败。
注:静态成员变量属于类,不会对象,不会参与序列化。用transient关键字标记的变量不参与序列化。
原理:将一个对象转换成可存储可传输的状态。 
2、Parcelable接口:实现这个接口,可以通过Intent和Binder进行传递。
实现Parceable接口,重写describeContents和writeToPracel()两个方法。还需要提供一个CREATOR的常量,重写createFromParcel()和newArray()两个方法。 
原理: 将一个完整的对象进行分解,分解的每一个部分都是Intent所支持的数据类型。
在序列化过程中需要实现序列化、反序列化和内容描述
public class PicEntity implements Parcelable {    public String picURL;    public String middlePicURL;    public String smallPicURL;    //从序列化后得对象中创建原始对象    protected PicEntity(Parcel in) {        picURL = in.readString();        middlePicURL = in.readString();        smallPicURL = in.readString();    }    //反序列化的过程    public static final Creator<PicEntity> CREATOR = new Creator<PicEntity>() {        //从序列化后的对象返回原始对象        @Override        public PicEntity createFromParcel(Parcel in) {            return new PicEntity(in);        }        //创建指定长度的原始对象数组        @Override        public PicEntity[] newArray(int size) {            return new PicEntity[size];        }    };    //返回当前对象的内容描述    @Override    public int describeContents() {        return 0;    }    //序列化的过程    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeString(picURL);        dest.writeString(middlePicURL);        dest.writeString(smallPicURL);    }}
Intent、Bundle、Bitmap都是直接序列化的。List和Map也可序列化,里面的每个元素都是可序列化的。
3、区别
Serializable 是Java序列化接口,将整个对象序列化,简单开销大,序列化和反序列化需要大量I/O操作。
Parcelable是Android序列化方式,适合Android平台。将对象的每个部分序列化,效率高,使用麻烦。
三、Binder
Binder类,实现了IBinder接口。主要是AIDL(多线程,多并发访问)和Messenger(底层还是AIDL,单线程处理)
作用:跨进程通信的一种方式
(1)客户端和服务端进行通信的媒介,bindService时,服务端返回一个Binder对象,客户端客户获取数据。
(2)ServiceManager连接 各种Manager(ActivityManager、WindowManager等)和对应ManagerService的桥梁。

四、Messenger
信使,可以在不同的进程中传递Message对象,在Message中放入要传递的数据
特点:对AIDL做了封装,一次只能处理一个请求。
流程图:

使用步骤:
1、服务端
public class MessengerService extends Service {    private static String TAG = "MessengerService";    private Messenger messenger = new Messenger(new MessageHandler());    @Override    public void onCreate() {        super.onCreate();    }    @Nullable    @Override    public IBinder onBind(Intent intent) {    //返回这个messenger对象底层的Binder        return messenger.getBinder();    }    //处理服务端发送来的数据    private static class MessageHandler extends Handler{        @Override        public void handleMessage(Message msg) {            String myText = msg.getData().getString("msg");            LogUtil.i(TAG,"收到的消息:"+myText);            //回复消息            Messenger client = msg.replyTo;            Message replyMessage = Message.obtain(null,2);            Bundle bundle = new Bundle();            bundle.putString("reply","收到了消息了啊");            replyMessage.setData(bundle);            try {                //做出回复                client.send(replyMessage);            }catch (RemoteException e){                e.printStackTrace();            }        }    }}
清单文件配置:
<service android:name=".service.MessengerService"    android:process=":remote"    ></service>
2、客户端
/** * * 用户端 */public class MainActivity extends BaseActivity{    private Messenger messenger;    @Override    protected int getContentViewID() {        return R.layout.activity_main;    }    @Override    public void initView() {        super.initView();        Intent intent = new Intent(this, MessengerService.class);        bindService(intent,connection,BIND_AUTO_CREATE);    }    private Messenger replyMessenger = new Messenger(new MessageHandler());    private static class MessageHandler extends Handler {        @Override        public void handleMessage(Message msg) {            String myText = msg.getData().getString("reply");            LogUtil.i(TAG,"客户端收到的消息:"+myText);        }    }    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            //绑定成功后,用服务端返回的IBinder来创建一个Messenger            //通过这个Messenger向服务端发消息            messenger = new Messenger(service);            Message message = Message.obtain(null,1);            Bundle bundle = new Bundle();            bundle.putString("msg","ssssss");            message.setData(bundle);            //客户端接收到服务端回复的消息            message.replyTo = replyMessenger;            try {                //发送消息                messenger.send(message);            }catch (RemoteException e){                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {            LogUtil.i("链接断开");        }    };    @Override    protected void onDestroy() {        super.onDestroy();        unbindService(connection);    }}
五、AIDL:
1、创建实体类,实现Parcelable
public class Book implements Parcelable {    public int bookId;    public String bookName;    public Book(int bookId,String bookName){        this.bookId=bookId;        this.bookName = bookName;    }    protected Book(Parcel in) {        bookId = in.readInt();        bookName = in.readString();    }    @Override    public String toString() {        return "["+bookId+"---"+bookName+"]";    }    public static final Creator<Book> CREATOR = new Creator<Book>() {        @Override        public Book createFromParcel(Parcel in) {            return new Book(in);        }        @Override        public Book[] newArray(int size) {            return new Book[size];        }    };    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeString(bookName);        dest.writeInt(bookId);    }}
2、AIDL接口的实现:
(1)AIDL文件支持的数据类型
基本数据类型(int、long、char、boolean、double等)
String和CharSequence
ArrayList : 里面的元素必须能被AIDL支持
HashMap
Parcelable:实现了Parcelable接口的对象
注:其中Parcelable对象和AIDL对象显式import进来

创建一个AIDL文件,将需要给客户端调用的接口在AIDL文件中声明
// IBookManager.aidlpackage com.example.hejian.demo2.aidl;//导入所需要使用的非默认支持数据类型的包import com.example.hejian.demo2.aidl.Book;interface IBookManager{List<Book> getBookList();void add(in Book book);}
(2)如果有自定义Parcelable对象,必须建一个同名的AIDL文件
// Book.aidl//这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用//注意:Book.aidl与Book.java的包名应当是一样的package com.example.hejian.demo2.aidl;parcelable Book;
3、服务端
创建一个Service,在Service中实现这个AIDL接口。
注:AIDL方法是在服务端的Binder线程池中执行,可能出现并发访问的情况(可使用CopyOnWriteArrayList进行并发访问)
4、绑定服务端的Service,通过服务器返回的Binder对象转成AIDL接口所需类型。
5、专门删除跨进程的listener的接口RemoteCallbackList。客户端进程终止,自动移除客户端注册的listener
原理:内部有一个map专门保存所有的AIDL回调,map的key时IBinder类型,value是Callback类型(真正的远程listener)
他们底层的Binder对象是同一个,将相同的key的listener删除掉就可。
// IOnNewBookArrivedListener.aidlpackage com.example.hejian.aidldemo;// Declare any non-default types here with import statementsimport com.example.hejian.aidldemo.Book;//新书到了的监听interface IOnNewBookArrivedListener {    void onNewBookArrived(in Book newBook);}
服务端Service:
/** * 服务端 */public class AIDLService extends Service {    private CopyOnWriteArrayList<Book> mBooks = new CopyOnWriteArrayList<>();    public final String TAG = this.getClass().getSimpleName();    private RemoteCallbackList<IOnNewBookArrivedListener> backListener = new RemoteCallbackList<>();    @Override    public void onCreate() {        super.onCreate();        mBooks.add(new Book(13,"思维"));        mBooks.add(new Book(23,"思维1"));    }    private Binder binder= new IBookManager.Stub(){        @Override        public List<Book> getBookList() throws RemoteException {            Log.e(TAG, "获取到服务端list : " + mBooks.toString());            return mBooks;        }        @Override        public void add(Book book) throws RemoteException {            mBooks.add(book);            //通过监听通知所有的客户端            final int N = backListener.beginBroadcast();            for (int i=0;i<N;i++){                IOnNewBookArrivedListener listener = backListener.getBroadcastItem(i);                listener.onNewBookArrived(book);            }            backListener.finishBroadcast();            Log.e(TAG, "增加一本以后,服务端list : " + mBooks.toString());        }        @Override        public void registListener(IOnNewBookArrivedListener listener) throws RemoteException {            backListener.register(listener);        }        @Override        public void unregistListener(IOnNewBookArrivedListener listener) throws RemoteException {            backListener.unregister(listener);        }    };    @Nullable    @Override    public IBinder onBind(Intent intent) {        return binder;    }}
用户端:
public class MainActivity extends AppCompatActivity {    //由AIDL文件生成的Java类    private IBookManager mBookManager = null;    //包含Book对象的list    private List<Book> mBooks = new ArrayList<>();    private static String TAG = "MainActivity";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Button button = (Button)findViewById(R.id.button);        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                try {                    if (mBookManager!=null){                        mBooks.add(new Book(23,"新增的"));                        mBookManager.add(new Book(23,"新增的"));                    }                } catch (RemoteException e) {                    e.printStackTrace();                }                Log.e(TAG, "增加一本以后,客户端list : "+mBooks.toString());            }        });    }    @Override    protected void onResume() {        super.onResume();        Intent intent = new Intent(this, AIDLService.class);        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);    }    private final ServiceConnection mServiceConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.e(TAG, "service connected");            //将服务端返回的Binder对象转成AIDL接口,然后通过接口掉方法            mBookManager = IBookManager.Stub.asInterface(service);            if (mBookManager != null) {                try {                    mBookManager.registListener(listener);                    mBooks = mBookManager.getBookList();                    Log.e(TAG, "获取客户端list:"+mBooks.toString());                } catch (RemoteException e) {                    e.printStackTrace();                }            }        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.e(TAG, "service disconnected");        }    };    private IOnNewBookArrivedListener listener = new IOnNewBookArrivedListener.Stub() {        @Override        public void onNewBookArrived(Book newBook) throws RemoteException {            Log.e(TAG,"监听到服务器加了一个书:"+newBook.toString());        }    };    @Override    protected void onDestroy() {        super.onDestroy();        unbindService(mServiceConnection);    }}
6、Binder死亡监听
(1)设置DeathRecipient监听,在binderDied方法中重连远程服务(在Binder线程池中被回调)
(2)在onServiceDisconnected中重连远程服务
7、远程服务的权限验证
(1)例如在清单文件声明权限,在Service的onBind方法中做权限验证
(2)在服务端onTransact方法验证
(3)获取Uid和Pid,验证客户端包名等
注意:由于客户端onServiceConnected和onServiceDisConnected方法运行在UI线程,不能直接进行耗时操作
服务端本身运行在Binder线程池中,可以执行大量的耗时操作。

注:开发艺术探索笔记整理


















0 0
原创粉丝点击