IPC通信方式

Posted 陳英傑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IPC通信方式相关的知识,希望对你有一定的参考价值。

IPC又称进程间通信,面试老生常谈的问题,Linux常用方式有:管道、信号、消息队列、共享内存、内存映射、信号量、套接字。
android进程间通信方式:
Intent、Broastcast,AIDL、Messenger、ContentProvider、Binder、Socket。

一、Intent

这个平时用的最多的,就不多说了

二、Broastcast

广播也比较简单,简单列举一下知识点:
四大组建之一

分类

标准广播:也叫无需广播,所有广播接收器都能接收到广播,无法拦截,效率高。
有序广播:优先级高的先接收到,逻辑执行完往下传递,前面的广播接收器可以拦截广播。

注册方式

静态注册:在AndroidManifest.xml中,应用存活着就会一直处于接收状态。
动态注册:在代码中注册,可以手动解除注册。
其中有序广播需要设置优先级priority。
注意:不要在 onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当onReceive()方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打开程序其它组件的角色,比如创建一条状态栏通知,或者启动一个服务等。

三、AIDL

aidl是IPC机制的一种解决方案,使用起来相对Messenger等稍微复杂,简单例子如下:

简单使用

1、服务端

首先创建服务端的应用,app/src/main下创建aidl文件,这里只定义了一个方法,传进来一个字符串,经过服务端处理在返回给客户端:

package com.lianzhuoxinxi.baoduoduo;

import com.lianzhuoxinxi.baoduoduo.User;

interface IMyAidlInterface 

    String getName(String str);

然后rebuild一下之后会根据此文件生成对应接口类

接下来就是服务端的处理代码,创建一个Service,它里面有一个aidl接口的实现,主要在这里面处理数据,然后返回给客户端:

public class MyRemoteService extends Service 

    @Nullable
    @Override
    public IBinder onBind(Intent intent) 
        return new MyBinder();
    


    private class MyBinder extends IMyAidlInterface.Stub 

        @Override
        public String getName(String str) throws RemoteException 
            if (!TextUtils.isEmpty(str)) 
            	// 把客户端传过来的字符串转大写返回给客户端
                return str.toUpperCase();
            
            return "empty";
        

    

最后在AndroidManifest.xml中注册服务,这里有三个属性要注意,enabled必须为true,false表示被禁用;exported为true表示能与其他应用交互,false表示此服务只能在应用内使用;action是客户端绑定使用的。

<service android:name=".service.MyRemoteService"
     android:exported="true"
     android:enabled="true">
     <intent-filter>
         <action android:name="lzxx.bd.aidl.service"/>
         <category android:name="android.intent.category.DEFAULT"/>
     </intent-filter>
 </service>

服务端就这么简单,运行起来就行了。

2、客户端

首先创建一个客户端应用,然后把服务端的aidl文件(包括包名)拷贝过来,这里注意保证文件的包名一定要和服务端一样,所以最保险的方式就是拷贝。

在MainActivity中绑定服务,当绑定成功就可以调用服务端aidl中定义的方法了。

private void aidlTest() 
	// 此处action和服务端配置的一致,必须设置服务端应用的包名,否则找不到。
    Intent intent = new Intent("lzxx.bd.aidl.service");
    intent.setPackage("com.lianzhuoxinxi.baoduoduo");
    bindService(intent, new ServiceConnection() 
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) 
            aidlInterface = IMyAidlInterface.Stub.asInterface(service);
        

        @Override
        public void onServiceDisconnected(ComponentName name) 

        
    , BIND_AUTO_CREATE);


private void getNameFromServer() 
	try 
		Log.e("AIDL", ">>>>" + aidlInterface.getName("hello aidl"));
     catch (RemoteException e) 
        e.printStackTrace();
    

打印结果如下,可以看到服务端的处理逻辑起作用了。

2020-11-27 09:58:40.633 25189-25189/? E/AIDL: >>>>HELLO AIDL

