IPC是Inter-Process Communication的缩写,意思是进程间通信(如果不清楚什么是进程,请左转问度娘后在回转来看)。Android中IPC的方式有很多种,今天先说最灵活也是最常用的一种,即AIDL(Android Interface Definition Language)方式。

创建多进程的方式有两种,一,给四大组件指定android:process属性,二,通过JNI在native层fork一个新的进程。今天Demo以第一种为例,为了方便查看此Demo没有写两个工程,而是在同一个应用内给SecondActivity指定

android:process="com.dyk.aidltest.SecondActivity" 
这样就相当于SecondActivity是另一个应用的Activity。下面开始讲解Demo。

Demo下载

工程大纲:

首先新建一个实现了Parcelable的Book类,用作测试Bean。

package com.example.aidl;import android.os.Parcel;import android.os.Parcelable;public class Book implements Parcelable{public int bookId;public String bookName;public Book(int bookId, String bookName) {super();this.bookId = bookId;this.bookName = bookName;}@Overridepublic int describeContents() {return 0;}/**序列化*/@Overridepublic void writeToParcel(Parcel out, int flags) {out.writeInt(bookId);out.writeString(bookName);}private Book(Parcel source){bookId = source.readInt();bookName = source.readString();}/**反序列化*/public static final Creator<Book> CREATOR = new Creator<Book>() {@Overridepublic Book createFromParcel(Parcel source) {return new Book(source);}@Overridepublic Book[] newArray(int size) {return new Book[size];}};}

接着新建需要的aidl文件

package com.example.aidl;parcelable Book;

package com.example.aidl;import com.example.aidl.Book;interface IBookManager{List<Book> getBookList();void addBook(in Book book);}

注意:Book必须实现Parcelable接口(序列化,反序列化),参数in代表输入型参数,out代表输出型参数,inout代表输入输出型参数,否则aidl不支持。就算Book和aidl文件在同一包下,也需要导包。如果AIDL用到了自定义的parcelable对象,则必须建立一个和它同名的aidl文件,并在其中声明它为parcelable类型。AIDL接口中只支持方法,不支持静态常量,这一点区别于传统的接口。

新建上述文件夹后可以在gen/package name目录下可以查找到系统自动为我们生成的IBookManager。乍一看这个类有点乱,format后可以发现这个类并没有想象的那么复杂。在此简单分析下这个类,不感兴趣的可以略过这一阶段,直接进入下一分割线。

--------------------------------------------------------------华丽丽的分割线--------------------------------------------

首先这个IBookManager继承了IInterface接口,里面有一个抽象类Stub和两个抽象方法。

public static abstract class Stub extends android.os.Binder implements com.example.aidl.IBookManager

<span style="white-space:pre"></span>public java.util.List<com.example.aidl.Book> getBookList() throws android.os.RemoteException;public void addBook(com.example.aidl.Book book) throws android.os.RemoteException;
S tub里面有几个比较重要的方法:asInterface,asBinder,onTransact和一个代理类
private static class Proxy implements com.example.aidl.IBookManager 
asInterface:将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种对象区分进程,若在统一进程,返回服务端的Stub本身,否则返回封装后的Stub.proxy对象。

if (((iin != null) && (iin instanceof com.example.aidl.IBookManager))) {return ((com.example.aidl.IBookManager) iin);}return new com.example.aidl.IBookManager.Stub.Proxy(obj);
asBinder:返回当前的Binder对象

onTransact:服务端通过参数code确定客户端请求的目标方法,接着从data中取出目标方法所需的参数,然后执行目标方法,最后向reply中写入返回值。

<span style="white-space:pre"></span>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_getBookList: {data.enforceInterface(DESCRIPTOR);java.util.List<com.example.aidl.Book> _result = this.getBookList();reply.writeNoException();reply.writeTypedList(_result);return true;}case TRANSACTION_addBook: {data.enforceInterface(DESCRIPTOR);com.example.aidl.Book _arg0;if ((0 != data.readInt())) {_arg0 = com.example.aidl.Book.CREATOR.createFromParcel(data);} else {_arg0 = null;}this.addBook(_arg0);reply.writeNoException();return true;}case TRANSACTION_registerListener: {data.enforceInterface(DESCRIPTOR);com.example.aidl.IOnNewBookArrivedListener _arg0;_arg0 = com.example.aidl.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());this.registerListener(_arg0);reply.writeNoException();return true;}case TRANSACTION_unregisterListener: {data.enforceInterface(DESCRIPTOR);com.example.aidl.IOnNewBookArrivedListener _arg0;_arg0 = com.example.aidl.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());this.unregisterListener(_arg0);reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}

-------------------------------------------------------------------华丽丽的分割线-------------------------------------------------------------------------

