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", "上海"});
        cursor.addRow(new String[]{"刘敏", "9", "重庆"});

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Bundle bundle = new Bundle();
            bundle.putString("data", "Cursor data from " + Thread.currentThread());
            cursor.setExtras(bundle);
        }
        return cursor;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}

在AndroidManifest.xml中注册:

authorities:定义连接时的uri
process:指定新的进程名
<provider
      android:name=".ipc.MContentProvider"
      android:authorities="com.test.provider.MContentProvider"
      android:process=":test.provider" />

ContentProvider里面有系统封装好的一个方法,另外还有一个类似Activity生命周期的onCreate方法,这个方法在第一次连接时调用,即getContentResolver().query()时调用,而且只调用一次,后面再连接不会调用。

在这里插入图片描述

从onCreate中日志可以看出,初始化在主线程中。

由于Cursor是一个接口,所以在query方法中,我们创建了一个MatrixCursor来模拟查询数据,我往Cursor里面插入了3条数据,并且设置了一条Bundle数据,我们在主线程中去查询一下:

    private void providerTest() {
    	// "content://${authorities}"
        Uri uri = Uri.parse("content://com.test.provider.MContentProvider");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Cursor query = getContentResolver().query(uri, null, null, null);
            if (query != null) {
                Bundle extras = query.getExtras();
                String data = extras.getString("data");
                Log.e(getClass().getSimpleName(), Thread.currentThread() + " " + data);

                while (query.moveToNext()) {
                    int nameIndex = query.getColumnIndex("name");
                    int ageIndex = query.getColumnIndex("age");
                    int cityIndex = query.getColumnIndex("city");
                    String name = query.getString(nameIndex);
                    String age = query.getString(ageIndex);
                    String city = query.getString(cityIndex);
                    Log.e(getClass().getSimpleName(), nameIndex + ":" + name + ", " + ageIndex + ":" + age + ", " + cityIndex + ":" + city);
                }
            }
        }
    }

打印结果:
在这里插入图片描述
从第一个Log执行结果看出来,当前在主线程,并且获取到了Cursor中Bundle携带的数据。
从第二条Log执行结果看出来,我们在ContentProvider所在的子线程插入的3条数据拿到了。

六、Binder

Binder是Android中一个非常重要的东西,上面三四五都是基于Binder实现的,我在某乎上看到一篇介绍Binder的文章,讲的很详细,传送门

七、Socket

这个也不多说了,网上很多。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值