Android跨进程通信(IPC机制 一)

作者: Android_Jian | 来源:发表于2018-08-22 23:47 被阅读67次

最近在复习主席的《Android开发艺术探索》这本书,将第二章IPC机制相关的部分简单总结下。

开启多进程的方式:给四大组件在AndroidMenifest文件中指定 android:process属性。

Android中的IPC方式主要有六种,分别为:

  1. 使用Bundle
  2. 使用文件共享
  3. 使用Messenger
  4. 使用AIDL
  5. 使用ContentProvider
  6. 使用Socket

下面我们主要看下 3. 使用Messenger 和 4.使用AIDL 这两种跨进程通信的方式。其实Messenger的底层实现也是AIDL。

使用Messenger:

实现一个Messenger可以分为两个步骤,分别为服务端和客户端。

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

客户端进程:客户端进程中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,消息类型为Message对象。

下面我们新建一个Demo来测试下,我们在客户端进程中向服务端发送一句话,在服务端进程中打印出这句话:

1.新建项目MessengerTest,创建MessengerService,让其运行在单独的进程中。

        <service
            android:name=".MessengerService"
            android:process=":remote"/>

2.服务端进程代码:

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 MyConstants.MSG_FROM_CLIENT:
                    Log.e(TAG,"receive msg from client:  "+msg.getData().getString("msg"));
                    break;
                
                default:
                    break;
            }
        }
    }
    
    private final Messenger mMessenger = new Messenger(new MessengerHandler());
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

3.客户端进程代码:

public class MainActivity extends AppCompatActivity {

    private Button mBtnSend;
    
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMessenger = new Messenger(service);
            Message msg = Message.obtain(null,MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg","hello world");
            msg.setData(data);
            try {
                mMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    private Messenger mMessenger;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnSend = (Button) findViewById(R.id.mBtnSend);
        
        mBtnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = new Intent(MainActivity.this,MessengerService.class);
                bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
            }
        });
    }

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

测试结果如下:

08-19 23:13:42.646 27432-27432/? E/MessengerService: receive msg from client:  hello world

上述我们只是发送消息到服务端进程,服务端进程对msg进行相应的操作。也就是说我们只是实现了客户端到服务端的单向通信,如果我们需要服务端处理完毕后回复客户端进程的话,要怎么操作呢?修改后的代码如下:

服务端进程:

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 MyConstants.MSG_FROM_CLIENT:
                    Log.e(TAG,"receive msg from client:  "+msg.getData().getString("msg"));

                    Messenger client = msg.replyTo;
                    Message replyMessage = Message.obtain(null,MyConstants.MSG_FROM_SERVICE);
                    Bundle bundle = new Bundle();
                    bundle.putString("reply","你好,世界");
                    replyMessage.setData(bundle);
                    try {
                        client.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;

                default:
                    break;
            }
        }
    }

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

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

客户端进程:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private Button mBtnSend;
    private Messenger mMessenger;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMessenger = new Messenger(service);
            Message msg = Message.obtain(null,MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg","hello world");
            msg.setData(data);

            msg.replyTo = mGetReplyMessenger;
            try {
                mMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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

    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MyConstants.MSG_FROM_SERVICE:
                    Log.e(TAG,"receive msg from service:  "+msg.getData().getString("reply"));

                    break;
                default:
                    break;
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnSend = (Button) findViewById(R.id.mBtnSend);

        mBtnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = new Intent(MainActivity.this,MessengerService.class);
                bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
            }
        });
    }

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

测试结果如下:

08-19 23:34:04.236 4737-4737/com.example.administrator.messengertest:remote E/MessengerService: receive msg from client:  hello world
08-19 23:34:04.236 4386-4386/com.example.administrator.messengertest E/MainActivity: receive msg from service:  你好,世界

有关Messenger的操作方式到这里就介绍完毕了,下面我们看下AIDL方式。使用AIDL来进行进程间通信,同样分为服务端和客户端两个方面。

  1. 服务端
    服务端首先要创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。

  2. 客户端
    客户端所要做的事情就稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。