接下来就可以写我们的服务端了,这里使用一个service充当服务端。先上代码

package com.example.service;import java.util.List;import java.util.concurrent.CopyOnWriteArrayList;import android.app.Service;import android.content.Intent;import android.content.pm.PackageManager;import android.os.IBinder;import android.os.RemoteCallbackList;import android.os.RemoteException;import android.util.Log;import com.example.aidl.Book;import com.example.aidl.IBookManager.Stub;import com.example.aidl.IOnNewBookArrivedListener;public class MyService extends Service {public static final String TAG = "MyService";// CopyWriteArrayList支持并发读/写// AIDL方法是在服务端的Binder线程池中执行的,// 因此当多个客户端同时连接时,会存在多个线程同时访问的情形// 类似的还有ConcurrentHashMap/** 书籍列表 */private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();/** Stub继承Binder类实现了IBookManager接口 */public class MyServiceImpl extends Stub {@Overridepublic List<Book> getBookList() throws RemoteException {for (int i = 0; i < 10; i++) {Book mBook = new Book(i, "平凡的世界");mBookList.add(mBook);}return mBookList;}@Overridepublic void addBook(Book book) throws RemoteException {mBookList.add(book);@Overridepublic IBinder onBind(Intent intent) {return new MyServiceImpl();}}
在onBind中返回继承Stub的内部类MyServiceImpl。上文也提到过Stub继承Binder类实现了IBookManager接口并且本身是一个抽象类。所以非抽象子类需要重写getBookList()和addBook(Book book)方法。

服务器端暂时到此结束,接下来就到客户端了。

package com.example.aidltest;import com.example.aidl.Book;import com.example.aidl.IBookManager;import com.example.aidl.IOnNewBookArrivedListener;import android.app.Activity;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.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends Activity implements OnClickListener {public static final String TAG = "MainActivity";private Button bindService;private Button startSecondActivity;private TextView tvShow;private IBookManager mIBookManager;private ServiceConnection mServiceConnection = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {//<span style="font-family: Arial, Helvetica, sans-serif;">onServiceDisconnected</span><span style="font-family: Arial, Helvetica, sans-serif;">和onServiceConnected方法都在UI线程中运行</span>//所以不要在它们里面直接调用服务器端的耗时方法}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// service就是com.example.service.MyService$MyServiceImpl// Log.i(TAG,TAG+"******"+service.getClass().getName());// 面向接口编程mIBookManager = IBookManager.Stub.asInterface(service);//为了避免ANR下面的代码最好新开线程中运行,这里为了方便就不麻烦了try {// 父类调用子类方法tvShow.setText(mIBookManager.getBookList().toString());Book newBook = new Book(888,"监听测试***书籍");mIBookManager.addBook(newBook);} catch (RemoteException e) {e.printStackTrace();}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();}@Overrideprotected void onDestroy() {//解除绑定unbindService(mServiceConnection);super.onDestroy();}private void initView() {bindService = (Button) findViewById(R.id.bindService);startSecondActivity = (Button) findViewById(R.id.startSecondActivity);tvShow = (TextView) findViewById(R.id.tvShow);bindService.setOnClickListener(this);startSecondActivity.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.bindService:Intent intent = new Intent("com.example.service.MyService");// MyService类的onBind方法返回的是MyService的内部类MyServiceImplboolean isBind = bindService(intent, mServiceConnection,Context.BIND_AUTO_CREATE);if (isBind) {Log.i(TAG, TAG + "********绑定成功***********");} else {Log.i(TAG, TAG + "********绑定失败***********");}break;case R.id.startSecondActivity:Intent intent2 = new Intent(MainActivity.this, SecondActivity.class);startActivity(intent2);break;default:break;}}}
流程: A.bindService()--->S.onCreate--->S.onBind---->>A.onServiceConnected绑定成功,并获得Service对象

结束绑定按钮的监听事件-->>unbindService(conn)关闭连接对象-->>S.destory()销毁该service

PS:SecondActivity和Maintivity代码完全一样,只是在AndroidMainfest多了一个属性,可自行创建。

 android:process="com.dyk.aidltest.SecondActivity" 
点击“绑定服务”后效果如图: 可以看到已经获取到Book的信息了。


get。


客户端到调用服务器端的方法到此结束。如果有这么一个需求:服务端有数据变动需要立即通知客户端,那么应该怎么做呢?没错,接口。需要说明的是这里的接口不是普通的接口。因为AIDL中无法使用普通接口,所以我们需要写一个aidl接口。

package com.example.aidl;import com.example.aidl.Book;interface IOnNewBookArrivedListener{void onNewBookArrived(in Book newBook);}

在IBookManaget里增加两个抽象方法
void registerListener(IOnNewBookArrivedListener listener);void unregisterListener(IOnNewBookArrivedListener listener);
由于IBookManager增加两个抽象方法,所以相应的MyServiceImpl需要重写这两个方法。

<span style="white-space:pre"></span>@Overridepublic void registerListener(IOnNewBookArrivedListener listener)throws RemoteException {mListenerList.register(listener);Log.i(TAG, "register listener:" + listener);}@Overridepublic void unregisterListener(IOnNewBookArrivedListener listener)throws RemoteException {// 其实就是从Map中移除相应的键值对(listener是key)mListenerList.unregister(listener);Log.i(TAG, "unregister listener:" + listener);}
对象是不能跨进程直接传输的,对象跨进程传输本质都是反序列化的过程。使用普通的ArrayList后Binder会把客户端传输过来的对象重新转化并生成一个对象,虽然我们注册和反注册使用的同一个客户端对象,但是这样通过Binder传递到服务器端后会产生两个全新的对象。使用RemoteCallbackList可以解决这个问题,具体优点如下:
// RemoteCallbackList是系统专门提供的用于删除跨进程listener的接口// 当客户端进程终止后,它能自动移除客户端所注册的listener// 内部自动实现了线程同步的功能// 它不是list 内部采用Map接口保存AIDL的回调/** 监听器列表 */private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();

在addBook中调用这个监听方法

<span style="white-space:pre"></span>// 遍历通知每个监听者int len = mListenerList.beginBroadcast();for (int i = 0; i < len; i++) {IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);// 如果listener.onNewBookArrived(book)比较耗时,最好新开线程,防止ANRlistener.onNewBookArrived(book);}mListenerList.finishBroadcast();
接下来在MainActivity或SecondActivity中实现这个接口,并重写两个方法registerListener和unregisterListener。

<span style="white-space:pre"></span>IOnNewBookArrivedListener mIOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {@Overridepublic void onNewBookArrived(Book newBook) throws RemoteException {//书籍增加后的做法Log.i(TAG, "newBook="+newBook.bookId+"\t"+newBook.bookName);}};
MainActivity或SecondActivity的onServiceConnected中注册监听

<span style="white-space:pre"></span>//注册为监听者mIBookManager.registerListener(mIOnNewBookArrivedListener);

MainActivity或SecondActivity的onDestroy中反注册监听

<span style="white-space:pre"></span>if (mIBookManager!=null&&mIBookManager.asBinder().isBinderAlive()) {try {//解除监听mIBookManager.unregisterListener(mIOnNewBookArrivedListener);} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}

到这里基本算大功告成了,最后的最后还有一个小缺陷需要我们修复。默认情况下我们的服务器端谁都可以连接,这可不一定是我们想要的。没错,加入限权。

首先在AndroidMainfest中声明所需的限权

 <!-- 自定义MyService的限权,在OnBind中验证限权 -->    <permission        android:name="com.example.service.permission.MyService"        android:protectionLevel="normal" >    </permission>

<span style="white-space:pre"></span><service            android:name="com.example.service.MyService"            android:permission="com.example.service.permission.MyService" >            <intent-filter>                <action android:name="com.example.service.MyService" />            </intent-filter>        </service>

<span style="white-space:pre"></span><!-- 使用限权 -->    <uses-permission android:name="com.example.service.permission.MyService" />

最后在onBinder中验证限权,没有限权返回空,无法绑定

<span style="white-space:pre"></span>@Overridepublic IBinder onBind(Intent intent) {//限权验证 ,没有限权返回空,无法绑定int check  = checkCallingOrSelfPermission("com.example.service.permission.MyService");if (check==PackageManager.PERMISSION_DENIED) {return null;}return new MyServiceImpl();}

缕清思路加写作一共花了3个多小时,终于完成了!这一大套流程下来可真不容易,希望对你有所帮助。


Demo下载地址:http://download.csdn.net/detail/qq_17250009/9252577

更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. 类和 Json对象
  3. Python list sort方法的具体使用
  4. python list.sort()根据多个关键字排序的方法实现
  5. Android提交数据到服务器的两种方式四种方法
  6. 初探Android中的binder机制
  7. android面试题总结
  8. Android中SyncTask的使用
  9. android 预装第三方apk的方法

随机推荐

  1. android解析XML
  2. android 画文字 自动换行
  3. Android:关于音频焦点(Respecting Audio F
  4. android之手机震动Vibrate
  5. Android(安卓)Build: Tips and Tricks
  6. android位置服务GPS经纬度获取
  7. android init.rc init.%PRODUCT%.rc 解析
  8. android判断用户网络类型
  9. android中的字体闪烁效果和跑马灯效果
  10. Android(安卓)studio 操作数据库游标适配