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
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