自定义数据类型

1、服务端

在刚才的aidl文件中增加两个方法,一个是添加用户,一个是获取用户列表

interface IMyAidlInterface 

    String getName(String str);

    List<User> getUserList();

    void addUser(inout User user);

创建一个用户实体类,需要实现Parcelable接口,并且需要添加一个readFromParcel方法

public class User implements Parcelable 

    private String name;
    private int age;

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public int getAge() 
        return age;
    

    public void setAge(int age) 
        this.age = age;
    

    @Override
    public int describeContents() 
        return 0;
    

    @Override
    public void writeToParcel(Parcel dest, int flags) 
        dest.writeString(this.name);
        dest.writeInt(this.age);
    

    public void readFromParcel(Parcel parcel) 
        name = parcel.readString();
        age = parcel.readInt();
    

    public User() 
    

    protected User(Parcel in) 
        this.name = in.readString();
        this.age = in.readInt();
    

    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() 
        @Override
        public User createFromParcel(Parcel source) 
            return new User(source);
        

        @Override
        public User[] newArray(int size) 
            return new User[size];
        
    ;

同时在aidl文件夹中再创建一个User.aidl文件,声明自定义的数据类型

package com.lianzhuoxinxi.baoduoduo;

parcelable User;

Service中也实现新增加的方法:

public class MyRemoteService extends Service 

    private List<User> users;

    @Override
    public void onCreate() 
        super.onCreate();
        users = new ArrayList<>();
    

    @Nullable
    @Override
    public IBinder onBind(Intent intent) 
        return new MyBinder();
    


    private class MyBinder extends IMyAidlInterface.Stub 

        @Override
        public String getName(String str) throws RemoteException 
            if (!TextUtils.isEmpty(str)) 
                return str.toUpperCase();
            
            return "empty";
        

        @Override
        public List<User> getUserList() throws RemoteException 
            return users;
        

        @Override
        public void addUser(User user) throws RemoteException 
            users.add(user);
        
    

编译之后运行起来。
目录结构:

2、客户端

同样把aidl文件夹拷贝过来,把User类连同包名拷贝过来,目录结构:

其他不用修改,编译之后就可以调用新加的addUser()和getUserList()方法了。

四、Messenger

基于Binder的一种进程间通信方式

1、服务端

创建一个service用来接收请求并返回结果:

public class MessengerService extends Service 

    private Messenger serverMessenger = new Messenger(new MessengerHandler());

    @Override
    public void onCreate() 
        super.onCreate();
    

    @Nullable
    @Override
    public IBinder onBind(Intent intent) 
        return serverMessenger.getBinder();
    

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
        return super.onStartCommand(intent, flags, startId);
    

    @Override
    public void onDestroy() 
        super.onDestroy();
    

    private static class MessengerHandler extends Handler 

        @Override
        public void handleMessage(@NonNull Message msg) 
            super.handleMessage(msg);

            Bundle data = msg.getData();
            String type = data.getString(Cons.client_key);

            Message message = Message.obtain();
            Bundle bundle = new Bundle();
            if ("1".equals(type)) 
                bundle.putString(Cons.server_key, "request type 1 response");
             else if ("2".equals(type)) 
                bundle.putString(Cons.server_key, "request type 2 response");
             else 
                bundle.putString(Cons.server_key, "request unknown type response");
            
            message.setData(bundle);
            try 
                msg.replyTo.send(message);
             catch (RemoteException e) 
                e.printStackTrace();
            
        
    

2、客户端

  1. 实现ServiceConnection接口,实例化发送请求的messenger对象:
private class MyServiceConnection implements ServiceConnection 

        private String type;

        public MyServiceConnection(String type) 
            this.type = type;
        

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) 
            // 1、get connection from server
            if (1.equals(type)) 
                messenger1 = new Messenger(service);
             else if (2.equals(type)) 
                messenger2 = new Messenger(service);
             else 
                messengerX = new Messenger(service);
            
        

        @Override
        public void onServiceDisconnected(ComponentName name) 

        
    ;
  1. 绑定服务:
    这里创建了3个连接相当于3个客户端请求,共用同一个服务。
