【Android学习】IPC(跨进程通信,Inter-Process Communication)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SunshineTan/article/details/78649816

1,概念

IPC(跨进程通信,进程间通信,Inter-Process Communication):指两个进程之间进行数据交换的过程。

1)Binder机制

①概念

可以实现进程间通信。
Binder类实现了IBinder接口。

也可以理解为是一种虚拟的物理设备,设备驱动是/dev/binder。

从Android Framework角度:Binder是ServiceManager连接各种Manager和相应ManagerService的桥梁;
从Android应用层角度:Binder是客户端和服务端进行通信的媒介。如bindService,服务端会返回一个包含了服务端调用的Binder对象,客户端可以通过它获取服务端提供的服务或数据。

②优点

相比其它IPC方式,优点在于:
i>高性能
从数据拷贝次数来看Binder只需要进行一次内存拷贝,而管道、消息队列、Socket都需要两次,共享内存不需要拷贝,Binder的性能仅次于共享内存。
ii>稳定性
上面说到共享内存的性能优于Binder,那为什么不适用共享内存呢,因为共享内存需要处理并发同步问题,控制负责,容易出现死锁和资源竞争,稳定性较差。而Binder基于C/S架构,客户端与服务端彼此独立,稳定性较好。
iii>安全性
我们知道Android为每个应用分配了UID,用来作为鉴别进程的重要标志,Android内部也依赖这个UID进行权限管理,包括6.0以前的固定权限和6.0以后的动态权限,传荣IPC只能由用户在数据包里填入UID/PID,这个标记完全
是在用户空间控制的,没有放在内核空间,因此有被恶意篡改的可能,因此Binder的安全性更高。

③IBinder

IBinder接口的实现类,该类用以提供客户端用来与服务进行交互的编程接口,该接口可以通过三种方法定义接口:

扩展 Binder 类

场景:服务仅供本地应用使用,不需要跨进程工作
原理:通过扩展 Binder 类并从 onBind() 返回它的一个实例来创建接口。客户端收到 Binder 后,可利用它直接访问 Binder 实现中以及Service 中可用的公共方法。
实现:
1.创建BindService服务端,继承自Service并在类中,创建一个实现IBinder 接口的实例对象并提供公共方法给客户端调用
2.从 onBind() 回调方法返回此 Binder 实例。
3.在客户端中,从 onServiceConnected() 回调方法接收 Binder,并使用提供的方法调用绑定服务。

public class LocalService extends Service{
    private final static String TAG = "wzj";
    private int count;
    private boolean quit;
    private Thread thread;
    private LocalBinder binder = new LocalBinder();

    /**
     * 创建Binder对象,返回给客户端即Activity使用,提供数据交换的接口
     */
    public class LocalBinder extends Binder {
        // 声明一个方法,getService。(提供给客户端调用)
        LocalService getService() {
            // 返回当前对象LocalService,这样我们就可在客户端端调用Service的公共方法了
            return LocalService.this;
        }
    }

