Android 中的IPC机制

2019-03-01  本文已影响1人  bogerLiu

1. 进程和线程

  1. 线程
    1.1 什么是线程呢?
    是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。线程有就绪、阻塞和运行三种基本状态。(网上搜的)

1.2 如何创建新的线程

    private class MyThread extends Thread{
        @Override
        public void run() {
            super.run();
            //run;
        }
    }
    
    MyThread myThread= new MyThread();
    myThread.start();
  class MyThread implements Runnable {

        @Override
        public void run() {

        }
    }

    public void test() {
        Thread t = new Thread(new MyThread());
        t.start();
    }
  Thread t = new Thread();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 10;
            }
        });
        try {
            Integer integer = futureTask.get(); //返回call()返回的值,这里会阻塞,知道线程结束了才会执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        new Thread(futureTask).start();


说明 这里的FutureTask是Java5提供的一种新的线程实现方式,它的特点是有返回值,而且call()方法会抛出异常,FutureTask主要是内部的Callable来实现线程的这里可以在它的源码run()方法中发现

线程创建的方法就是以上四种方式,就先说到这,稍后会专门写一篇文章来总结线程,毕竟本文的重点是Binder。


  1. 进程
    2.1 什么是进程?
    系统正在运行的程序就是一个进程,进程里可以有多个线程。
    2.2 Android中创建进程的方式
    就是四大组件在AndroidManifest文件中指定android:process属性即可。
    例如以下2种方式
 <activity
            android:name=".ui.activity.WebActivity"
            android:process=":process" />
        <activity
            android:name=".ui.activity.WebActivity"
            android:process="com.test.tb.remote" />

第一种 :结尾的 系统会在前面拼接上包名,并且属于当前应用独立进程,第二种则是全局进程,其他应用则可以通过ShareUID以及包名相同的方式跑在同一进程


2.多进程的优缺点

  1. 对于同一个APP内如果所有模块都跑在同一进程则可能导致该进程内存分配过大,容易被系统回收。
    2.多模块进程,可以保证该模块崩溃不会导致主进程崩溃从而导致APP退出。
  1. 静态成员和单例模式失效(多进程会有多个内存模块,放在内存中唯一的效果都会失效)
  2. Application会被创建多次
    3.线程同步失效
    4.SharePreference失效(底层通过读写XML文件实现,对于并发读写可能会出现问题)

3. Android中的多进程数据交互方式

  1. 使用Bundle,通过Intent传递过去
  2. 使用文件共享
  3. 使用Messenger
  4. 使用AIDL
  5. 使用Socket通信
  6. 使用ContentProvider

对于2,5 这里就不做详细介绍了,这个是万能的实现方式。


接下来详细解释每一种实现方式

第1种:使用Intent传递Bundle实现

因为对于Activity,Service,Receiver,都可以通过Intent启动,在启动的时候直接将值传过去。


第3种:使用Messenger信使实现

这里Messenger是Android替我们实现好的AIDL方式,具体的Messenger用法如下

public class ClientActivity extends Activity {
    private Messenger mClient;
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mClient = new Messenger(iBinder);
            Message message = Message.obtain(null, ServerService.TAG_MESSAGE);
            Bundle bundle = new Bundle();
            bundle.putString("key", "value from client");
            message.setData(bundle);
            try {
                mClient.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, ServerService.class);
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }
   public void sendMessage(String str) {
        if (mClient != null) {
            Message message = Message.obtain(null, ServerService.TAG_MESSAGE);
            Bundle bundle = new Bundle();
            bundle.putString("key", str);
            message.setData(bundle);
            try {
                mClient.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            } 
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);
    }
}
//记得把service放在新的进程里
public class ServerService extends Service {

    public static final int TAG_MESSAGE = 1000;
    private Handler mServerHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case TAG_MESSAGE:
                    Bundle data = msg.getData();
                    String key = data.getString("key");
                    Log.e("tag", key);
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }
        }
    };
    private Messenger mMessenger = new Messenger(mServerHandler);

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

以上就是Messenger的使用实例:客户端向服务器发送数据。


这里如果要服务器向客户端发送数据呢?就需要如下

public class ClientActivity extends Activity {
    Messenger mClientMessenger;

