IPC 机制(下)

2.4 Android 中的 IPC 方式


2.4.1 使用 Bundle

Bundle 实现了 Parcelable 接口,可以通过 Intent 传递数据,Intent有一个成员变量Bundle mExtras存储自己的数据, 而Bundle实际上就是一个ArrayMap, 以键值对的方式保存着各种类型的数据。

2.4.2 使用文件共享

典型例子是 SharedPreferences , 它通过键值对的方式在 xml 文件中存储数据,文件位于 /data/data/package_name/shared_prefs目录下. 如果应用是多进程模式的, 不同进程就会读写同一份 xml 文件,也就实现了跨进程通信。 但由于 SharedPreferences 有缓存策略, 即在内存中有一份SharedPreferences。因此多进程下容易发生数据丢失, 因此不建议采用 SharedPreferences 实现跨进程通信。

多个进程对同一份文件进行读写,文件共享方式适合在对数据同步要求不高的进程之间进行通信。

2.4.3 使用 Messenger

  • Messenger(信使),通过 Messenger 可以在不同进程中传递消息(Message)。
  • Messenger 是一种轻量级的 IPC 方法,底层实现是 AIDL。
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
    //Stub.asInterface 就是 AIDL 中的
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

使用方法很简单,由于它一次处理一个请求,所以服务端我们不同考虑线程同步问题。
1.服务端进程

public class MessengerService extends Service {

    private static final String TAG = "MessengerService";

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

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MyConstants.MSG_FROM_CLIENT:
                    Log.d(TAG, "消息来自客户端" + msg.getData().getString("msg"));
                    Messenger client = msg.replyTo;
                    Message replyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
                    Bundle data = new Bundle();
                    data.putString("reply", "你好,我是服务端!");
                    replyMessage.setData(data);
                    try {
                        client.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }
            super.handleMessage(msg);
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

2.客服端进程

public class MainActivity extends AppCompatActivity {

    private Messenger mService;
    private static final String TAG = "MessengerService";

    private Messenger mGetService = new Messenger(new MessengerHandle());

    @SuppressLint("HandlerLeak")
    private static class MessengerHandle extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MyConstants.MSG_FROM_SERVICE:
                    Bundle data = msg.getData();
                    Log.d(TAG, data.getString("reply"));
                    break;
                default:
                    break;
            }
            super.handleMessage(msg);
        }
    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "链接成功");
            // mService 是个信使
            mService = new Messenger(service);
            // msg 是一个消息,data 是消息内容
            Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg", "你好,服务端");
            msg.setData(data);
            msg.replyTo = mGetService;
            try {
                // 信使传递消息。
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, MessengerService.class);
        //绑定服务
        bindService(intent, conn, Context.BIND_AUTO_CREATE);//绑定服务
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);//取消绑定
    }
}
        
IPC 机制(下)_第1张图片

2.4.4 使用 AIDL

AIDL 支持的类型 :
  • 基本数据类型;
  • String 和 CharSequence;
  • List:只支持 ArrayList,里面每个元素都必须能够被 AIDL 支持;
  • Map:只支持 HashMap,里面的每个元素都必须被 AIDL 支持,包括 key 和 value;
  • Parcelable:所有实现了 Parcelable 接口的对象;
  • AIDL :所有的 AIDL 接口本身也可以在 AIDL 文件中使用。

其中自定义的 Parcelable 对象和 AIDL 对象必须要显示 import 进来,如果 AIDL 文件中用到了自定义的 Parcelabale 对象,那么必须新建一个和它同名的 AIDL 文件。

IPC 机制(下)_第2张图片
下面案例目录结构

使用案例来演示 AIDL

1.AIDL文件: Book .aidl、IBookManager .aidl、IOnNewBookArrivedListener .aidl

package com.kjn.chaptermessenger.aidl;
import com.kjn.chaptermessenger.aidl.Book;

parcelable Book ;
package com.kjn.chaptermessenger.aidl;
import com.kjn.chaptermessenger.aidl.Book;
import com.kjn.chaptermessenger.aidl.IOnNewBookArrivedListener;