    /**
     * 把Binder类返回给客户端
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }


    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "Service is invoke Created");
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                // 每间隔一秒count加1 ,直到quit为true。
                while (!quit) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                }
            }
        });
        thread.start();
    }

    /**
     * 公共方法
     * @return
     */
    public int getCount(){
        return count;
    }
    /**
     * 解除绑定时调用
     * @return
     */
     @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, "Service is invoke onUnbind");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "Service is invoke Destroyed");
        this.quit = true;
        super.onDestroy();
    }
}
public class BindActivity extends Activity {
    protected static final String TAG = "wzj";
    Button btnBind;
    Button btnUnBind;
    Button btnGetDatas;
    /**
     * ServiceConnection代表与服务的连接,它只有两个方法,
     * onServiceConnected和onServiceDisconnected,
     * 前者是在操作者在连接一个服务成功时被调用,而后者是在服务崩溃或被杀死导致的连接中断时被调用
     */
    private ServiceConnection conn;
    private LocalService mService;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bind);
        btnBind = (Button) findViewById(R.id.BindService);
        btnUnBind = (Button) findViewById(R.id.unBindService);
        btnGetDatas = (Button) findViewById(R.id.getServiceDatas);
        //创建绑定对象
        final Intent intent = new Intent(this, LocalService.class);

        // 开启绑定
        btnBind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "绑定调用:bindService");
                //调用绑定方法
                bindService(intent, conn, Service.BIND_AUTO_CREATE);
            }
        });
        // 解除绑定
        btnUnBind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "解除绑定调用:unbindService");
                // 解除绑定
                if(mService!=null) {
                    mService = null;
                    unbindService(conn);
                }
            }
        });

        // 获取数据
        btnGetDatas.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mService != null) {
                    // 通过绑定服务传递的Binder对象,获取Service暴露出来的数据

                    Log.d(TAG, "从服务端获取数据:" + mService.getCount());
                } else {

                    Log.d(TAG, "还没绑定呢,先绑定,无法从服务端获取数据");
                }
            }
        });


        conn = new ServiceConnection() {
            /**
             * 与服务器端交互的接口方法 绑定服务的时候被回调,在这个方法获取绑定Service传递过来的IBinder对象,
             * 通过这个IBinder对象,实现宿主和Service的交互。
             */
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG, "绑定成功调用:onServiceConnected");
                // 获取Binder
                LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
                mService = binder.getService();
            }
            /**
             * 当取消绑定的时候被回调。但正常情况下是不被调用的,它的调用时机是当Service服务被意外销毁时,
             * 例如内存的资源不足时这个方法才被自动调用。
             */
            @Override
            public void onServiceDisconnected(ComponentName name) {
                mService=null;
            }
        };
    }
}

2)Socket

可以实现任意两个终端/进程的通信。

3)Serializable、Parcelable

4)Linux进程通信:

①管道

在创建时分配一个page大小的内存,缓存区大小比较有限;

②消息队列

信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;

③共享内存

无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;

④套接字

作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;

⑤信号量

常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。

当它的值大于0时,表示当前可用资源的数量;
当它的值小于0时,其绝对值表示等待使用该资源的进程个数。

注意,信号量的值仅能由PV操作来改变。
一般来说,信号量S³0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S£0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。

⑥信号

不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;

2,多进程模式

1)概念

默认进程的进程名为包名。
我们可以在eclipse的DDMS视图中查看进程信息。

2)实现

方法一:通过给四大组件指定Android:process属性,可以开启多进程模式。

<activity
    android:name="com.test.SecondActivity"
    android:label="@string/app_name"
    android:process=":remote"/>
    //当SecondActivity启动时,系统会为它创建一个单独的进程,进程名为:`com.test:remote`。对于以`:`开头的进程属于私有进程,其他组件不可和他在一个进程中跑;否则为全局进程,如:`android:process="com.test.remote"`,其它应用通过ShareUID方式可以和它跑在同一个进程中。(Android为每个应用分配一个唯一的UID,相同UID的应用才能共享数据。所以两个应用要跑在同一个进程中必须保证:有相同的ShareUID并且签名相同,这样才可以互相访问对方的私有数据:共享data、组件信息、内存数据。
    //对于MainActivity,没有指定进程名,作为入口Activity,它运行在默认进程中,进程名是包名。

Android为每个进程都分配了一个独立的VM,不同VM在内存上有不同的地址空间。不同进程进行的操作不会影响到其它进程。所以一般不采用多进程。

方法二:通过JNI在native层去fork一个新的进程。

3)多进程问题

不同进程中的四大组件无法通过内存共享数据。

①静态成员和单例模式完全失效

Android为每一个进程分配了一个独立的虚拟机,不同的VM在内存上有不同的地址空间,导致不同的VM访问同一个类的对象会产生多份副本。

②线程同步机制完全失效

不同进程在不同内存中,锁对象(不是同一个对象)无法保证线程同步。

③SharedPreferences的可靠性下降

SharedPreferences不支持两个进程同时执行写操作,会丢失数据。

④Application会多次创建

在不同的VM上,会创建不同的Application。

3,跨进程通信

IPC方式的优缺点和适用场景

1)Intent传递数据

在Intent中附加extras来传递信息。

Activity、Service、Receiver都支持Intent中传递Bundle数据。
Bundle实现了Parcelable接口,可以在不同进程间传输。

