Android进程间通信(2)-纯代码,非AIDL

来源:互联网 发布:apache storm 教程 编辑:程序博客网 时间:2024/06/11 15:44

上一篇文章中我们已经实现了进程间的通信,但是文章最后留了一个问题,就是实现进程间通信是不是只用通过aidl?答案是no,这篇文章我就带大家通过纯代码的方法实现以下进程间的通信。

1,服务端,我们新建一个Service,取名为MyCodeService.java,代码如下:

package com.wms.github.aidl.server;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.os.Parcel;import android.os.RemoteException;import android.util.Log;/** * Created by 王梦思 on 2017/5/25. */public class MyCodeService extends Service {    private static final String TAG = "MyService";    @Override    public IBinder onBind(Intent intent) {        Log.e(TAG, "onBind...");        //这里不能返回null,必须要返回我们创建的Binder对象        return new MyBinder();    }    @Override    public boolean onUnbind(Intent intent) {        Log.e(TAG, "onUnbind...");        return super.onUnbind(intent);    }    @Override    public void onStart(Intent intent, int startId) {        Log.e(TAG, "onStart...");        super.onStart(intent, startId);    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.e(TAG, "onStartCommand...");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onCreate() {        Log.e(TAG, "onCreate...");        super.onCreate();    }    @Override    public void onDestroy() {        Log.e(TAG, "onDestroy...");        super.onDestroy();    }    /**     * 这里一定要继承自String2UpperCase.Stub     */    class MyBinder extends Binder {        private static final String DESCRIPTOR = "com.wms.MyBinder";        @Override        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {            //code是和操作对应,这个code和客户端code对应            switch (code) {                case 0x001:                    data.enforceInterface(DESCRIPTOR);                    java.lang.String _arg0;                    _arg0 = data.readString();                    java.lang.String _result = _arg0.toUpperCase();                    reply.writeNoException();                    reply.writeString(_result);                    return true;            }            return super.onTransact(code, data, reply, flags);        }    }}

在MyCodeService中我们将MyBinder直接继承自Binder对象,aidl中式继承自Android Studio自动生成的java文件。在MyBinder里面,我们重写了onTransact方法,这个方法是在客户端发送请求的时候会调用transact方法,而transact方法内部代码如下:

/**     * Default implementation rewinds the parcels and calls onTransact.  On     * the remote side, transact calls into the binder to do the IPC.     */    public final boolean transact(int code, Parcel data, Parcel reply,            int flags) throws RemoteException {        if (false) Log.v("Binder", "Transact: " + code + " to " + this);        if (data != null) {            data.setDataPosition(0);        }        boolean r = onTransact(code, data, reply, flags);        if (reply != null) {            reply.setDataPosition(0);        }        return r;    }

在transact内部回调了onTransact方法,所以在客户端发送消息后,就会调用到MyCodeService中的 onTransact方法,处理完数据后直接返回给客户端。

注意别忘记了在AndroidManifest.xml文件中注册MyCodeService.java

<service            android:name="com.wms.github.aidl.server.MyCodeService"            android:exported="true">            <intent-filter>                <action android:name="com.wms.github.aidl.server.MyCodeService"/>                <category android:name="android.intent.category.DEFAULT"/>            </intent-filter>        </service>

这样服务端的代码就完成了,其实很简单。

2,客户端,新建一个CodeActivity,代码如下:

package com.wms.github.aidl.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.util.Log;import android.view.View;import android.widget.EditText;/** * Created by 王梦思 on 2017/5/25. */public class CodeActivity extends MainActivity {    private EditText mEditText;    private IBinder mBinder;    private static final String DESCRIPTOR = "com.wms.MyBinder";    private ServiceConnection mServiceConn = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            //当绑定成功后调用            mBinder = service;            Log.e("MainActivity", "onServiceConnected...");        }        @Override        public void onServiceDisconnected(ComponentName name) {            mBinder = null;            Log.e("MainActivity", "onServiceDisconnected...");        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mEditText = (EditText) findViewById(R.id.id_edittext);        findViewById(R.id.bind).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                bindService();            }        });        findViewById(R.id.unbind).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                unBindService();            }        });        findViewById(R.id.invokeServer).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                invokeServer();            }        });    }    /**     * 绑定服务     */    public void bindService() {        Intent intent = new Intent();        intent.setAction("com.wms.github.aidl.server.MyCodeService");        //Android 5.0以上必须要加这句代码,不然报错        intent.setPackage("com.wms.github.aidl.server");        bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);    }    public void unBindService() {        unbindService(mServiceConn);    }    public void invokeServer() {        String inputStr = mEditText.getText().toString().trim();        try {            android.os.Parcel _data = android.os.Parcel.obtain();            android.os.Parcel _reply = android.os.Parcel.obtain();            java.lang.String result = "";            try {                _data.writeInterfaceToken(DESCRIPTOR);                _data.writeString(inputStr);                mBinder.transact(0x001, _data, _reply, 0);                _reply.readException();                result = _reply.readString();            } finally {                _reply.recycle();                _data.recycle();            }            //重新显示到Edittext            mEditText.setText(result);        } catch (RemoteException e) {            //这里会抛出远程异常            e.printStackTrace();        }    }}

这里需要注意的是,在onServiceConnected中,aidl实现进程通信的时候是调用Stub内部方法asInterface来生成一个String2Uppercase对象的,这里没有这么做,因为我们没有String2Uppercase类,这里直接在CodeActivity中保存onServiceConnected回调回来的ibinder,大家可能会疑问这个ibinder是什么呢?其实就是我们上一篇文章中所说的binder驱动,不明白的可以去看上一篇文章。

当点击调用服务端转换按钮时,会执行如下代码

String inputStr = mEditText.getText().toString().trim();        try {            android.os.Parcel _data = android.os.Parcel.obtain();            android.os.Parcel _reply = android.os.Parcel.obtain();            java.lang.String result = "";            try {                _data.writeInterfaceToken(DESCRIPTOR);                _data.writeString(inputStr);                mBinder.transact(0x001, _data, _reply, 0);                _reply.readException();                result = _reply.readString();            } finally {                _reply.recycle();                _data.recycle();            }            //重新显示到Edittext            mEditText.setText(result);        } catch (RemoteException e) {            //这里会抛出远程异常            e.printStackTrace();        }

关键的代码就是mBinder.transact(0x001, _data, _reply, 0);
这行代码其实就是让Binder驱动传送数据给服务端,其实内部和aidl实现一模一样,只是这些操作aidl利用apt工具自动给我们生成了而已。大家可以去查看下aidl文件生成的java文件就明白了。

通过本文的分析,大家是不是更加清晰了aidl的机制呢?也解决了我们文章开头的问题,解决进程通信没必要非要写aidl,aidl只是一个辅助我们更简单实现通信的。其实android系统中很多地方都用到了代码的方式来获取系统服务。这里我就不展开说了,如果有兴趣可以去查看framework代码。

代码传送门 : http:github.com/wms1993/blog_aidl_demo

原创粉丝点击