下面我们看下代码:

  1. 新建项目AIDLTest,创建BookManagerService,让其运行在单独的进程中。
        <service
            android:name=".BookManagerService"
            android:process=":remote"/>
  1. AIDL文件创建,为了方便AIDL的开发,将所有和AIDL相关的类和文件全部放入同一个包中,如下所示: 截图.PNG

Book.java:

package com.example.administrator.aidltest;

import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable{

    public int bookId;

    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.bookId);
        dest.writeString(this.bookName);
    }

    protected Book(Parcel in) {
        this.bookId = in.readInt();
        this.bookName = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
}

Book.aidl:

// Book.aidl
package com.example.administrator.aidltest;

parcelable Book;

IBookManager.aidl:

// IBookManager.aidl
package com.example.administrator.aidltest;

import com.example.administrator.aidltest.Book;

interface IBookManager {

     List<Book> getBookList();
     void addBook(in Book book);
}
  1. 服务端进程代码:
package com.example.administrator.aidltest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class BookManagerService extends Service{

    private static final String TAG = "BMS";
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private Binder mBinder = new IBookManager.Stub(){

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
  1. 客户端进程代码:
package com.example.administrator.aidltest;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private Button mBookAdd;
    private Button mBookGet;

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

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    private IBookManager bookManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initListener();
        serviceBind();
    }

    private void initView(){
        mBookAdd = (Button) findViewById(R.id.mBookAdd);
        mBookGet = (Button) findViewById(R.id.mBookGet);
    }

    private void serviceBind(){
        Intent intent = new Intent(MainActivity.this,BookManagerService.class);
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    private void initListener(){
        mBookAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (bookManager != null){
                    try {
                        bookManager.addBook(new Book(001,"第一行代码"));
                        bookManager.addBook(new Book(002,"Android艺术开发探索"));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        mBookGet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (bookManager != null){
                    try {
                        List<Book> mBookList = bookManager.getBookList();
                        for (int i = 0; i<mBookList.size(); i++){
                            Log.e(TAG,"query book list: "+mBookList.get(i).bookName+"\n");
                        }
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}
  1. 编译运行出错,提示找不到符号:
Error:(20, 34) 错误: 找不到符号
符号:   类 Book
位置: 类 BookManagerService

解决方式:在 app 目录下的build.gradle文件中添加java.srcDirs路径,如下所示:

    sourceSets{       //android节点下
        main{
            java.srcDirs = ['src/main/java','src/main/aidl']
        }
    }

测试结果如下:

08-21 00:01:18.899 30868-30868/com.example.administrator.aidltest E/MainActivity: query book list: 第一行代码
08-21 00:01:18.899 30868-30868/com.example.administrator.aidltest E/MainActivity: query book list: Android艺术开发探索

现在我们考虑下,如果用户不想时不时地去查询图书列表了,太累了,他去问图书馆,“当有新书的时候,能不能把新书的信息告诉我呢?”。很显然这是一种观察者模式,下面我们来实现下:

  1. 在上述代码的基础上,新增 IOnNewBookArrivedListener.aidl文件:
// IOnNewBookArrivedListener.aidl
package com.example.administrator.aidltest;

import com.example.administrator.aidltest.Book;

interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book newBook);
}
  1. 在原有 IBookManager 接口中增加两个方法:
// IBookManager.aidl
package com.example.administrator.aidltest;

import com.example.administrator.aidltest.Book;
import com.example.administrator.aidltest.IOnNewBookArrivedListener;

interface IBookManager {

     List<Book> getBookList();
     void addBook(in Book book);
     void registerListener(IOnNewBookArrivedListener listener);
     void unregisterListener(IOnNewBookArrivedListener listener);
}
  1. 服务端代码修改如下:
package com.example.administrator.aidltest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

public class BookManagerService extends Service{

    private static final String TAG = "BMS";

    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);

    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =
            new CopyOnWriteArrayList<IOnNewBookArrivedListener>();

    private Binder mBinder = new IBookManager.Stub(){

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            if (!mListenerList.contains(listener)){
                mListenerList.add(listener);
            }
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            if (mListenerList.contains(listener)){
                mListenerList.remove(listener);
            }
        }
    };

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

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

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

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        for (int i=0; i<mListenerList.size(); i++){
            IOnNewBookArrivedListener listener = mListenerList.get(i);
            listener.onNewBookArrived(book);
        }
    }

    private class ServiceWorker implements Runnable{

        @Override
        public void run() {
            while (!mIsServiceDestroyed.get()){
                try {
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }

                int bookId = mBookList.size()+1;
                Book newBook = new Book(bookId,"new book#"+bookId);

                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  1. 客户端代码修改如下:
package com.example.administrator.aidltest;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
    
    private IBookManager mRemoteBookManager;

    private Button mBookAdd;
    private Button mBookGet;
    
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Log.e(TAG,"receive new book :  "+msg.obj);
                    break;
                
                default:
                    break;
            }
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IBookManager.Stub.asInterface(service);
            try {
                mRemoteBookManager = bookManager;
                bookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteBookManager = null;
        }
    };
    
    private IOnNewBookArrivedListener mListener = new IOnNewBookArrivedListener.Stub(){
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,newBook).sendToTarget();
        }
    };
    
    private IBookManager bookManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initListener();
        serviceBind();
    }

    private void initView(){
        mBookAdd = (Button) findViewById(R.id.mBookAdd);
        mBookGet = (Button) findViewById(R.id.mBookGet);
    }

    private void serviceBind(){
        Intent intent = new Intent(MainActivity.this,BookManagerService.class);
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    private void initListener(){
        mBookAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (bookManager != null){
                    try {
                        bookManager.addBook(new Book(001,"第一行代码"));
                        bookManager.addBook(new Book(002,"Android艺术开发探索"));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        mBookGet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (bookManager != null){
                    try {
                        List<Book> mBookList = bookManager.getBookList();
                        for (int i = 0; i<mBookList.size(); i++){
                            Log.e(TAG,"query book list: "+mBookList.get(i).bookName+"\n");
                        }
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()){
            try {
                mRemoteBookManager.unregisterListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
        super.onDestroy();
    }
}

运行结果如下:

08-22 00:24:59.829 12598-12598/com.example.administrator.aidltest E/MainActivity: receive new book :  com.example.administrator.aidltest.Book@3010b98b
08-22 00:25:02.829 12598-12598/com.example.administrator.aidltest E/MainActivity: receive new book :  com.example.administrator.aidltest.Book@1c298a68
08-22 00:25:05.829 12598-12598/com.example.administrator.aidltest E/MainActivity: receive new book :  com.example.administrator.aidltest.Book@1a4bce81

我们接着分析,在MainActivity销毁时,我们会解除已经注册到服务端的listener,结果服务端无法找到我们之前注册的那个listener,解注册宣布失败。原因是:对象是不能跨进程直接传输的,对象的跨进程传输本质上都是反序列化的过程,Binder会把客户端传递过来的对象重新转化并生成一个新的对象。解决方式是采用RemoteCallbackList,代码修改如下:

BookManagerService:

package com.example.administrator.aidltest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

public class BookManagerService extends Service{

    private static final String TAG = "BMS";

    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);

    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList =
            new RemoteCallbackList<IOnNewBookArrivedListener>();

    private Binder mBinder = new IBookManager.Stub(){

        @Override
        public List<Book> 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);
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.unregister(listener);
        }
    };

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

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

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

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        final int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++){
            IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
            if (l != null){
                l.onNewBookArrived(book);
            }
        }
        mListenerList.finishBroadcast();
    }

    private class ServiceWorker implements Runnable{

        @Override
        public void run() {
            while (!mIsServiceDestroyed.get()){
                try {
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }

                int bookId = mBookList.size()+1;
                Book newBook = new Book(bookId,"new book#"+bookId);

                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

这样,跨进程解注册问题就解决了,到这里AIDL的基本使用方法就介绍完毕了。

相关文章

网友评论

    本文标题:Android跨进程通信(IPC机制 一)

    本文链接:https://www.haomeiwen.com/subject/hathiftx.html