Bundle中的数据必须能够被序列化:如基本类型、实现了Parcellable接口的对象、实现了Serializable接口的对象或者是Android支持的特殊对象。

2)ContentProvider

支持跨进程访问。

3)广播

4)共享文件和SharedPreferences

适合用于数据同步要求不高的进程间通信,要妥善处理并发读/写的问题。

在面对高并发的读/写访问时:
i>文件支持多个进程同时写,所以高并发情况下读取的内容可能不是最新的。ii>SharedPreferences:由于其读写的缓存策略,即在内存中有一份SharePreferences文件的缓存,多并发情况下,有很大几率丢失数据,不建议用于跨进程通信。

5)基于Binder的Messenger

注意:handler是跨线程!

①Messenger

Messenger是轻量级的IPC方案,它的底层实现是AIDL。Messenger封装了AIDL,一次处理一个请求,不存在多线程并发的问题。
在不同的进程中共传递Message对象(Handler中的Messager,因此 Handler 是 Messenger 的基础)。
Messenger和Message实现了Parcelable接口。

Messenger载体:
what、arg1、arg2、Bundle、replyTo、object(只有系统定义的实现了Parcelable接口的对象才可以跨进程传递,自定义的实现了Parcelable接口的对象不可通过该字段传输)

②原理

使用 Messenger 为服务创建接口,客户端就可利用 Message 对象向服务发送命令。同时客户端也可定义自有 Messenger(在Message中可以存放我们需要传递的数据),以便服务回传消息。
因为 Messenger 会在单一线程中创建包含所有请求的队列,以串行的方式处理客户端发来的消息,所以不需要担心线程安全问题。

③实现

举例1:
通过Messenger来传输Message,Message中能用的载体只有what、arg1、arg2、Bundle以及replyTo。object在跨进程通信中实用性降低,一般用Bundle来传递大量的数据类型。
通过Handle来实现Messenger的进程 间通信:

//1,服务端进程(Android进程)
public class MessengerService extends Service{//在服务端创建一个Service来处理客户端的连接请求
    private static final String TAG = "MessengerService"private static class MessengerHandler extends Handler{//创建一个Handler并通过它来创建一个Messenger对象
        @Override
        public void handleMessage(Message msg){
            switch(msg.what){
                case MyConstants.MSG_FROM_CLIENT:
                    Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
                    break;
            }
        }
    }
    private final Messenger mMessenger = new Messenger(new MessengerHandler());
    @Override
    public IBinder onBind(Intent intent){//在Service的onBind中返回这个Messenger对象底层的Binder即可
        return mMessenger.getBinder();
    }
}

需要 注册service,并让其运行在单独的进程中:

<service
    android:name="com.ret.MessengerService"
    android:process=":remote">
//2,客户端进程(Android进程)
//首先绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象。
//如果需要服务端能回应客户端,还需要创建一个Handler并创建一个新的Messenger,把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。
public class MessengerActivity extends Activity{
    private static final String TAG = “MessengerActivity”;
    private Messenger mService;
    private SeerviceConnection mConnection = new ServiceConnection(){
        public void onServiceConnected(ComponentName className, IBinder service){
            mService = new Messenger(service);
            Message msg = Message.obtain(null,MyConstants.MSG_FROM_CLIENT);
            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);
        ...
        Intent intent = new Intent(this,MessengerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 
    }
    @Override
    protected void onDestroy(){
        unbindeService(mConnection);
        super ...
    }
}

举例二:
1.服务实现一个 Handler,由其接收来自客户端的每个调用的回调
2.Handler 用于创建 Messenger 对象(对 Handler 的引用)
3.Messenger 创建一个 IBinder,服务通过 onBind() 使其返回客户端
4.客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,然后使用Messenger将 Message 对象发送给服务
5.服务在其 Handler 中(在 handleMessage() 方法中)接收每个 Message

public class MessengerService extends Service {

    /** Command to the service to display a message */
    static final int MSG_SAY_HELLO = 1;
    private static final String TAG ="wzj" ;

