Android开发Android开发程序员

Android中的IPC方式

2018-11-28  本文已影响24人  ScottStone

IPC是Inter-ProcessCommunication的缩写,进程间通信、跨进程通信,是指两个进程之间进行数据交换的过程。说起进程间通信,我们首先脑补下什么是进程,什么是线程,进程和线程是截然不同的概念。按照操作系统中的描述,线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。最简单的情况下,一个进程中可以只有一个线程,即主线程,在Android里面主线程也叫UI线程,在UI线程里才能操作界面元素。通常情况下一个进程中需要执行大量耗时的任务,如果这些任务放在主线程中去执行就会造成界面长时间无法响应,严重影响用户体验,这种情况在PC系统和移动系统中都存在,在Android中有一个特殊的名字叫做ANR(ApplicationNotResponding),即应用无响应。解决这个问题就需要用到线程,把一些耗时的任务放在线程中即可。

对于Android来说,它是一种基于Linux内核的移动操作系统,它的进程间通信方式并不能完全继承自Linux,它有自己的进程间通信方式。在Android中最有特色的进程间通信方式就是Binder了,通过Binder可以轻松地实现进程间通信。除了Binder,Android还支持Socket,通过Socket也可以实现任意两个终端之间的通信,当然同一个设备上的两个进程通过Socket通信自然也是可以的。

具体IPC方式有很多,比如可以通过在Intent中附加extras来传递信息,或者通过共享文件的方式来共享数据,还可以采用Binder方式来跨进程通信,另外,ContentProvider天生就是支持跨进程访问的,因此我们也可以采用它来进行IPC。此外,通过网络通信也是可以实现数据传递的,所以Socket也可以实现IPC。下面就具体介绍几种主要的IPC方式。

1.使用Bundle

四大组件中的三大组件(Activity、Service、BroadcastReceiver)都是支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以它可以方便地在不同的进程间传输。这里传输的数据必须可以被序列化,比如基本数据类型和实现了Parcelable、Serializable接口的类都可以传输。

2.使用文件共享

这个看起来比较好理解,就是在SD卡中存储个文件用来共享,但是这个明显的有并发问题。众所周知,SharedPreferences是Android提供的一个轻量级的存储方案,在xml中以键值对的形式存储。同样的,在多进程的情况下,读写并不是可靠的。

3.使用Messenger

Messenger很多地方翻译为信使,这个也算贴切。Messenger是一中轻量级的IPC方式,底层实现也是基于AIDL。

Messenger的使用方法很简单,它对AIDL做了封装,使得我们可以更简便地进行进程间通信。同时,由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题,这是因为服务端中不存在并发执行的情形。实现一个Messenger有如下几个步骤,分为服务端和客户端。

A.服务端进程首先,我们需要在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。

B.客户端进程客户端进程中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象。如果需要服务端能够回应客户端,就和服务端一样,我们还需要创建一个Handler并创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。这听起来可能还是有点抽象,不过看了下面的两个例子,读者肯定就都明白了。首先,我们来看一个简单点的例子,在这个例子中服务端无法回应客户端。

/**
 * Created by Stone on 2018/8/16.
 */
public class MessengerService extends Service {


    private static final String TAG = "MessengerService";

    private static class MessengerHandler extends Handler

    {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
                    
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private final Messenger mMessenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}
public class MessengerActivity extends AppCompatActivity {

    private static final String TAG = " MessengerActivity";
    private TextView tvResult;
    private Messenger mService;
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            Message msg = Message.obtain(null, 1);
            Bundle data = new Bundle();
            data.putString("msg", " hello, this is client.");
            msg.setData(data);
            try {
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        public void onServiceDisconnected(ComponentName className) {
        }
    };
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        tvResult = findViewById(R.id.tv_result);
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

问题来了,实际使用中不可能只有客户端访问服务端,还有服务端响应客户端,那我们怎么改进下呢?直接上代码:

/**
 * Created by Stone on 2018/8/16.
 */
public class MessengerService extends Service {


    private static final String TAG = "MessengerService";

    private static class MessengerHandler extends Handler

