IPC机制

Android IPC简介

(1)IPC是Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。

(2)ANR是Application Not Responding的缩写,即应用无响应。主线程执行大量的耗时操作容易导致ANR现象发生。

(3)在Android中最有特色的进程间通信方式就是Binder了,通过Binder可以轻松地实现进程间通信。

(4)Android还支持Socket,通过Socket也可以实现任意两个终端或者两个进程之间的通信。

什么是进程,什么是线程,进程和线程是两个截然不同的概念。

    在操作系统中,线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程指的一个执行单元,在PC和移动设备上指的是一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含被包含的关系,最简单情况下,一个进程可以只有一个线程,即主线程,在Android里面也叫UI线程,在UI线程里才能操作界面元素。

    Android中一般都是通过Binder实现进程间通信。除了Binder,Android还支持Socket,通过Socket也可以实现任意两个终端之间的通信,当然一个设备上的两个进程之间通过Socket通信自然也是可以的。

多进程模式的运行机制

(1)多进程会带来很多意想不到的麻烦,因为Android为每一个应用都分配了一个独立的虚拟机,或者说为每个进程都分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致在不同的虚拟机中访问同一个类的对象会产生多份副本。这样很就容易导致数据不同步。

(2)所有运行在不同进程的四大组件,只要它们之间需要通过内存在共享数据,都会共享失败。

(3)主要有以下四方面的问题:

    1)静态成员和单例模式完全失效。(由独立虚拟机造成)

    2)线程同步机制完全失效。(同上)

    3)SharedPreferences的可靠性下降。(存在并发读写的问题)

    4)Application会多次创建。(新的进程中又会导致进程所在的Application在新的虚拟机中再次创建)

(4)运行在同一个进程中的组件是属于同一个虚拟机和同一个Application的,同理运行在不同进程的组件是属于两个不同的虚拟机和Application的。

基于上面的这些问题,因为我们需要学习进程间通信机制!!!!!

IPC基础概念介绍

    当我们需要通过Intent和Binder传输数据时就需要使用Parcelable或者Serializeble。Serializable和Parcelable接口可以完成对象的序列化过程。还有时候我们需要把对象持久化到存储 设备上或者通过网络传输给其他客户端,这个时候也需要Serializable来完成对象的持久化。

Parcelable方法:    

@Override
public Test createFromParcel(Parcel in) {
    return new Test(in);
}

从序列化后的对象中创建原始对象

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

创建指定长度的原始对象数组

protected Test/*Test为类名*/(Parcel in) {

}

将序列化后的对象中创建原始对象

@Override
public void writeToParcel(Parcel dest, int flags) {
}

将当前对象 写入序列化结构中,其中flags标识有俩种值,0或者1,为1时标识当前对象需要作为返回值返回,不能立即释放资源,一般都会返回0

writeToParcel()的标记位为:

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

返回当前对象的内容描述,如果含有文件描述符,返回1,否则返回0,一般都返回0

describeContents()的标记位为:

CONTENTS_FILE_DESCRIPTOR

Serializable和Parcelable的区别:

        Serializable是java中的序列化接口,使用简单,开销大,因为都是使用IO流进行操作

        Parcelable是Android中的序列化方式,因此更适合用在Android平台上,缺点就是他使用起来比较麻烦,但是他的效率比较高,Parcelable主要运用在内存序列化上,通过Parcelable将对象序列化到存储设备中,或者将对象序列化后通过王阔传输也可以,但是过程比较复杂

Binder:

        Binder是Android中的一个类,它实现了IBinder接口,是跨进程通信的一种方式

     从Framework角度来说,Binder是ServiceManager连接各种Manager和相应ManagerService的桥梁

        从Android  应用层来说,Binder是客户端和服务器端进行通信的媒介,当bindService时,客户端可以获取服务器提供的服务或者数据,服务包括普通服务和基于ALDL的服务

AIDL

AIDL有俩个类:Stub,Proxy

类中方法含义:

1)DESCRIPTOR:

      Binder的唯一标识,一般用当前Binder的类名表示

2)asInterface()

      用于将服务器端的Binder对象转换成客户端所需的AIDL接口类型的对象,转换是区分进程的,如果客户端和服务器端位于同一进程,那么此方法返回的就是服务器端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象