connection1 = new MyServiceConnection(type_1);
connection2 = new MyServiceConnection(type_2);
connectionX = new MyServiceConnection("");

bindService(new Intent(this, MessengerService.class), connection1, BIND_AUTO_CREATE);
bindService(new Intent(this, MessengerService.class), connection2, BIND_AUTO_CREATE);
bindService(new Intent(this, MessengerService.class), connectionX, BIND_AUTO_CREATE);
  1. 创建一个Handler来接收服务器返回结果:
private static class ClientHandler extends Handler 

    private WeakReference<MessengerActivity> reference;

    public ClientHandler(MessengerActivity activity) 
        this.reference = new WeakReference<>(activity);
    

    @Override
    public void handleMessage(@NonNull Message msg) 
        super.handleMessage(msg);
        MessengerActivity activity = reference.get();
        Bundle data = msg.getData();
        String serverData = data.getString(Cons.server_key);
        activity.updateResult(serverData);
    


private void updateResult(String str) 
	result.append(str).append("\\n");
	tvResult.setText(result.toString());

  1. 实例化接收请求结果的Messenger对象:
private Messenger clientMessenger = new Messenger(new ClientHandler(this));
  1. 然后是发送请求的方法:
    这里创建Message对象千万别用new,因为牵扯到内存抖动问题,这里不过多介绍。
private void sendData(String type) 
    // 2、create Message
    Message message = Message.obtain();
    Bundle bundle = new Bundle();
    bundle.putString(Cons.client_key, type);
    message.setData(bundle);
    // 3、data receive object
    message.replyTo = clientMessenger;
    try 
        // 4、send request
        if (type_1.equals(type)) 
            messenger1.send(message);
         else if (type_2.equals(type)) 
            messenger2.send(message);
         else 
            messengerX.send(message);
        
     catch (RemoteException e) 
        e.printStackTrace();
    

发送请求:

// 发送类型1请求
sendData("1");
// 发送类型2请求
sendData("2");
// 发送其他类型请求
sendData("");

最后在activity的onDestroy()方法中解绑服务:

    @Override
    protected void onDestroy() 
        super.onDestroy();
        unbindService(connection1);
        unbindService(connection2);
        unbindService(connectionX);
    

随便点击调用sendData方法之后结果:

五、ContentProvider

  • ContentProvider是Android中提供的专门用于不同应用间数据共享的方式,从这一点来看,他肯定是支持进程间通信的,和Messenger一样,他的底层实现也是Binder,由此可见,Binder在Android系统中的重要性
  • ContentProvider比Messenger使用起来要简单的多了,因为系统已经为我们做了封装,我们无需关心底层即可轻松实现IPC
  • 系统预置了好多ContentProvider,比如通讯录信息,日程表信息,等,要跨进程访问这些信息,只需要通过ContentProvider的query,updata,insert,delete方法即可

接下来简单使用它,创建一个ContentProvider:

public class MContentProvider extends ContentProvider 
    @Override
    public boolean onCreate() 
        Log.e(getClass().getSimpleName(), "onCreate Thread: " + Thread.currentThread());
        return false;
    

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) 
        Log.e(getClass().getSimpleName(), "query() Thread : " + Thread.currentThread());
        MatrixCursor cursor = new MatrixCursor(new String[]"name", "age", "city", 5);
        cursor.addRow(new String[]"张伟", "39", "北京");
        cursor.addRow(new String[]"王刚", "21"以上是关于IPC通信方式的主要内容,如果未能解决你的问题,请参考以下文章

Android艺术开发探索——第二章:IPC机制(下)

Linux 进程间通信(IPC)

分叉和父子通信

你了解RPC么?

IPC通信方式

IPC通信方式