    /**
     * 用于接收从客户端传递过来的数据
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Log.i(TAG, "thanks,Service had receiver message from client!");
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * 创建Messenger并传入Handler实例对象
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * 当绑定Service时,该方法被调用,将通过mMessenger返回一个实现
     * IBinder接口的实例对象
     */
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "Service is invoke onBind");
        return mMessenger.getBinder();
    }
}
public class ActivityMessenger extends Activity {
    /**
     * 与服务端交互的Messenger
     */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service. */
    boolean mBound;

    /**
     * 实现与服务端链接的对象
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            /**
             * 通过服务端传递的IBinder对象,创建相应的Messenger
             * 通过该Messenger对象与服务端进行交互
             */
            mService = new Messenger(service);
            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        if (!mBound) return;
        // 创建与服务交互的消息实体Message
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            //发送消息
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenager);
        Button bindService= (Button) findViewById(R.id.bindService);
        Button unbindService= (Button) findViewById(R.id.unbindService);
        Button sendMsg= (Button) findViewById(R.id.sendMsgToService);

        bindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("zj","onClick-->bindService");
                //当前Activity绑定服务端
                bindService(new Intent(ActivityMessenger.this, MessengerService.class), mConnection,
                        Context.BIND_AUTO_CREATE);
            }
        });

        //发送消息给服务端
        sendMsg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sayHello(v);
            }
        });


        unbindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Unbind from the service
                if (mBound) {
                    Log.d("zj","onClick-->unbindService");
                    unbindService(mConnection);
                    mBound = false;
                }
            }
        });
    }

}

注意:把Service放在单独的进程中:

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

④场景

Messenger以串行的方式处理客户端发来的消息,对于大量的并发请求需要一个个处理,效率低。

6)AIDL(Android Interface Description Language,Android接口描述语言)

①概念

AIDL是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口.编译器可以通过扩展名为aidl的文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的。
Android系统中的进程之间不能共享内存,Android通过AIDL来公开服务的接口,将这种可以跨进程访问的服务称为AIDL服务。
AIDL可以生成进程间的接口的代码,诸如service可能使用的接口。
在Service(服务)的onBind(Intent intent)方法中返回mBinder实现了aidl接口的对象

②场景

实现跨进程的方法调用。
想让服务同时处理多个请求,则应该使用 AIDL。

③AIDL文件支持数据类型

使用这些类型时不需要import声明。

i>基本数据类型

ii>String和CharSequence

iii>List、Map、Parcelable

List:只支持ArrayList,里面每个元素都必须被AIDL支持。
Map:只支持HashMap,里面每个元素都必须被AIDL支持,包括key和value。
Parcelable:所有实现了Parcelable接口的对象。
AIDL:所有的AIDL接口本身也可以在AIDL文件中使用。
(这些类型内所包含的数据成员也只能是简单数据类型,String等其他支持类型)

④Binder连接池

创建一个Service即可完成多个AIDL接口的工作。

⑤建立AIDL服务

i>在Eclipse Android工程的Java包目录中建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同。
ii>如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(*.java)。
iii>建立一个服务类(Service的子类)。
iv>实现由aidl文件生成的Java接口。
v>在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。

⑥注意

i>aidl对应的接口名称必须与aidl文件名相同不然无法自动编译。
ii>aidl对应的接口的方法不能加访问权限修饰符,也不能用final,static。
iii>自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中。
iv>在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数。
v>Java原始类型默认的标记为in,不能为其它标记。

7)Socket和网络通信

Socket分为流式套接字和用户数据报套接字。
注意:
A.不要在主线程访问网络(Android4.0以上设备会抛出异常:android.os.NetworkOnMainThreadException)
B.当Activity退出时,关闭当前Socket

4,java IPC方式

JAVA进程间通信的方法主要有以下几种:

1)管道(Pipe)

管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。

①管道流只能在两个线程之间传递数据

②管道流只能实现单向发送,如果要两个线程之间互通讯,则需要两个管道流

2)命名管道(named pipe)

命名管道克服了管道没有名字的限制,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。

3)信号(Signal)

信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送 信号给进程本身。

4)消息(Message)队列

消息队列是消息的链接表,包括Posix消息队列system V消息队列。

5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。

6)内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。

7)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。

8)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。

猜你喜欢

转载自blog.csdn.net/SunshineTan/article/details/78649816