3)asBinder()

      此方法返回当前Binder对象

4)onTransact()

      此方法运行在服务器端中的Binder线程池,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理

      服务器通过code可以确定客户端所请求的目标方法是什么,接着从data中取出目标方法所需的参数,然后执行目标方法。当目标方法执行完毕后,就像reply中写入返回值

      如果此方法返回false,那么客户端的请求会失败,因此我们可以利用这个特性来做权限验证

   

Binder工作机制:

Binder中的方法:

        linkToDeath和unlinkToDeath

       Binder运行在服务端进程,如果服务端进程由于某种原因异常终止,服务器端的Binder连接断裂,会导致远程调用失败,如果说你并不知道连接已经断裂,那么客户端的功能会收到影响,因而会有这俩个方法,当Binder死亡时,我们额可以收到通知,从而重新发起连接请求而恢复连接。

使用这俩个方法

IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        //此处填写
    }
};

IPC实现方式

1.使用Bundle

2.使用文件共享

        使用文件共享时要尽量避免并发读写的情况,可以考虑使用线程同步来解决这种问题,所以文件共享的方式只适合在对数据同步要求不高的进程之浅见进行通信。

3.使用Messenger

        简单实现 Messenger:

            需要有服务端跟客户端

            1.服务端进程:

                创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后再Service的onBind中返回这个Messenger对象底层的Binder即可。

package com.example.messenger;

import android.app.Service;
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.util.Log;

public class MessengerService extends Service {

    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 100:

                    Log.e("TAGS","客户端发送的消息:"+msg.getData().getString("key"));
                    Messenger replyTo = msg.replyTo;
                    Message message = Message.obtain(null,200);
                    Bundle bundle = new Bundle();
                    bundle.putString("replyTo","服务器端已经收到了您发送的消息。");
                    message.setData(bundle);
                    try {
                        replyTo.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }

                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

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

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

!!!需要注册service

<service
    android:name=".MessengerService"
    android:enabled="true"
    android:exported="true"></service>

            2.客户端进程:

                    绑定服务端的Service,绑定成功后服务端返回的IBinder对象创建一个Messenger,通过这个 Messenger就可以向服务端发送消息,发送消息类型为Message对象。如果需要服务端能够回应客户端,就和服务端一样,我们需要创建一个Handler并创建一个新的 Messenger,并把这个 Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。

package com.example.messenger;

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;

public class MainActivity extends AppCompatActivity {

    private Messenger mService;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mService = new Messenger(iBinder);
            Message message = Message.obtain(null, 100);
            Bundle bundle = new Bundle();
            bundle.putString("key", "Hello Word!");
            message.setData(bundle);
            //当客户端发送消息的时候,需要把接受服务器回复的Messenger通过Message的replyTo参数传递给服务器
            message.replyTo = messenger;
            try {
                mService.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

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

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

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

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 200:

                    getData(msg);

                    break;
                default:
                    super.handleMessage(msg);

            }
        }

        private void getData(Message msg) {
            Bundle data = msg.getData();
            String replyTo = data.getString("replyTo");
            Log.e("TAGS","服务器返回的消息:"+replyTo);
        }
    }

}

        Messenger的工作原理:

        Messenger是以串行的方式处理客户端发来的消息,如果发送很多消息到服务端,但是服务端只能一个个去处理,所以遇到这种情况就不适合使用Messenger了,而且Messenger不支持跨进程调用服务端的方法。

Demo地址:

https://github.com/HaoMoster/Messenger

4.使用AIDL

AIDL简介:

    Android Interface Definition Language,即Android接口定义语言;用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。

  • 在多进程通信中,存在两个进程角色(以最简单的为例):服务器端和客户端

服务器端(Service):

步骤一:新建定义AIDL文件,并声明该服务需要向客户端提供的接口

interface AIDL_Service1 {
    void AIDL_Service();

}
//AIDL中支持以下的数据类型
//1. 基本数据类型
//2. String 和CharSequence
//3. List 和 Map ,List和Map 对象的元素必须是AIDL支持的数据类型;
//4. AIDL自动生成的接口(需要导入-import)
//5. 实现android.os.Parcelable 接口的类(需要导入-import)