interface IBookManager {
     List getBookList();
     void addBook(in Book book);
     void registerListener(IOnNewBookArrivedListener listener);
     void unregisterListener(IOnNewBookArrivedListener listener);

}
package com.kjn.chaptermessenger.aidl;
import com.kjn.chaptermessenger.aidl.Book;
interface IOnNewBookArrivedListener {

    void onNewBookArrived(in Book newBook);
}

2.客户端:BookManagerActivity

public class BookManagerActivity extends AppCompatActivity {

    private static final String TAG = "BookManagerActivity";
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

    private IBookManager mRemoteBookManager;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Log.d(TAG, "收到新书:" + msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            mRemoteBookManager = bookManager;

            try {
                //设置 binder 死亡代理
                mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);
                List list = bookManager.getBookList();
                Log.d(TAG, "查询图书列表,列表类型: " + list.getClass().getCanonicalName());
                Log.d(TAG, "查询图书列表: " + list.toString());
                Book book = new Book(3, "Android 开发艺术探索");
                bookManager.addBook(book);
                Log.d(TAG, "new book:" + book);
                List newList = bookManager.getBookList();
                Log.d(TAG, "查询图书列表: " + newList.toString());
                //注册新书到达监听器
                bookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteBookManager = null;
            Log.d(TAG, "binder died.");
        }
    };
    //Binder 链接断裂监听
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());
            if (mRemoteBookManager == null) {
                return;
            }
            mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mRemoteBookManager = null;
            //TODO:这里重新绑定 Service
        }
    };
    //新书到达监听器
    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {

        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    public void checkIt(View view) {
    }

    @Override
    protected void onDestroy() {
        if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) {
            Log.d(TAG, "onDestroy: " + mOnNewBookArrivedListener);
            try {
                mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(conn);
        super.onDestroy();
    }
}

3.服务端:BookManagerService

public class BookManagerService extends Service {

    private static final String TAG = "BookManagerService";
    // 原子操作,保证在多线程中不是出现意外的变化
    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
    // CopyOnWriteArrayList 支持并发写
    private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList<>();
    // RemoteCallbackList  专门用于跨进程删除监听
    private RemoteCallbackList mListenerList = new RemoteCallbackList<>();
    //mBinder 对象获得来自 AIDL
    private Binder mBinder = new IBookManager.Stub() {
        //获取图书列表
        @Override
        public List getBookList() throws RemoteException {
            return mBookList;
        }
        //添加图书
        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
        //注册监听器:监听新书到来通知客户端
        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.register(listener);
            listenerSize();
        }
        //注销监听器
        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            boolean success = mListenerList.unregister(listener);
            if (success) {
                Log.d(TAG, "注销成功.");
            } else {
                Log.d(TAG, "没有找到,不能注销.");
            }
            listenerSize();
        }
        // onTransact 方法会被 getBookList()、addBook(Book book)、registerListener和unregisterListener调用
        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            int check = checkCallingOrSelfPermission("com.kjn.chaptermessenger.permission.ACCESS_BOOK_SERVICE");
            if (check == PackageManager.PERMISSION_DENIED) {
                return false;
            }
            // 验证包名
            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }
            if (packageName != null && !packageName.startsWith("com.kjn")) {
                return false;
            }
            return super.onTransact(code, data, reply, flags);
        }
    };
   // N 的获取要调用 beginBroadcast()和finishBroadcast() 两个对应方法。
    private void listenerSize() {
        final int N = mListenerList.beginBroadcast();
        mListenerList.finishBroadcast();
        Log.d(TAG, "registerListener, current size:" + N);
    }
    //新书到达时,通知监听器
    public void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        Log.d(TAG, "新书到达时,通知监听器:" + mListenerList);
        final int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++) {
            IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
            Log.d(TAG, "新书到达时,通知监听器:" + listener);
            if (listener != null) {
                listener.onNewBookArrived(book);
            }
        }
        mListenerList.finishBroadcast();
    }
    //耗时5秒发送新书通知
    private class ServiceWorker implements Runnable {

        @Override
        public void run() {
            while (!mIsServiceDestoryed.get()) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "Ios"));
        new Thread(new ServiceWorker()).start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mIsServiceDestoryed.set(true);
    }
}

4.AndroidManifest 中设置权限和多进程


或者
 
...

