最近感觉自己越来越菜,感觉自己什么都懂一点点,什么也不会。于是决定静下心来学习学习。所以,从看开发艺术说起。今天重新看了看第二章,感觉有了不一样的收获。那么今天就来记录下。
Messenger,信使。
AIDL(Android Interface definition language),android接口定义语言。实现跨进程通讯的一种方式。
使用Messenger
- 服务端进程
我们需要在服务端创建一个Service来处理客户端的链接请求。同时创建一个Handler来通过他创建Messenger对象,然后在onBind中返回Messenger对象底层的Binder对象。 - 客户端进程
绑定服务端的service,利用服务端返回的IBinder对象创建Messenger,通过这个Messenger对象发送Message类型的消息。如果需要服务端的响应消息,那么只需要利用Hnadler创建一个Messenger对象,并把这个对象通过Message的replyTo参数传递给服务端,服务端就可以通过这个replyTo参数就可以回应客户端。
我们来看个例子:
服务端Service,并将这个service开启到另一个进程
public class MessengerService extends Service {
public static final String TAG = "MessengerService";
public static class MessengerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0: //来自客户端的消息
Log.e(TAG,"receive msg from Client"+msg.getData().getString("msg"));
Messenger client = msg.replyTo;
Message replyMessage = Message.obtain(null,1);
Bundle data = new Bundle();
data.putString("reply","hello ,i can shoudao you message");
replyMessage.setData(data);
try {
client.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
Log.e("tag","bind success");
return mMessenger.getBinder();
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
}
客户端Activity
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = "MessengerActivity";
private Messenger mService;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("tag","can ");
mService = new Messenger(service);
Message msg = Message.obtain(null,0);
Bundle data = new Bundle();
data.putString("msg","hello,this is client");
msg.setData(data);
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
Log.e("tag","send msg errormessage:-->"+e.getMessage());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
private static class MessengerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
Log.e("tag","recive msg from service:"+msg.getData().getString("reply"));
}
}
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
button = (Button) findViewById(R.id.bind);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MessengerActivity.this,MessengerService.class);
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
});
}
@Override
protected void onDestroy() {
unbindService(conn);
super.onDestroy();
}
}
如果还有不懂的,请看张鸿洋 或者看书。
- Messenger以串行的方式处理客户端发来的消息,如果大量消息同时发送到服务器,就不太合适了
- Messenger主要目的是为了传递消息,很多时候我们可能需要调用服务端的方法,这种情况Messenger无法做到,这个时候,我们就需要使用AIDL了。
- -
使用AIDL
- 服务端
创建一个Service用来监听客户端的连接请求,然后创建AIDL文件,将暴漏给客户端的接口在这个文件中声明,最后在service中实现这个AIDL接口 - 客户端
绑定服务端service,将服务端返回的Binder对象转化为AIDL接口所属的类型,接着就可以调用AIDL中的方法了。
那么,接下来我就按照书上的内容来做一个demo。
首先将Book序列化
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(){
}
public Book(int bookId,String bookName){
this.bookId = bookId;
this.bookName = bookName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(bookId);
out.writeString(bookName);
}
public static final Parcelable.Creator<Book> CREATOR = new ClassLoaderCreator<Book>() {
@Override
public Book createFromParcel(Parcel source, ClassLoader loader) {
return new Book(source);
}
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
private Book(Parcel in){
bookId = in.readInt();
bookName = in.readString();
}
@Override
public String toString() {
return String.format("[bookId:%s, bookName:%s]",bookId,bookName);
}
}
接着,创建IBookManager.aidl和Book.aidl,注意Book.aidl创建的时候创建个文件,起名交Book.aidl,若直接在as下创建AIDL文件,会提示以存在。
Book.aidl
package com.gl.android_yishu;
parcelabl
IBookManager.aidl,注意,要将除了一些基础类型以外的其他类型用import导进来
package com.gl.android_yishu;
// Declare any non-default types here with import statements
import com.gl.android_yishu.Book;
import com.gl.android_yishu.IOnNewBookArrivedListener;
interface IBookManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
IOnNewBookArrivedListener.aidl (监听器)
package com.gl.android_yishu;
// Declare any non-default types here with import statements
import com.gl.android_yishu.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book book);
}
将这个aidl文件创建好之后,rebuild,就会在build/source/aidl/debug 下面生成对应的java文件。我在这里不管文件的内容。(我会在后面学习Binder的时候记录)。
接下来看看service的写法。
public class BookManagerService extends Service {
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
private RemoteCallbackList<IOnNewBookArrivedListener> mListener = new
RemoteCallbackList<IOnNewBookArrivedListener>();
private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
private Binder mBinder = new IBookManager.Stub(){
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListener.register(listener);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListener.unregister(listener);
}
};
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
int check = checkCallingOrSelfPermission("com.gl.android_yishu.permission.ACCESS");
if (check == PackageManager.PERMISSION_DENIED){
Log.e("tag","权限认证失败");
}else{
Log.e("tag","onbind---->");
return mBinder;
}
return null;
}
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(0, "Android"));
mBookList.add(new Book(1,"IOS"));
new Thread(new ServiceWorker()).start();
}
private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
int N = mListener.beginBroadcast();
Log.e("tag","onNewBookArrived , notify listeners" +N);
for (int i = 0; i < N; i++) {
IOnNewBookArrivedListener listener = mListener.getBroadcastItem(i);
listener.onNewBookArrived(book);
}
mListener.finishBroadcast();
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestoryed.get()){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size()+1;
Book newBook = new Book(bookId,"new Book#"+bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
代码不多,也不复杂。
- CopyOnWriteArrayList 支持并发读写,这个类不是继承List,但是在Binder中会按照List的规范去访问数据并最后形成一个新的ArrayList传给客户端。所以,即使这个不是List,我们仍然可以使用。
- RemoteCallbackList 是系统专门提供的用户删除跨进程listener的接口。为什么要使用这个,因为对象不能进行跨进程直接传输,跨进程直接传输实际上是反序列化的过程。因此,若不用这个类,客户端和服务器的listener就不是同一个,也就无法unregist。
- 权限认证,我们只希望拥有权限的才能绑定,就可以自定义权限,并在OnBind中进行权限认证,当然,我们自己的应用是需要用uses-permission标签来声明权限的。
- 在使用了RemoteCallbackList之后,我们需要注意,以下面的格式为准
int N = mListener.beginBroadcast();
Log.e("tag","onNewBookArrived , notify listeners" +N);
for (int i = 0; i < N; i++) {
IOnNewBookArrivedListener listener = mListener.getBroadcastItem(i);
listener.onNewBookArrived(book);
}
mListener.finishBroadcast();
以begin开头,以finish结束。
我们来看下客户端的代码
public class MainActivity extends AppCompatActivity {
private Button messenger,aidl;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
Log.e("tag","receive new book" + msg.obj);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private ServiceConnection conn;
{
conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
service.linkToDeath(new IBinder.DeathRecipient() {
@Override
public void binderDied() {
bind();
}
},0);
} catch (RemoteException e) {
e.printStackTrace();
}
try {
List<Book> list = bookManager.getBookList();
Log.e("tag", "query book list,list type :" + list.getClass().getCanonicalName());
Log.e("tag", "query book list :" + list.toString());
bookManager.registerListener(mListener);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
// bind();
}
};
}
private void initView() {
messenger = (Button) findViewById(R.id.messenger);
messenger.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MessengerActivity.class);
startActivity(intent);
}
});
aidl = (Button) findViewById(R.id.AIDL);
aidl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bind();
}
});
}
private void bind() {
Intent intent = new Intent(MainActivity.this,BookManagerService.class);
bindService(intent,conn,Context.BIND_AUTO_CREATE);
}
private IOnNewBookArrivedListener mListener = new IOnNewBookArrivedListener.Stub(){
@Override
public void onNewBookArrived(Book book) throws RemoteException {
handler.obtainMessage(1,book).sendToTarget();
}
};
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onDestroy() {
unbindService(conn);
super.onDestroy();
}
}
没什么特别的。不过,下面一点需要注意:
Binder是可能会意外死亡的。我们可以通过重连来解决。
1.
try {
service.linkToDeath(new IBinder.DeathRecipient() {
@Override
public void binderDied() {
bind();
}
},0);
} catch (RemoteException e) {
e.printStackTrace();
}
bind()函数自己看。
2.在onServiceDisconnected()方法中重连
这2中方法的区别,第一种在客户端的Binder线程池中被毁掉,也就是说我们不能直接访问UI线程,而第二种则是在UI线程中被回调。
还有一点需要注意:客户端调用远程服务的方法,被调用的方法运行在服务端的Binder线程池中 ,同时,客户端线程会被挂起,那么,我们就不能再这个时候进行耗时操作。客户端的onServiceconnected()和onServiceDisconnected()方法都运行在UI线程中,所以我们也不能用来惊醒耗时操作。另外,由于服务端方法运行在Binder线程池中,本身就是耗时的,所以尽量不要开线程去进行异步任务。
最后,还有一种Binder验证的方法,就是在onTranscat()方法中进行认证。关于AIDL和Messenger,就这么多了。