《Android开发艺术探索》--Android中的IPC机制

Android中的多进程

为什么要使用多进程

使用多进程可以避免65535的方法限制,而且由于Android系统对于每个单独的App应用都有内存大小的限制,所以可以通过一个App的多进程方式来增加可用的内存。

开启多进程

在Android中开启多进程很简单,但是当多进程开启之后需要考虑的东西有很多,包括进程间的通讯,进程的创建和销毁的使用场景,以及开启多进程之后各种不可预期的错误

<!--主进程-->
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <!--开启多进程,通过process属性来指定进程名-->
        <activity
            android:name=".Main2Activity"
            android:process=":remote">
        </activity>

开启多进程的弊端

  1. 使用多进程之后首先单例模式和静态成员完全失效

  2. 线程同步机制失效

  3. sharedPreference可靠性下降,因为对XML文件的并发读写会出问题

  4. Application会多次创建

Android中的多进程通讯方式

Android中提供了多种的进程间通讯方式:

  • 使用Bundle

  • 使用共享文件

  • 使用Messenger

  • 使用ContentProvider

  • 使用Socket

  • 使用Binder(AIDL)

使用Bundle

由于Bundle实现了Parcelable接口,所以可以在不同的进程之间传递一些可以包裹化的数据,但是不存在交互性,也就是说,只能在启动另一个进程时将需要传入的数据传过去,只能单向一次传输。

使用文件共享

两个文件可以通过同一个对共享文件的读写来进行数据的交换,这样可以实现交互性,但是由于Android中多个进程间无法对文件的读写进行同步,这样就会导致这种跨进程通讯方式会引发读写错误。

使用Messenger

Messenger是Android实现的一种轻量级的IPC机制,他可以传送一些轻量级的数据Message对象,实际的底层实现是用的Binder,只是对AIDL进行了一些封装,使得更便于使用,而且是串行处理Message对象的,所以不存在并发的问题。

服务端代码

public class MessengerService extends Service {

    private static final String TAG = "MessengerService";
    //声明服务端的Messenger
    private final Messenger mMessenger = new Messenger(new MessageHandler());


    /**
     * 构造Service端用于处理消息的Handler
     */
    private static class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Contast.MSG_FROM_CLIENT:
                    Log.i(TAG, "handleMessage: "+msg.getData().getString(Contast.MSG_CONTENT));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //将其中的Binder返回
        return mMessenger.getBinder();
    }
}

客户端代码

public class Main2Activity extends AppCompatActivity {
    private Messenger mMessenger;

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMessenger = new Messenger(service);
            //构造Message用于发送
            Message message = Message.obtain(null, Contast.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString(Contast.MSG_CONTENT,"这是客户端发来的消息");
            message.setData(data);

            try {
                mMessenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mMessenger = null;
        }
    };



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


    }

    public void serviceStart(View view){
        //启动服务端Service
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        //解除绑定
        unbindService(mServiceConnection);
        super.onDestroy();
    }
}

使用ContentProvider

ContentProvider原本就适合用于进程间通讯,它的底层实现同样是Binder机制。ContentProvider主要定义了一些提供给外部应用访问本应用的一些借口,通过ContentProvider来定义访问接口,通过ContentResolver来进行数据的访问

自定义实现ContentProvider

/**
 * 自定义的ContentProvider,实现其中的增删改查方法
 */
public class PipContentProvider extends ContentProvider {


    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

注册自定义的ContentProvider

<provider
    <!--声明要访问该ContentProvider所需要的权限-->
    android:permission="com.wei.piechart.PROVIDER"
    <!--ContentProvider的唯一标识-->
    android:authorities="com.wei.piechart.PipContentProvider"
    <!--声明要注册的ContentProvider-->
    android:name=".PipContentProvider"
    android:process=":provider"/> 

在程序中通过ContentResolver和Uri来访问指定的ContentProvider

//获取ContentResolver
ContentResolver contentResolver = getContentResolver();
//通过Uri来指定要访问的对象
Uri uri = Uri.parse("content:com.wei.piechart.PipContentProvider");
//类似于SQL数据库一样,就可以进行查询操作,结果放在Cursor结果集中
Cursor cursor = contentResolver.query(uri, null, null, null, null);

使用Socket

Socket套接字本是网络通讯中的一种方式,但是也可以用于本机中的两个进程,通过使用端口号来进行。
Socket分为ServiceSocket和Socket,一个用于服务端,一个用于客户端,和蓝牙通过Socket进行交互的原理是一样的。

使用Binder(AIDL)

准确来说,AIDL并不是一种IPC机制,而是一种语言AIDL(Android Interface Definition Language)Android接口定义语言,通过这种语言我们可以很方便的创建出通过Binder通讯的IPC模型。其实Binder才是Android最为主流的进程间通讯方式,也是前边一些机制的底层实现原理,而AIDL只是使我们有一个便捷的方式来实现Binder机制。

关于Android中的Binder机制可以阅读这篇文章AndroidBinder机制详解

使用限制

类型限制

首先AIDL文件中并不是所有的数据类型都可以使用,可以在AIDL文件中使用的有以下类型:

  • 基本数据类型(int long….)

  • String和CharSequence

  • List:只支持ArrayList,而且List中的元素必须能够被AIDL支持

  • Map:只支持HashMap,而且Map中的元素必须能够被AIDL支持

  • Parcelable:所有实现了Parcelable接口的对象

  • AIDL:所有的AIDL接口本身也可以在AIDL文件中使用

类型声明限制

除了基本类型之外,所有的类型都需要标明方向,使用in表示输入类型参数,使用out表示输出类型参数,使用inout表示输入输出类型参数

对于用到的自定义的Parcelable类型和AIDL类型,都需要显式的import进来

流程

定义AIDL接口

Book.aidl

// Book.aidl
package com.wei.piechart;

// Declare any non-default types here with import statements
parcelable Book;

IBookManager.aidl

// IBookManager.aidl
package com.wei.piechart;

// Declare any non-default types here with import statements
import com.wei.piechart.Book;

interface IBookManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    List<Book> getBookList();
    void addBook(in Book book);
}

Book.java

public class Book implements Parcelable{
    int id;
    String name;

    protected Book(Parcel in) {
        id = in.readInt();
        name = in.readString();
    }

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

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
    }
}

生成IBookManager.java

在定义好接口之后重新Build一下项目,AS会自动生成IBookManager.java这个类。这是AIDL的最主要的功能,通过在aidl文件中定义所需要的字段和方法,可以快速的生成实现了Binder的类。

实现服务端

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

    //在Service端的书籍列表,支持多进程
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    //用于返回给客户端的Binder
    private Binder mBinder = new IBookManager.Stub(){
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

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

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //将服务端创建的Binder返回给客户端
        return mBinder;
    }

}

实现客户端

//声明ServiceConnection
    private ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //在客户端将服务端返回的Binder转化为接口使用
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                bookManager.addBook(new Book(1, "第一本书"));
                List<Book> books = bookManager.getBookList();
                Log.i(TAG, "onServiceConnected: "+books.toString());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };


public void serviceStart(View view) {
        //启动服务端Service
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}

猜你喜欢

转载自blog.csdn.net/wqc_csdn/article/details/54848651