Android跨进程通信(IPC机制)简析

IPC是 Inter-Process Communication 的缩写,含义为跨进程通信,是指两个进程间的数据交换过程,至于什么是进程,程序如何实现多进程我们就不赘述了,本篇博客主要来总结一下Android跨进程通信的几种方式。

(1)使用Bundle

Android四大组件的三大组件(Activity,Serive,Receiver)都是支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable,所以它可以方便的在不同的进程间传输。基于这一点,当我们在一个进程中启动了另一个进程的Activity,Service和Receiver ,我们就可以在Bundle中附加我们需要传送的信息并通过Intent传送出去,当然,我们通过Intent传送的数据必须能够被序列化。

(2)使用文件共享

文件共享也是一种不错的进程间通信方式,两个进程通过读/写同一个文件夹来交换数据。由于Android系统基于Linux,使得其并发读/写文件可以没有限制的进行,甚至两个线程同时对一个文件进行读/写都是允许的。但通过文件跨进程共享数据会导致一些意想不到的问题。

(3) 使用AIDL

AIDL全称是Android接口定义语言 可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。 进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。

下面是使用AIDL的步骤:

1. 创建.aidl文件

AIDL 使用简单语法,使您能通过可带参数和返回值的一个或多个方法来声明接口。 参数和返回值可以是任意类型,甚至可以是其他 AIDL 生成的接口。

2. 创建客户端服务端进行通信的实体类,并实现Parcelable接口。

3. 创建aidl文件

4. 创建于实体类对应的aidl文件

5. 创建通信接口aidl文件

6. build project,然后找到build/generated/source/aidl目录下面生成的Java文件

默认情况下,AIDL 支持下列数据类型:

Java 编程语言中的所有原语类型(如 int、long、char、boolean 等等) 
String 
CharSequence 
List 
List 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或你声明的可打包类型。 可选择将 List 用作“通用”类(例如,List)。另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。

Map 
Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 不支持通用Map 另一端实际接收的具体类始终是 HashMap,但生成的方法使用的是 Map 接口。

你必须为以上未列出的每个附加类型加入一个 import 语句,即使这些类型是在与你的接口相同的软件包中定义。

7.实现服务端代码

package com.example.huangjie.ipc;

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

import java.util.ArrayList;
import java.util.List;


/**
 * Created by huangjie on 2018/5/22.
 */

public class AidlService extends Service {
    private ArrayList<Book> bookList;
    private IBinder mBinder = new BookManager.Stub() {
        @Override
        public void addBook(Book book) throws RemoteException {
            bookList.add(book);
        }

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

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {

        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        bookList = new ArrayList<>();
        bookList.add(new Book("ID123", "Android开发艺术探索"));
        bookList.add(new Book("ID124", "剑指offer Java版"));

    }


}

8.声明服务端Service

 <service
            android:name=".AidlService"
            android:process=":test">
        </service>

9.编写客户端绑定Service代码

package com.example.huangjie.ipc;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;

import java.util.List;


public class MainActivity extends AppCompatActivity {


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


        ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {

                BookManager bookManager = BookManager.Stub.asInterface(service);

                try {
                    List<Book> bookList = bookManager.getBookList();

                    Toast.makeText(getApplicationContext(), "来自服务端的数据" + bookList.toString(), Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };

        Intent intent = new Intent(this, AidlService.class);

        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }


}

10.总结

1.实现服务端于客户端通信实体类并实现Parcelable接口


2.在项目 src/ 目录中加入 .aidl 文件。


3.声明一个 IBinder 接口实例(基于 AIDL 生成)。


4.实现 ServiceConnection。


5。调用 Context.bindService(),以传入你的 ServiceConnection 实现。


6.在你的 onServiceConnected() 实现中,你将收到一个 IBinder 实例(名为 service)。调用BookManager bookManager = BookManager.Stub.asInterface(service) ,以将返回的参数转换为BookManager 类型。


7.调用你在接口上定义的方法。你应该始终捕获 DeadObjectException 异常,它们是在连接中断时引发的;这将是远程方法引发的唯一异常。


8.如需断开连接,请使用你的接口实例调用 Context.unbindService()。
 

(4)使用Messenger

Messenger可以翻译为信使,通过它可以在不同的进程中传递 Message 对象,在 Message 中放入我们需要传递的数据,就可轻松的实现数据的跨进程通信。Messager是一种轻量级的IPC方案,他的底层实现是AIDL, 这点从Message的构造方法就能看出来,不管是 IMessenger 还是 Stub.asInterface, 这种使用方法都表明它的底层就是AIDL。

Public Messenger(Handler handler){

   mTarget = target.getIMessenger();
}

public Messenger(IBinder target){
   mTarget = IMessenger.Stub.asInterface(target);
}

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

1.服务端进程

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

package test.zxl.com.test_messenger_server;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;

import test.zxl.com.test_messenger.data.TestChildObject;
import test.zxl.com.test_messenger.data.TestObject;

public class TestService extends Service {
    private static final String TAG = "TestService";

