IPCCommunication making address

Interprocess communication

When it comes to Android interprocess communication, you can certainly think of writing aiDL files, and then using aAPT generated classes to facilitate the completion of the server, as well as the client code.

What is theIPCCommunication

IPCCommunication realizes interprocess communication through two ways. One is handwriting binder, which uses Bunder to communicate with each other and can customize the protocol in Bunder; the other is based on Message, which uses Message to communicate between client and server.

Why write this framework

I once heard a person say, try to use common components or frameworks in the program, so that you can write as little code as possible, which has two advantages, one is less code means fewer mistakes, the other is more time to drink coffee;

use

Handwriting Binder has two ways: 1. Use common IPCService library, including communication server with Activity, the server can be started by Activity or not, if not, you can inherit BaseIPCService. See SimpleService for an implementation

public class MainActivity extends AppCompatActivity {

    private ServiceUtils mServiceUtils;
    private static final String TAG = "jcy_service";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mServiceUtils = new ServiceUtils(this);
    }


    @Override
    protected void onResume(a) {
        super.onResume();
        // Invoke the default IPC service
        mServiceUtils.setConnectListener(mConnectListener);
        mServiceUtils.bindService();
    }


    @Override
    protected void onStop(a) {
        super.onStop();
    }

    @Override
    protected void onDestroy(a) {
        super.onDestroy();
        mServiceUtils.unBindService();
    }

    ConnectListener mConnectListener = new ConnectListener() {

        @Override
        public void onBind(a) {}@Override
        public void onDisconnected(a) {}@Override
        public void onConnected(boolean success) {
            mServiceUtils.setReceiverListener(mReceiverListener);
        }

        @Override
        public void onUnbind(a) {
            mServiceUtils.setReceiverListener(null); }; };private ReceiverListener mReceiverListener = new ReceiverListener() {
        @Override
        public Bundle receiveInfo(int code, Bundle msg) {
            String msgStr = msg.getString("msgStr");
            int msgInt = msg.getInt("msgInt");
            Log.d(TAG, "receiveInfo: msgStr " + msgStr + " ,msgInt : " + msgInt);
            Bundle ret = new Bundle();
            ret.putString("return"."Server returns code" + code + " ,msgStr " + msgStr + " ,msgInt : " + msgInt);
            returnret; }}; }Copy the code

The client

public class MainActivity extends AppCompatActivity  {

    private static final String TAG = "jcy_client_one";
    private TextView mTextView;
    private TextView tv_state;
    private ClientUtils mClientUtils;
    private Button btnGet, btn_bind, btn_unbind;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.tv_service);
        tv_state = (TextView) findViewById(R.id.tv_state);
        btnGet = (Button) findViewById(R.id.btn_getService);
        btn_bind = (Button) findViewById(R.id.btn_bind);
        btn_unbind = (Button) findViewById(R.id.btn_unbind);
        btnGet.setEnabled(false);
        tv_state.setText("Service not bound");
        mClientUtils = new ClientUtils(this);
        mClientUtils.setConnectListener(mConnectListener);
        btn_bind.setEnabled(true);
        btn_unbind.setEnabled(false);
    }

    @Override
    protected void onResume(a) {
        super.onResume();
    }

    @Override
    protected void onDestroy(a) {
        super.onDestroy();
        if(mClientUtils! =null){ mClientUtils.unBindService(); }}/** * get data **@param v
     */
    public void onGetServie(View v) {
        if (mClientUtils.isConnect()) {
            Bundle send = new Bundle();
            send.putString("msgStr"."ClientOneApp sends messages");
            send.putInt("msgInt".110);
            try {
                Bundle ret = mClientUtils.send(110, send);
                if (ret == null) {
                    Log.e(TAG, "onGetServie: ret==null");
                } else {
                    String retStr = ret.getString("return");
                    mTextView.append("\n"+retStr); }}catch (Exception e) {
                e.printStackTrace();
                Log.e(TAG, "onGetServie:Exception "+ e.toString()); }}else {
            Toast.makeText(this."Service not connected, unable to get message", Toast.LENGTH_SHORT).show(); }}/** * Bind service *@param v
     */
    public void onBindServie(View v) {
        mClientUtils.bindService();
    }

    /** * unbind *@param v
     */
    public void onUnBindServie(View v) {
        mClientUtils.unBindService();
    }



    /** * Connection status callback */
    private ConnectListener mConnectListener = new ConnectListener() {
        @Override
        public void onBind(a) {
            tv_state.setText("The service is being bound... ");
        }

        @Override
        public void onDisconnected(a) {
            btnGet.setEnabled(false);
            tv_state.setText("Service disconnected");
            btn_bind.setEnabled(true);
            btn_unbind.setEnabled(false);
        }

        @Override
        public void onConnected(boolean success) {
            if (success) {
                tv_state.setText("Service binding successful");
                btn_bind.setEnabled(false);
                btn_unbind.setEnabled(true);
                btnGet.setEnabled(true);
            } else {
                btnGet.setEnabled(false);
                tv_state.setText("Service binding failed");
                mClientUtils.unBindService();
                btn_bind.setEnabled(true);
                btn_unbind.setEnabled(false); }}@Override
        public void onUnbind(a) {
            btn_bind.setEnabled(true);
            btn_unbind.setEnabled(false);
            tv_state.setText("Service unbound");
            mTextView.setText(""); }}; }Copy the code

