preface
It is actually a very small knowledge, the majority of people in the process of use AIDL also basic didn’t appear because of the mistakes, because it is small, so in most of the Internet on AIDL article, it have neglected – or not, but very small occupied space, and basically all is the official document translation, the translator readers are actually not much. These days I am studying AIDL, but I am a stubborn person. When I encounter something unclear, I just want to make it clear, so I made some efforts to study the directional tag in AIDL and its in, out and inout.
This blog post is organized.
The body of the
1, an overview of the
In, out, and inout are three directed tags. Therefore, readers should have a certain understanding of AIDL in Android. Learn AIDL, this article is enough (part 1). In addition, this article can basically be said to be my psychological process of studying this thing, which may be a little rambling, please forgive me.
2. Official documents
The Android website describes the tag orientation as follows:
All non-primitive parameters require a directional tag indicating which way the data goes . Either in , out , or inout . Primitives are in by default , and connot be otherwise .
All non-basic parameters need a directional tag to indicate how the data flows, whether in, out, or inout. The orientation tag for the base parameter is in by default and only. I have some questions in my mind about this orientation tag. First, what is the way data flows? Secondly, what do in, out and inout represent respectively, and what are the differences between them? Clearly, official documents do not spell this out. Then I’m just going to have to figure it out for myself.
In the spirit of not reinventing the wheel, I first looked it up on Google to see if I could come up with a good answer, and I did find some answers — but all of them seemed to have some holes. So I had no choice but to set out to find out what was going on.
3. Start your research
3.1. Input/output? NO!
The first guess that came to mind was that in was the input, which was the method’s argument, and out was the output, which was the method’s return value. It’s a reasonable guess, but it’s not a reasonable guess. Here’s why:
- As stated in the documentation, the orientation tag of the base parameter is in by default and only in, but obviously, the base parameter can be either a method pass or a method return value, so this guess itself is not valid.
- If in represents input and out represents output, what should inout represent?
- After testing, the directional tag can only be used to modify the input parameter of AIDL method, and cannot modify its return value.
Comprehensive consideration of the above points, basic can rule out the possibility of this guess.
3.2, the way? Way!
After eliminating the above ideas, I began to further guess what in, out, inout might mean — at one point I had an epithet: Besides input and output, in, out is always used to represent the flow of data! At the same time, IT occurred to me that I might have misinterpreted the official document: “way” means method, but it also means way! All non-basic parameters need a directional tag to indicate where the data is going, whether it’s in, out, or inout. The orientation tag for the base parameter is in by default and only.
In and out represent two one-way data flows between the client and server, respectively, while inout represents two-way data flows between the two ends. Based on this guess, I designed an experiment to test it, and the AIDL file looks like this:
package com.lypeer.ipcclient;
parcelable Book;Copy the code
package com.lypeer.ipcclient;
import com.lypeer.ipcclient.Book;
interface BookManager {
List getBooks();
Book addBookIn(in Book book);
Book addBookOut(out Book book);
Book addBookInout(inout Book book);
}Copy the code
Android: Learning AIDL, this article is enough (on), I will not repeat here. I’m going to focus on the idea of the experiment. As you can see, the three methods that have a positioning tag all pass arguments and return values to Book objects (Book is a class that IMPLEMENTS Parcelable and contains only two arguments, String name and int price), and they all have different positioning tags. I then verify my guess by calling these three methods on the client side and printing information on the client side and the server side respectively. Paste the client and server code below:
/** * Aidlactivity. Java * Because the test machine is too many useless debug messages, E * * Created by lypeer on 2016/7/17. */ public class AIDLActivity extends appactivity {private BookManager mBookManager = null; private boolean mBound = false; private List mBooks; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_aidl); ** @param view */ public void addBookIn(view view) {if (! mBound) { attemptToBindService(); Toast.maketext (this, toast.length_short).show(); toast.maketext (this, toast.length_short).show(); return; } if (mBookManager == null) return; Book book = new Book(); Book.setname ("APP r&d record In"); book.setPrice(30); try { Book returnBook = mBookManager.addBookIn(book); Log.e(getLocalClassName(), returnBook.toString()); } catch (RemoteException e) { e.printStackTrace(); } } public void addBookOut(View view) { if (! mBound) { attemptToBindService(); Toast.maketext (this, toast.length_short).show(); toast.maketext (this, toast.length_short).show(); return; } if (mBookManager == null) return; Book book = new Book(); Book.setname ("APP r&d record Out"); book.setPrice(30); try { Book returnBook = mBookManager.addBookOut(book); Log.e(getLocalClassName(), returnBook.toString()); } catch (RemoteException e) { e.printStackTrace(); } } public void addBookInout(View view) { if (! mBound) { attemptToBindService(); Toast.maketext (this, toast.length_short).show(); toast.maketext (this, toast.length_short).show(); return; } if (mBookManager == null) return; Book book = new Book(); Book.setname ("APP r&d record Inout"); book.setPrice(30); try { Book returnBook = mBookManager.addBookInout(book); Log.e(getLocalClassName(), returnBook.toString()); } catch (RemoteException e) { e.printStackTrace(); AttemptToBindService () {Intent Intent = new Intent(); intent.setAction("com.lypeer.aidl"); intent.setPackage("com.lypeer.ipcserver"); bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStart() { super.onStart(); if (! mBound) { attemptToBindService(); } } @Override protected void onStop() { super.onStop(); if (mBound) { unbindService(mServiceConnection); mBound = false; } } private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e(getLocalClassName(), "service connected"); mBookManager = BookManager.Stub.asInterface(service); mBound = true; if (mBookManager ! = null) { try { mBooks = mBookManager.getBooks(); Log.e(getLocalClassName(), mBooks.toString()); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected(ComponentName name) { Log.e(getLocalClassName(), "service disconnected"); mBound = false; }}; }Copy the code
The idea on the client side is pretty clear. Anyway, connect to the server first, then call the three methods defined in AIDL separately, and then observe the change in the return value. Next paste the server:
Java ** Created by lypeer on 2016/7/17. */ public class AIDLService extends Service {public final String TAG = this.getClass().getSimpleName(); private List mBooks = new ArrayList<>(); private final BookManager.Stub mBookManager = new BookManager.Stub() { @Override public List getBooks() throws RemoteException { synchronized (this) { Log.e(TAG, "invoking getBooks() method , now the list is : " + mBooks.toString()); if (mBooks ! = null) { return mBooks; } return new ArrayList<>(); } } @Override public Book addBookIn(Book book) throws RemoteException { synchronized (this) { if (mBooks == null) { mBooks = new ArrayList<>(); } if(book == null){ Log.e(TAG , "Book is null in In"); book = new Book(); } book.setPrice(2333); if (! mBooks.contains(book)) { mBooks.add(book); } Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString()); return book; } } @Override public Book addBookOut(Book book) throws RemoteException { synchronized (this) { if (mBooks == null) { mBooks = new ArrayList<>(); } if(book == null){ Log.e(TAG , "Book is null in Out"); book = new Book(); } book.setPrice(2333); if (! mBooks.contains(book)) { mBooks.add(book); } Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString()); return book; } } @Override public Book addBookInout(Book book) throws RemoteException { synchronized (this) { if (mBooks == null) { mBooks = new ArrayList<>(); } if(book == null){ Log.e(TAG , "Book is null in Inout"); book = new Book(); } book.setPrice(2333); if (! mBooks.contains(book)) { mBooks.add(book); } Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString()); return book; }}}; @Override public void onCreate() { Book book = new Book(); Book.setname ("Android Development Art Exploration "); book.setPrice(28); mBooks.add(book); super.onCreate(); } @Nullable @Override public IBinder onBind(Intent intent) { Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString())); return mBookManager; }}Copy the code
Again, the thread is clear: first accept the client connection request and pass back the bookManager.stub IBinder interface that the server handled to the client. The method implemented in bookManager.stub basically receives the Book object from the client, attempts to modify it, and then passes the modified object back.
Through such experiments, we can monitor the flow of data from the client to the server and the flow of data from the server to the client. Based on this data, we can verify the above guesses. According to our guess, in, out, inout actually marks the flow direction of data, so the data marked with in or out can only be one-way transmission, and the reverse is invalid, while the data inout can be two-way transmission. Does the resulting data show such characteristics?
First, install the two apps on the mobile phone, and then open them, and execute addBookIn(), addBookOut(), addBookInout() click events successively. Finally, the log information at both ends is as follows:
1, on bind,intent = intent {act=com.lypeer. Aidl PKG =com.lypeer. Invoking getBooks() method, now the list is: name, invoking getBooks() method, now the list is: 29] 3, Intent = intent {act=com.lypeer. Aidl PKG =com.lypeer. Invoking getBooks() method, now the list is: name, invoking getBooks() method, now the list is: Invoking addBooks() method, now the list is: Invoking addBooks() method, now the list is: Invoking addBooks() method, now the list is: [name: Android, price: null, invoking addBooks() method, now the list is: [name: Android, price: 28, name: APP Inout, price: 2333, name: null, price: 2333, name: APP Inout, price: 2333]Copy the code
As you can see, the log information on the server is basically as expected. The first four lines are bound to the server by the client. The element in the list is initialized and can be left alone. The next three lines show the data received by the server when the client calls addBookIn(), addBookOut(), and addBookInout() methods: In and out tag methods, the server can normally receive data from the client, but inout tag methods, the server receives an empty Book object! All three methods pass a Book object with the same parameters. Based on the data of the server and the previous guesses, we can basically confirm that the previous guesses are correct. In, as a directional tag, means that data can only flow from the client to the server, while out, on the contrary, means that data can flow in both directions. If this is the case, then we can make some guesses about the log information on the client side: since in means that data can only flow from the client to the server, the return value of the client should be an empty Book object; Out and inout should return the same value on the server as they do on the tag. So is that true? Have a look at:
1, Service Connected 2, [name: Android development art exploration, price: 28] 3, Service Connected 4, [name: Android development art exploration, price: 28] 28] 5, price: 2333 6, name: null, price: 2333 7, name: 2333 Inout, price: 2333Copy the code
Again, the first four lines are the connection information, and the next three lines are the data returned by the server when the addBookIn(), addBookOut(), addBookInout() methods are called. The result is that all three rows are identical to the data on the server! If there is something wrong with the data, then clearly there must be something wrong with the conjecture.
3.3, Data flow!
So let’s think about this one more time. First of all, the data from the server does not flow from the client to the server by using out as a way to target the tag – the server receives an empty object! This shows that the directional tag is related to the direction of the data flow. So what’s the problem? It comes down to how you think about the flow of data. Before, I thought of the data flow simply as the parameter input and return value output of the method. Now I think there are some problems:
- If the flow of data from the server to the client is thought of as a method that returns the value back to the client, why not design out to be written before the return value? Why do you need a useless input? The people who designed the language were so sick of it that they couldn’t possibly have thought of that, could they?
- The default type of directional tags in AIDL can only be in by default. Should they be entered as arguments and not returned as values?
So, the question should be in the way of return value as data from the server to the client on this matter, though in a sense method return values or as a data from the server to the client, but is not what we here say data from the server to the client, although a bit around, but saw the reader should be able to see what I mean. So how do you understand the flow of data from the server to the client? Now that the return value of the method has been rejected, what is the carrier to which the data flows back – anyway, the data always has a carrier, otherwise how do we know it has returned? Since the carrier is not an object returned by the method, it must be an object that existed before the method was called. Although it may feel strange: my object is on the client side and the method implementation is on the server side, how can it change the object? But since it’s a derivation, let’s do an experiment and see if it’s clear.
To verify this, log the server’s return message directly from the client, and change the original output returnbook.tostring () to book.tostring () (book object is the method argument). The code is not attached. Only a little change. If the above guess is correct, then the output should be the same as the book object parameters on the server side when in is a tag oriented method, and the book object parameters on the server side when out is a tag oriented method. Let’s look at the actual log value:
1, service Connected 2, [name: Android dev art, price: 28] 3, name: APP dev record, price: 30 4, name: null, price: 2333 5, Name: APP R&d record Inout, Price: 2333Copy the code
Again, the last three lines represent the arguments to the book object after calling the addBookIn(), addBookOut(), and addBookInout() methods. As you can see, the output log information is finally the same as I expected! This shows that the previous guess is correct!
3.4. Conclusion
This basically leads to the conclusion that the directional tag in AIDL represents the flow of data in cross-process communication, where IN indicates that data can only flow from client to server, out indicates that data can only flow from server to client, and inout indicates that data can flow in both directions between server and client. Where the data flow is for the object passing the method in the client. In indicates that the server will receive the complete data of the object, but the object on the client will not change as the server changes the parameter. Out indicates that the server will receive the empty object of that object, but the client will synchronize the change after the server has made any changes to the received empty object. When inout is tag oriented, the server will receive complete information about the object from the client, and the client will synchronize any changes the server makes to the object.
4. Source code analysis
Above we guess through analysis, design experiments and so on to get a conclusion, so next we will conduct source analysis, to see if in theory can provide proof for our conclusion. First I found the bookManager.java file generated by as from the bookManager.aidl file and extracted the relevant code snippet from it:
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getBooks: { data.enforceInterface(DESCRIPTOR); java.util.List _result = this.getBooks(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBookIn: { data.enforceInterface(DESCRIPTOR); com.lypeer.ipcclient.Book _arg0; if ((0 ! = data.readInt())) { _arg0 = com.lypeer.ipcclient.Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBookIn(_arg0); reply.writeNoException(); return true; } case TRANSACTION_addBookOut: { data.enforceInterface(DESCRIPTOR); com.lypeer.ipcclient.Book _arg0; _arg0 = new com.lypeer.ipcclient.Book(); this.addBookOut(_arg0); reply.writeNoException(); if ((_arg0 ! = null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } case TRANSACTION_addBookInout: { data.enforceInterface(DESCRIPTOR); com.lypeer.ipcclient.Book _arg0; if ((0 ! = data.readInt())) { _arg0 = com.lypeer.ipcclient.Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBookInout(_arg0); reply.writeNoException(); if ((_arg0 ! = null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.lypeer.ipcclient.BookManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public java.util.List getBooks() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.lypeer.ipcclient.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBookIn(com.lypeer.ipcclient.Book book) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((book ! = null)) { _data.writeInt(1); book.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addBookIn, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public void addBookOut(com.lypeer.ipcclient.Book book) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_addBookOut, _data, _reply, 0); _reply.readException(); if ((0 ! = _reply.readInt())) { book.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } @Override public void addBookInout(com.lypeer.ipcclient.Book book) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((book ! = null)) { _data.writeInt(1); book.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addBookInout, _data, _reply, 0); _reply.readException(); if ((0 ! = _reply.readInt())) { book.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); }}}Copy the code
In the Java files generated by the AIDL file, the basic call sequence for remote calls is to call the relevant methods from the Proxy class, and then call transact() in those methods, at which point the onTransact() method in the Stub will be called. The specific business logic methods are then called from within this method — of course, there is always some writing or reading of data during these method calls, because these are cross-thread operations and the data must be serialized and transferred. It is best for the reader to follow the line of method calls when reading the code and comments, so that the overall flow of data is much clearer and easier to read.
Through the analysis of the source code, we can easily come to the same conclusion as the previous analysis, so that basically what is the AIDL directional tag, in, out, inout they represent what, what are the differences between these problems, also solved.
conclusion
The directional tag in AIDL represents the flow of data in cross-process communication, where IN indicates that data can only flow from client to server, out indicates that data can only flow from server to client, and inout indicates that data can flow in both directions between server and client. Where the data flow is for the object passing the method in the client. In indicates that the server will receive the complete data of the object, but the object on the client will not change as the server changes the parameter. Out indicates that the server will receive the empty object of that object, but the client will synchronize the change after the server has made any changes to the received empty object. When inout is tag oriented, the server will receive complete information about the object from the client, and the client will synchronize any changes the server makes to the object. (that’s right, it’s copied from the top and sticky 🙂
Finally, two more questions.
One is that maybe some readers don’t understand, why not just look at the source code? That must be the right conclusion! That’s true. But on the other hand, after a conclusion through studying to go to the source code, and directly go to the source code, the difficulty is not the same – to see the source code from the start, may not understand, which is read, are not necessarily the right conclusion – this sounds odd, but those who often watch of students should be have the same source. The source code, on the other hand, is a finished product, and if we look at it we may be able to draw conclusions, but it’s hard to get a sense of the author’s journey through the design of this thing, the journey of choosing between different options and choosing the best one — without feeling that, So I think maybe we need to spend a little more time on this thing to calm down and study it.
Moreover, what this article actually wants to present is the attitude of exploring technology. This point is only a small point, but I found many articles, many websites did not find a good answer, this is why? Because people don’t really settle down to study it. Given time, everyone could have come up with the same answer, but most people saw it in a hurry and ignored it in a hurry — or looked it up at random, as if what everyone on the Internet was saying made sense, and that’s it. It doesn’t make any sense.
The relevant code in this article can be downloaded by clicking portal.
Thank you.