    private static final String ACTION_STOP_SERVICE = "ACTION_STOP_SERVICE";

    private static final int MSG_TEST_BASIC = 1;
    private static final int MSG_TEST_OBJECT = 2;

    private Messenger mMessenger = new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MSG_TEST_BASIC:
                    Bundle mBundleBasic = msg.getData();
                    mBundleBasic.setClassLoader(getClass().getClassLoader());
                    Log.d(TAG,"server::MSG_TEST_BASIC::msg.arg1 = " + msg.arg1 + "::msg.objct = " + mBundleBasic.get("MSG_TEST_BASIC"));

                    Message messageBasic = Message.obtain();
                    messageBasic.what = MSG_TEST_BASIC;
                    mBundleBasic = new Bundle();
                    mBundleBasic.putInt("MSG_TEST_BASIC",112233);
                    messageBasic.setData(mBundleBasic);
                    try {
                        msg.replyTo.send(messageBasic);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }

                    break;
                case MSG_TEST_OBJECT:
                    Bundle mBundleObject = msg.getData();
                    mBundleObject.setClassLoader(getClass().getClassLoader());
                    Object mObjectData = mBundleObject.get("MSG_TEST_OBJECT");
                    TestObject mTestObject = null;
                    if(mObjectData instanceof TestObject){
                        mTestObject = (TestObject)mObjectData;
                    }
                    Log.d(TAG,"server::MSG_TEST_OBJECT::msg.objct = " + mTestObject);

                    Message messageObject = Message.obtain();
                    messageObject.what = MSG_TEST_OBJECT;
                    TestChildObject mTestChildObject = new TestChildObject();
                    mTestObject.mTestChildObject = mTestChildObject;
                    mTestObject.testInt = 112255;
                    mTestObject.testStr="server";
                    mTestObject.mTestChildObject.testInt = 112266;
                    mTestObject.mTestChildObject.testStr = "server_child";
                    mBundleObject.putParcelable("MSG_TEST_OBJECT",mTestObject);
                    messageObject.setData(mBundleObject);
                    try {
                        msg.replyTo.send(messageObject);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    });

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG,"onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG,"onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind");
        return mMessenger.getBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG,"onUnbind");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        Log.d(TAG,"onDestroy");
        super.onDestroy();
    }

    class StopReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            String mAction = intent.getAction();
            Log.d(TAG,"onReceive::mAction = " + mAction);
            if(TextUtils.equals(mAction,ACTION_STOP_SERVICE)){
                stopSelf();
            }
        }
    }
}

2.客户端进程