    private static class ClientHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case ServerService.TAG_MESSAGE_SERVER:
                    String serverKey = msg.getData().getString("serverKey");
                    Log.e("ServerService", serverKey);
                    break;
            }
        }
    }

    private Messenger mClient;
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mClient = new Messenger(iBinder);
            Message message = Message.obtain(null, ServerService.TAG_MESSAGE_CLIENT);
            Bundle bundle = new Bundle();
            bundle.putString("key", "value from client");
            message.setData(bundle);
            if (mClientMessenger == null) {
                mClientMessenger = new Messenger(new ClientHandler());
                message.replyTo = mClientMessenger; //这里很重要,这里就是将client的Messenger传递给了Server,建立了连接。
            }
            try {
                mClient.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    public void sendMessage(String str) {
        if (mClient != null) {
            Message message = Message.obtain(null, ServerService.TAG_MESSAGE_CLIENT);
            Bundle bundle = new Bundle();
            bundle.putString("key", str);
            message.setData(bundle);
            try {
                mClient.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

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

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

public class ServerService extends Service {

    private static final String TAG = "ServerService";
    public static final int TAG_MESSAGE_CLIENT = 1000;
    public static final int TAG_MESSAGE_SERVER = 1001;

    private static class ServerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case TAG_MESSAGE_CLIENT:
                    Bundle data = msg.getData();
                    String key = data.getString("key");
                    Log.e(TAG, key);
                    Messenger replyTo = msg.replyTo;
                    Message message = Message.obtain(null, TAG_MESSAGE_SERVER);
                    Bundle bundle = new Bundle();
                    bundle.putString("serverKey", "hello, I got message & send you a new message");
                    message.setData(bundle);
                    try {
                        replyTo.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }
        }
    }

    ;
    private Messenger mMessenger = new Messenger(new ServerHandler());

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

以上就是Messenger具体使用,接下来就看看Messenger源码上的实现。


先从服务端开始

Messenger msger = new Messenger(new Handler()); // 1
 public IBinder onBind(Intent intent) { //2
        return mMessenger.getBinder();
    }

这里看下Messenger的源码

  private final IMessenger mTarget;

    /**
     * Create a new Messenger pointing to the given Handler.  Any Message
     * objects sent through this Messenger will appear in the Handler as if
     * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
     * been called directly.
     * 
     * @param target The Handler that will receive sent messages.
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
    public IBinder getBinder() {
        return mTarget.asBinder();
    }
   public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

这里是Handler的源码

  final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

    private final class MessengerImpl extends IMessenger.Stub { //这里可以发现 实现是用AIDL方式
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

根据源码可以知道
步骤1根据Messenger构造函数知道,则是将target初始化,来自Handler中MessengerImpl类,对于当客户端调用Messenger的send()方法就是调用MessengerImpl内的send方法,这里然后在通过Handler的sendMessage将消息回调出来,这里可以看出来 Messenger中的handler还是Handler的本职工作,消息的回调
步骤2这里是对AIDL的返回Binder操作

对于回调,其实就是通过传递Messenger构建连接操作

第4种AIDL

可以移步我的这篇文章aidl


第6种 ContentProvider实现跨进程
  1. 首先是ContentProvider为什么支持跨进程呢?
    ContentProvider:内容提供者,是Android专门为了不同应用间数据共享的一种方式。其实你有没有注意到,平常我们获取系统联系人,相册中的照片等时候 就是一种跨进程获取数据的方式,APP本身是一个进程,联系人,相册是在另个进程中,通过ContentProvider可以获取到。

  2. 自定义ContentProvider实现通信
    ContentProvider主要是以表格方式存储,它暴露出来给我们的实现方法,而底层我们可以通过SQLite数据库,其他别的数据库,XML,文件,哪怕是内存中的类也可以,只要是满足它的格式即可。
    接下来实现一个自己的ContentProvider

public class BookProvider extends ContentProvider {
    private static final String TAG = "BookProvider";
    @Override
    public boolean onCreate() {
        Log.d(TAG, "onCreate() called");
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Log.d(TAG, "query() called ");
        return null;
    }


    @Override
    public String getType(Uri uri) {
        Log.d(TAG, "getType() called ");
        return null;
    }


    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.d(TAG, "insert() called ");
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.d(TAG, "delete() called ");
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        Log.d(TAG, "update() called ");
        return 0;
    }
}

manifest文件生命如下

  <provider
            android:name=".ipc.BookProvider"
            android:authorities="com.test.ipc.authorities"  //这个是唯一标识
            android:process=":providerpro" />

其实以上就完成了一个自定义的Provider,你可以在自己的Provider方法内进行数据存储查询
为了实现数据存储,这里可以用数据进行简单的存储

public class BookSQLite extends SQLiteOpenHelper {
    private static final String DB_NAME = "book_provider.db";
    public static final String BOOK_TABLE_NAME = "book";
    public static final String USER_TABLE_NAME = "user";
    private static final int DB_VERSION = 1;


    static final String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS" 
            + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";
    static final String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS" 
            + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT,sex INT)";

    public BookSQLite(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK_TABLE);
        db.execSQL(CREATE_USER_TABLE);

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

provider改后如下

public class BookProvider extends ContentProvider {
    private static final String TAG = "BookProvider";
    public static final String AUTHORITY = "com.test.ipc.authorities";
    public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
    public static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");

    public static final int BOOK_URI_CODE = 0;
    public static final int USER_URI_CODE = 1;

    private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private SQLiteDatabase mSQLite;
    private Context mContext;

    static {
        sMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE); //这里就将URI与对应的Code绑定,这样方便我们以后对不同的URI进行判断
        sMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
    }

    private String getTBName(Uri uri) {
        String name = null;
        switch (sMatcher.match(uri)) {
            case BOOK_URI_CODE:
                name = BookSQLite.BOOK_TABLE_NAME;
                break;
            case USER_URI_CODE:
                name = BookSQLite.USER_TABLE_NAME;
                break;
            default:
                break;

        }
        return name;
    }

    @Override
    public boolean onCreate() {
        Log.d(TAG, "onCreate() called");
        mContext = getContext();
        mSQLite = new BookSQLite(getContext()).getWritableDatabase();
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Log.d(TAG, "query() called ");
        String name = getTBName(uri);
        if (name == null) {
            return null;
        }
        return mSQLite.query(name, projection, selection, selectionArgs, null, null, sortOrder, null);

    }


    @Override
    public String getType(Uri uri) {
        Log.d(TAG, "getType() called ");
        return null;
    }


    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.d(TAG, "insert() called ");
        String name = getTBName(uri);
        if (name == null) {
            return null;
        }
        mSQLite.insert(name, null, values);
        mContext.getContentResolver().notifyChange(uri, null);
        return uri;

    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.d(TAG, "delete() called ");
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        Log.d(TAG, "update() called ");
        return 0;
    }
}

这样就是实现了数据的存储与查找

以上就是Android中的IPC机制实现

总结

这篇文章主要是一个总章,对进程与线程进行一个总的介绍,稍后会逐一介绍线程,Binder的具体实现细节,ContentProvider的底层实现。

参考《Android开发艺术探索》&Android源码

上一篇下一篇

猜你喜欢

热点阅读