步骤2:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCreat、onBind()、blabla)

public class MyService extends Service {

    // 实例化AIDL的Stub类(Binder的子类)
    AIDL_Service1.Stub mBinder = new AIDL_Service1.Stub() {

        //重写接口里定义的方法
        @Override
        public void AIDL_Service() throws RemoteException {
            System.out.println("客户端通过AIDL与远程后台成功通信");
        }
    };


    @Override
    public void onCreate() {
        super.onCreate();

        System.out.println("执行了onCreat()");

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("执行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("执行了onDestory()");
    }

    //在onBind()返回Stub类实例
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {

        System.out.println("执行了onBind()");

        return mBinder;
    }




    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("执行了onUnbind()");
        return super.onUnbind(intent);
    }

}

步骤3:在AndroidMainfest.xml中注册服务 & 声明为远程服务

<service android:name=".MyService" 
    android:process=":remote"
    android:exported="true">    
</service>

客户端(Client):

步骤1:拷贝服务端的AIDL文件到目录下
步骤2:使用Stub.asInterface接口获取服务器的Binder,根据需要调用服务提供的接口方法
步骤3:通过Intent指定服务端的服务名称和所在包,绑定远程Service

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="scut.carson_ho.service_client.MainActivity">


    <Button
        android:layout_centerInParent="true"
        android:id="@+id/bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="绑定服务"
        />
</RelativeLayout>
package scut.carson_ho.service_client;

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

import scut.carson_ho.service_server.AIDL_Service1;


public class MainActivity extends AppCompatActivity {

        private Button bindService;

        //定义aidl接口变量
        private AIDL_Service1 mAIDL_Service;

        //创建ServiceConnection的匿名类
        private ServiceConnection connection = new ServiceConnection() {

            //重写onServiceConnected()方法和onServiceDisconnected()方法
            //在Activity与Service建立关联和解除关联的时候调用
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }

            //在Activity与Service建立关联时调用
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {

                //使用AIDLService1.Stub.asInterface()方法将传入的IBinder对象传换成了mAIDL_Service对象
                mAIDL_Service = AIDL_Service1.Stub.asInterface(service);

                try {

                    //通过该对象调用在MyAIDLService.aidl文件中定义的接口方法,从而实现跨进程通信
                    mAIDL_Service.AIDL_Service();

                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        };


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

            bindService = (Button) findViewById(R.id.bind_service);

            //设置绑定服务的按钮
            bindService.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    System.out.println("点击了[绑定服务]按钮");

                    //通过Intent指定服务端的服务名称和所在包,与远程Service进行绑定
                    //参数与服务器端的action要一致,即"服务器包名.aidl接口文件名"
                    Intent intent = new Intent("scut.carson_ho.service_server.AIDL_Service1");

                    //Android5.0后无法只通过隐式Intent绑定远程Service
                    //需要通过setPackage()方法指定包名
                    intent.setPackage("scut.carson_ho.service_server");

                    //绑定服务,传入intent和ServiceConnection对象
                    bindService(intent, connection, Context.BIND_AUTO_CREATE);

                }
            });
        }

    }

5.使用ContentProvider

        ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式

  • 创建类继承 ContentProvider重写onCreate、query、update、insert、delete、getType(用来返回一个Uri请求所对应的MIME类型【媒体类型】,如果不需要关注类型,返回null就可以)
  • 注册 ContentProvider,指定android.authorities=""(唯一标识)
  • update、insert、delete方法会引起数据源的改变,在这个时候就需要ContentResolver的notifyChange方法来通知外界的数据改变情况,ContentResolver的registerContentObserver方法来注册观察者,通过unregisterContentObserver方法来接触观察者

6.使用Socket

        Socket是网络通信中的概念,他有TCP和UDP协议

        TCP是面向连接的协议,提供稳定的双向通信功能,他建立链接时需要进行三次握手 才能完成,为了提供稳定的数据传输功能,其本身提供了超市重传机制,因此具有很高的稳定性

        UDP是无连接的,提供不稳定的单向通信功能,也可以实现双向通信功能,缺点是不保证数据一定能够正确传输,尤其是在网络拥塞的情况下

选择适合的IPC:

        

猜你喜欢

转载自my.oschina.net/u/3705875/blog/1787299