代码多,含义写法都写在代码注释中,这里讲上述代码实现的步骤

  1. 远程服务端 Service 的实现,完成 mBinder 的返回,并在 onCreate 中添加两本书。
  2. 客户端的实现,绑定 Service,并获取书籍列表。
  3. Service 中完成注册绑定新书监听器,添加新书 ServiceWorker 的操作。
  4. 客户端注册新书监听器,用 handler 完成接收。
  5. 设置权限验证和死亡代理。

2.4.5 使用 ContentProvider

ContentProvider 是 Android 中提供的专门用于不同应用间进行数据共享的方式。和 Messenger 一样,ContentProvider 的底层同样也是 Binder。
  系统预置了许多 ContentProvider,比如通讯录信息、日程信息表等,只要通过 ContentReslover 的 query、update、insert 和 delete 方法即可。

2.4.6 使用 Socket

Scoket 也称为“套接字”,是网络通信中的概念。

  • 流式套接字:网络的传输控制层中的 TCP;
  • TCP 协议是面向连接的协议,提供稳定的双向通信功能,TCP链接的建立需要经过 “三次握手” 才能完成,本身提供了超时重传机制,因此具有很高的稳定性;
  • 用户数据报套接字:网络的传输控制层中的 UDP协议;
  • UDP 是无链接的,提供不稳定的单向通信功能,也可以实现双向通信功能,但是不稳定不推荐。

下面一个简单的演示案例,敲一边,梳理一下就清楚 如何 用Socket 来进程间通信的实例

客户端代码

     public class TCPClientActivity extends Activity implements View.OnClickListener {

    private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
    private static final int MESSAGE_SOCKET_CONNECTED = 2;

    private Button mSendButton;
    private TextView mMessageTextView;
    private EditText mMessageEditText;

    private PrintWriter mPrintWriter;
    private Socket mClientSocket;

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {//接收到新消息
                case MESSAGE_RECEIVE_NEW_MSG: {
                    mMessageTextView.setText(mMessageTextView.getText()
                            + (String) msg.obj);
                    break;
                }//链接失败
                case MESSAGE_SOCKET_CONNECTED: {
                    mSendButton.setEnabled(true);
                    break;
                }
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tcpclient);
        mMessageTextView = (TextView) findViewById(R.id.msg_container);
        mSendButton = (Button) findViewById(R.id.send);
        mSendButton.setOnClickListener(this);
        mMessageEditText = (EditText) findViewById(R.id.msg);
        Intent service = new Intent(this, TCPServerService.class);
        startService(service);
        new Thread() {
            @Override
            public void run() {
                connectTCPServer();
            }
        }.start();
    }

    @Override
    protected void onDestroy() {
        if (mClientSocket != null) {
            try {
                mClientSocket.shutdownInput();
                mClientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        super.onDestroy();
    }

    @Override
    public void onClick(View v) {
        if (v == mSendButton) {
            final String msg = mMessageEditText.getText().toString();
            if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
                mPrintWriter.println(msg);
                mMessageEditText.setText("");
                String time = formatDateTime(System.currentTimeMillis());
                final String showedMsg = "self " + time + ":" + msg + "\n";
                mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
            }
        }
    }

    @SuppressLint("SimpleDateFormat")
    private String formatDateTime(long time) {
        return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
    }

    private void connectTCPServer() {
        Socket socket = null;
        //超市重连策略:socket 为null 时,不停的重连
        while (socket == null) {
            try {
                socket = new Socket("localhost", 8688);
                mClientSocket = socket;
                mPrintWriter = new PrintWriter(new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream())), true);
                mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
                System.out.println("connect server success");
            } catch (IOException e) {
                SystemClock.sleep(1000);
                System.out.println("connect tcp server failed, retry...");
            }
        }

        try {
            // 接收服务器端的消息
            //这是通信编程,clientSocket.getInputStream()获取服务端传来的字节流、
            //在用InputStreamReader(类是从字节流到字符流的桥梁)在缓存到BufferedReader缓冲区
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
            while (!TCPClientActivity.this.isFinishing()) {
                String msg = br.readLine();
                System.out.println("receive :" + msg);
                if (msg != null) {
                    String time = formatDateTime(System.currentTimeMillis());
                    final String showedMsg = "server " + time + ":" + msg
                            + "\n";
                    mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg)
                            .sendToTarget();
                }
            }
            System.out.println("quit...");
            MyUtils.close(mPrintWriter);
            MyUtils.close(br);
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务器