The SimpleService class Message is used in much the same way as with custom Binders

Analysis of the

Handwritten Binder





Binder.png

This graph is stealing someone else’s hey hey; The Activity first binds to a remote Service by calling bindService, which returns an IBinder object. At this point, a connection has been established. Once the connection is established, the two parties can communicate via the IBinder they hold. The Activity uses IBinder’s Transact method to send messages to the underlying Binder Driver (Linux layer) and indirectly calls the execTransact method of the underlying IBinder. The result of execTransact is to call the onTransact method. Then the event processing can be carried out in this section. The upper layer mainly uses this method to communicate and process messages;

public boolean onTransact(int code, android.os.Parcel data, 
android.os.Parcel reply, int flags)Copy the code

This method usually returns true and only makes sense if it returns true. If it returns false, the method fails. The onTransact method runs in the Binder thread pool, and usually the client initiates a request. Then the underlying Android code encapsulates the request initiated by the client into three parameters to call the onTransact method. The first parameter code represents the communication flag bit, and data is the method parameter (the data passed by the client). Reply is the method return value (the method that returns data to the client).

Custom Binder is

/** * Description: Custom Binder implementation of message delivery * Autour: @Shenyang * Date: 17-3-24 10:33 am * Mail: [email protected] */
    public class IPCBinder extends Binder {

        /** * process message *@paramCode Indicates the id *@paramData calls the transact object to pass the argument *@paramThe argument * returned by reply from the object calling onTransact@paramFlags Java native methods are blocked by default, set to ibinder. FLAG_ONEWAY when blocking is not required, and 0 * otherwise@return
         * @throws RemoteException
         */
        @Override
        protected boolean onTransact(final int code, final Parcel data, final Parcel reply, final int flags) throws RemoteException {
            data.enforceInterface(Contant.DESCRIPTOR);
            Bundle bundle = dealMessage(code,data.readBundle());
            reply.writeNoException();
            reply.writeBundle(bundle);
            return true;
        }

        /** * Get the current Service to communicate with the Service *@return* /
        public BaseIPCService getService(a){
            return BaseIPCService.this; }}Copy the code

Have not found how to achieve asynchronous communication

The client sends the message

private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub
            Log.d(TAG."--- onServiceDisconnected --- ");
            connect = false;
            if(mConnectListener ! =null) {
                mConnectListener.onDisconnected();
            }
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            if (service == null) {
                connect = false;
                Log.d(TAG."--- onServiceConnected failed service == null ---");
                if(mConnectListener ! =null) {
                    mConnectListener.onConnected(false); }}else {
                Log.d(TAG."--- onServiceConnected sucessed ---");
                connect = true;
                if(mConnectListener ! =null) {
                    mConnectListener.onConnected(true); } mIBinder = service; }}};`Copy the code

After connecting successfully, send messages through Binder objects that can be retrieved using mibinder.transact (code, _data, _reply, 0);

 /** * Send a message to the server *@paramCode Message type *@paramBundle message body *@returnThe return value is the return * from the server@throws Exception
     */
    public Bundle send(int code, Bundle bundle) throws Exception {
        if (mIBinder == null) {
            throw new NullPointerException("IBinder is Null");
        }
        Bundle result = null;
        Parcel _data = Parcel.obtain();
        Parcel _reply = Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            _data.writeBundle(bundle);
            mIBinder.transact(code,
                    _data, _reply, 0);
            _reply.readException();
            result = _reply.readBundle();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        } finally {
            _reply.recycle();
            _data.recycle();
            returnresult; }}Copy the code