package test.zxl.com.test_messenger_client;

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.Messenger;
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 test.zxl.com.test_messenger.data.TestChildObject;
import test.zxl.com.test_messenger.data.TestObject;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    private static final int MSG_TEST_BASIC = 1;
    private static final int MSG_TEST_OBJECT = 2;

    private Button mBtnBaic;
    private Button mBtnObject;

    private Messenger mServerMessenger;
    private Messenger mClientMessenger = new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MSG_TEST_BASIC:
                    Bundle mBundleBasic = msg.getData();
                    mBundleBasic.setClassLoader(getClass().getClassLoader());
                    Log.d(TAG,"server::MSG_TEST_BASIC::msg.arg1 = " + msg.arg1 + "::msg.objct = " + mBundleBasic.get("MSG_TEST_BASIC"));
                    break;
                case MSG_TEST_OBJECT:
                    Bundle mBundleObject = msg.getData();
                    mBundleObject.setClassLoader(getClass().getClassLoader());
                    Object mObjectData = mBundleObject.get("MSG_TEST_OBJECT");
                    TestObject mTestObject = null;
                    if(mObjectData instanceof TestObject){
                        mTestObject = (TestObject)mObjectData;
                    }
                    Log.d(TAG,"server::MSG_TEST_OBJECT::msg.objct = " + mTestObject);
                    break;
            }
        }
    });

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

        Log.d(TAG,"client::onCreate");

        mBtnBaic = findViewById(R.id.btn_basic);
        mBtnObject = findViewById(R.id.btn_object);

        Intent mIntent = new Intent();
        mIntent.setComponent(new ComponentName("test.zxl.com.test_messenger_server","test.zxl.com.test_messenger_server.TestService"));
        bindService(mIntent,mServiceConnection, Context.BIND_AUTO_CREATE);

        mBtnBaic.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message message = Message.obtain();
                message.what = MSG_TEST_BASIC;
                Bundle mBundle = new Bundle();
                mBundle.putInt("MSG_TEST_BASIC",123);
                message.setData(mBundle);
                message.replyTo = mClientMessenger;
                try {
                    mServerMessenger.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        mBtnObject.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message message = Message.obtain();
                message.what = MSG_TEST_OBJECT;
                Bundle mBundle = new Bundle();
                TestObject mTestObject = new TestObject();
                TestChildObject mTestChildObject = new TestChildObject();
                mTestObject.mTestChildObject = mTestChildObject;
                mTestObject.testInt = 125;
                mTestObject.testStr="client";
                mTestObject.mTestChildObject.testInt = 126;
                mTestObject.mTestChildObject.testStr = "client_child";
                mBundle.putParcelable("MSG_TEST_OBJECT",mTestObject);
                message.setData(mBundle);
                message.replyTo = mClientMessenger;
                try {
                    mServerMessenger.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });


    }

    ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG,"client::onServiceConnected");
            mServerMessenger = new Messenger(service);
        }

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

3.客户端、服务端添加序列化对象TestObject、TestChildObject

package test.zxl.com.test_messenger.data;

import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.RequiresApi;

public class TestObject implements Parcelable {
    public int testInt;
    public String testStr = "";
    public TestChildObject mTestChildObject = null;

    public TestObject() {
    }

    protected TestObject(Parcel in) {
        testInt = in.readInt();
        testStr = in.readString();
        mTestChildObject = in.readParcelable(TestChildObject.class.getClassLoader());
    }

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

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(testInt);
        dest.writeString(testStr);
        dest.writeParcelable(mTestChildObject,flags);
    }

    @Override
    public String toString() {
        return "TestObject{" +
                "testInt=" + testInt +
                ", testStr='" + testStr + '\'' +
                ", mTestChildObject=" + mTestChildObject +
                '}';
    }
}

(5)使用ContentProvider

ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式,他天生就适合进程间通信。和Messenger一样,ContentProvider 的底层实现同样是Binder,由此可见,Binder 在Android 系统中是何等重要。ContentProvider的使用过程较为简单,我就不再演示,大家可以自行练习。
 

(6)使用套接字(Socket)

Socket是网络通信的概念,它分为流式套接字和用户数据报套接字,分别对应网络的传输控制层中的TCP 和 UDP 协议。TCP协议是面向连接的协议,提供稳定的双向通信功能,TCP连接的建立要通过“三次握手”来实现。UDP是无连接的协议,具有更好的效率,缺点是不能保证数据是否一定能正确传输。

Socket基于TCP和UDP的建立连接方式的参考文献:

Socket基于TCO和UDP建立连接的方式

好了,到此Android的跨进程通信方式已经都基本介绍到了,相信大家对于Android跨进程通信已经有了自己的理解,让我们一起再接再厉吧!


 

发布了9 篇原创文章 · 获赞 11 · 访问量 259

猜你喜欢

转载自blog.csdn.net/Healer_LU/article/details/103674205