public class TCPServerService extends Service {

    private boolean mIsServiceDestoryed = false;
    private String[] mDefinedMessages = new String[]{
            "你好啊,哈哈", "请问你叫什么名字",
            "今天杭州的天气不错啊,shy",
            "你知道吗,我可是可以和多个人聊天的",
            "给你讲个笑话,我中五百万了。"
    };

    @Override
    public void onCreate() {
        new Thread(new TcpServer()).start();
        super.onCreate();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        mIsServiceDestoryed = true;
        System.out.println("Service onDestroy");
        super.onDestroy();
    }

    private class TcpServer implements Runnable {

        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                //监听/建立本地 8688 端口
                serverSocket = new ServerSocket(8688);

            } catch (IOException e) {
                System.out.println("建立 TCP 服务器断开失败,端口:8688");
                e.printStackTrace();
                return;
            }
            while (!mIsServiceDestoryed) {
                try {
                    //接收客户端请求
                    final Socket client = serverSocket.accept();
                    System.out.println("接受");
                    new Thread() {
                        @Override
                        public void run() {
                            try {
                                responseClient(client);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }.start();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //响应客户端,把响应的信息转换成字符流传入客户端
    private void responseClient(Socket client) throws IOException {
        //用于接收客户端消息转换成字符流
        BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));

        //用于向客户端发送消息
        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
        out.println("欢迎来到聊天室");
        while (!mIsServiceDestoryed) {
            String str = in.readLine();
            System.out.println("msg from client:" + str);
            if (str == null) {
                //客户端断开链接
                break;
            }

            int i = new Random().nextInt(mDefinedMessages.length);
            String msg = mDefinedMessages[i];
            out.println(msg);
            System.out.println("send:" + msg);

        }
        System.out.println("client quit.");
        MyUtils.close(out);
        MyUtils.close(in);
        client.close();
    }
}

加入权限和多进程

    
    
...
    

2.5 Binder 连接池


简单讲就是使用一个 Service,一个 Binder 连接池来替代多个 Service 的实现。毕竟多个服务进程会让应用看起来繁重,用户有想卸载的欲望。

源码链接

  1. 服务端 onBind 返回 BinderPool 的 IBinder 实例,它实现了 queryBinder 方法,这个接口能够根据业务模块的特征来返回相应的 Binder 对象给它们,不同的业务模块拿到所需的 Binder 对象后就可以进行远程方法调用了。
@Override
public IBinder onBind(Intent intent) {
 Log.d(TAG, "onBind");
 return new BinderPool.BinderPoolImpl();
}
  1. Binder 连接池的主要作用就是将每个业务模块的 Binder 请求统一转发到远程Service去执行,从而避免了创建多个 Service。
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
 IBinder binder = null;
 switch (binderCode) {
     case BINDER_SECURITY_CENTER: {
         binder = new SecurityCenterImpl();
         break;
     }
     case BINDER_COMPUTE: {
         binder = new ComputeImpl();
         break;
     }
     default:
         break;
 }
 return binder;
}

2.6 选用合适的 IPC 方式

名称 有点 缺点 使用场景
Bundle 简单易用 智能传输 Bundle 支持的数据 四大组件的进程间通信
文件共享 简单易用 不合适高并发场景,并且无法做到进程间的即时通信 无并发访问情形,交互含简单的数据实时性不高的场景
AIDL 功能强大,支持一对多并发通信 使用稍微复杂,需要处理好线程同步 一对多通信且有 RPC 需求
Messenger 功能一般,支持一对多串行通信,支持实时通信 不能很好处理高并发情形,不支持 RPC ,数据通过 Messager 进行传输,因此只能传输 Bundle 支持的数据类型 第并发的一对多即时通信,无 RPC 需求,或者无须要返回结果的 RPC 需求
ContentProvider 在数据源访问方面功能强大,支持一对多并发数据共享,可通过 Call 方法扩展其他操作 可以理解为受约束的 AIDL,主要提供数据源的 CRUD 操作 一对多进程间的数据共享
Socket 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 实现细节稍微有点繁琐,不支持直接的 RPC 网络数据交换

你可能感兴趣的:(IPC 机制(下))