    {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
                    Messenger client = msg.replyTo;
                    Message relpyMessage = Message.obtain(null, 2);
                    Bundle bundle = new Bundle();
                    bundle.putString(" reply", "消息已收到");
                    relpyMessage.setData(bundle);
                    try {
                        client.send(relpyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private final Messenger mMessenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}
public class MessengerActivity extends AppCompatActivity {

    private static final String TAG = " MessengerActivity";
    private TextView tvResult;
    private Messenger mService;
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            Message msg = Message.obtain(null, 1);
            Bundle data = new Bundle();
            data.putString("msg", " hello, this is client.");
            msg.setData(data);
            //添加处理回复的Messenger
            msg.replyTo = mGetReplyMessenger;
            try {
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        public void onServiceDisconnected(ComponentName className) {
        }
    };
    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
    private  class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 2:
                    Log.i(TAG, " receive msg from Service:" + msg.getData().getString(" reply"));
                    tvResult.append("\n receive msg from Service:" + msg.getData().getString(" reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        tvResult = findViewById(R.id.tv_result);
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

从上面代码可以看到,在收到客户端的消息后,服务端把返回的消息放到了msg.replyTo中,这也是一个Messenger,具体接受的Messenger在MessengerActivity中定义,当然也是一组完整的Messenger跟Handler,具体可以看代码。这里IPC的类型必须是Message支持的类型。最后给出Messenger的工作原理:

Messenger原理

4.使用AIDL

简单的使用可参考Android Binder---AIDL ,这里接着Android Binder---AIDL介绍的AIDL继续往下写。考虑到用户每次都是访问getStudent方法比较费劲,如果StudentList有更新再通知用户去获取岂不是很省力?这里就要用到观察者模式了。我们创建一个新的AIDL接口,这个用来监听StudentList的变化。

// IOnNewStudentArrivedListener.aidl
package com.stone.demoandroid.entity;
import com.stone.demoandroid.entity.Student;
// Declare any non-default types here with import statements

interface IOnNewStudentArrivedListener {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void onNewStudentArrived(in Student newStudent);
}

同样的,之前的Manager也要有所变化添加两个方法:registerListener、unregisterListener

// IStudentManager.aidl
package com.stone.demoandroid.entity;

// Declare any non-default types here with import statements
import com.stone.demoandroid.entity.Student;
import com.stone.demoandroid.entity.IOnNewStudentArrivedListener;
interface IStudentManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */

    List<Student> getStudentList();
    void addStudent(in Student student);
    void registerListener( IOnNewStudentArrivedListener listener);
    void unregisterListener( IOnNewStudentArrivedListener listener);
}

Service跟Activity的代码太长了就不全部贴出来了,有兴趣的同学可以直接去Demo下载查看。这里有一点需要提一下,CopyOnWriteArrayList不能处理跨进程的Listener,需要用RemoteCallbackList,因为CopyOnWriteArrayList在服务端使用的Listener并不是客户端定义的那个,看名字也知道只是一个Copy。还有就是并没有处理ANR和Binder意外死亡的问题,有兴趣额同学可以处理下。

5.使用ContentProvider

ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式,从这一点来看,它天生就适合进程间通信。和Messenger一样,ContentProvider的底层实现同样也是Binder,由此可见,Binder在Android系统中是何等的重要。虽然ContentProvider的底层实现是Binder,但是它的使用过程要比AIDL简单许多,这是因为系统已经为我们做了封装,使得我们无须关心底层细节即可轻松实现IPC。具体的实现这里就不赘述了,四大组件之一,用法一搜一堆,有兴趣的可以看下Demo

6.使用Socket

Socket也称为“套接字”,是网络通信中的概念,它分为流式套接字和用户数据报套接字两种,分别对应于网络的传输控制层中的TCP和UDP协议。本科的时候选修过JavaSocket,作业有实现TCP和UDP协议,考试竟然是上机,自己实现一个局域网的聊天工具。

具体的实现,大家可以看下Demo

选择合适的IPC(任玉刚. Android开发艺术探索. 电子工业出版社. ),这里借大佬总结的帖在这里希望对大家有帮助:

image

主要的几个IPC的方式就简单介绍到这里,有什么问题可以留言。

上一篇下一篇

猜你喜欢

热点阅读