The Message communication

The onBind on the server side looks like this:

public IBinder onBind(Intent intent)
    {
        return mMessenger.getBinder(a);
    }Copy the code

So go in:

public IBinder getBinder(a) {
        return mTarget.asBinder(a);
    }Copy the code

You can see mtarget.asbinder () is returned;

Where does mTarget come from?

HandlerThread mHandlerThread = new HandlerThread("BaseMsgIPCService");
            mHandlerThread.start();
            Handler mHandler= new Handler(mHandlerThread.getLooper()){
                @Override
                public void handleMessage(Message msgfromClient)
                {
                    super.handleMessage(msgfromClient); }}; mMessenger=new Messenger(mHandler);

 public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }Copy the code

It turns out that Handler returned it, so let’s go ahead and follow it

    final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if(mMessenger ! =null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl(a);returnmMessenger; }}private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg); }}Copy the code

MTarget is a MessengerImpl object, so asBinder actually returns this, which is a MessengerImpl object; The client first gets the sevice (Ibinder) object through onServiceConnected ected objects. There’s nothing special about it. It’s written like this, but it’s usually written like this: IMessenger. Stub. AsInterface call (service) to the interface object; MService = new Messenger(service); Follow along and you’ll find:

public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); }Copy the code

It’s exactly the way we normally write it! By writing an AIDL file, the server onBind uses the Stub interface to return the interface. The client using the IBinder object obtained from the callback, using IMessenger. Stub. AsInterface (target) to get the call interface examples. (2) Server and client communication so, client and server communication is really nothing special, we can also write a similar AIDL file implementation; So how does the server communicate with the client? Remember that the client send method sends a Message, and that message. replyTo points to an mMessenger that we initialized in our Activity. The Message object is serialized and deserialized when the Message is sent to the server.

# Message
private void readFromParcel(Parcel source) {
        what = source.readInt();
        arg1 = source.readInt();
        arg2 = source.readInt();
        if (source.readInt() ! =0) {
            obj = source.readParcelable(getClass().getClassLoader());
        }
        when = source.readLong();
        data = source.readBundle();
        replyTo = Messenger.readMessengerOrNullFromParcel(source);
        sendingUid = source.readInt();
    }Copy the code

Basically see replyTo, call the Messenger. ReadMessengerOrNullFromParcel

public static Messenger readMessengerOrNullFromParcel(Parcel in) {
        IBinder b = in.readStrongBinder();
        returnb ! =null ? new Messenger(b) : null;
    }

    public static void writeMessengerOrNullToParcel(Messenger messenger,
            Parcel out) {
        out.writeStrongBinder(messenger ! =null ? messenger.mTarget.asBinder()
                : null);
    }Copy the code

Through the writeMessengerOrNullToParcel above you can see, it will be the client’s messenger. MTarget. AsBinder has carried on the back () object, client message. MTarget. AsBinder (what is)? The client is also Messenger created by Handler, so asBinder returns:

public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
 final IMessenger getIMessenger(a) {
        synchronized (mQueue) {
            if(mMessenger ! =null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            returnmMessenger; }}private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg); }}public IBinder getBinder(a) {
        return mTarget.asBinder(a);
    }Copy the code

AsBinder is actually asBinder in MessengerImpl extends IMessenger.Stub.

#IMessenger.Stub

@Override 
public android.os.IBinder asBinder()
{
return this;
}Copy the code

The MessengerImpl object itself is returned. Here you can see that message.mtarget.asbinder () actually returns the client’s MessengerImpl object. Finally, the code sent to the client looks like this:

msgfromClient.replyTo.send(msgToClient);

public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }Copy the code

This mTarget is essentially a wrapper around the client’s MessengerImpl object, so send(message) (with transact/onTransact details masked), This message must eventually be passed to the handler’s handleMessage method on the client side. Message source analysis excerpted from hongyang _ blog

Welcome follow and Star. If you find any bugs, I hope you can contact me in time

IPCCommunication making address