android 利用AIDL实现Binder跨进程通信

来源:互联网 发布:淘宝刷单是什么 编辑:程序博客网 时间:2024/05/16 10:45

AIDL整体流程

这里写图片描述
使用AIDL实现安卓跨进程通信的时候,主要分为3步骤:

  1. 声明AIDL接口文件,android sdk会根据aidl文件生成接口形式的java文件,服务端实现接口中的抽象类Stub,其中包含了我们声明的方法。并在onBind回调中作为Binder返回给客户端
  2. 客户端与服务端绑定,在回调函数onServiceConnected中获取Binder
  3. 利用Stub的asInterface方法转换为我们声明的接口,通过接口调用服务端逻辑。

在使用AIDL时要注意,服务端可能同时和多个客户端进行绑定,所以要设计为线程安全的模式。
除此之外,Service依托于主线程而不依托于创建它的组件,这句话的意思是比如在某个Acitivity中创建Service,Activity关闭后Service仍可运行,然后那个应用关闭后,Service失去依托的主线程所以无法运行。基于此,我们也要注意在Server中的CPU密集和耗时操作,同样要放在新的线程中运行。


示例

1、声明AIDL接口文件

// ITimeManager.aidlpackage com.example.zxc.aidlserver;// Declare any non-default types here with import statementsinterface ITimeManager {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    String getTime();}

声明一个方法,获取服务端的时间。
2、在服务端实现Stub抽象类,实现其中的方法,并通过onBInd回调接口,在Server被绑定调用此回调接口时,将Binder暴露给客户端:

public class MyService extends Service {    public MyService() {    }    private final ITimeManager.Stub binder = new ITimeManager.Stub() {        @Override        public String getTime() throws RemoteException {            return String.valueOf(System.currentTimeMillis());        }    };    @Override    public IBinder onBind(Intent intent) {        // TODO: Return the communication channel to the service.                return binder;    }}

3、在客户端绑定Service,实现连接回调接口,连接成功时将收到的Binder,通过Stub中的asInterface方法转换为可以使用的接口

public class MainActivity extends AppCompatActivity {    private ITimeManager iTimeManager;    private ServiceConnection mConn = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            iTimeManager = ITimeManager.Stub.asInterface(service);            try {                Log.d("TAG", iTimeManager.getTime());            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {            iTimeManager = null;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        bindService(new Intent(MainActivity.this, MyService.class), mConn, Context.BIND_AUTO_CREATE);    }}

将客户端和服务端放在不同应用中

当然,这里客户端和服务端还是同一个应用,如果两个应用的话,首先要将Myservice放在服务端,MainActivity中设置连接回调和绑定服务的操作放在客户端。然后aidl下整个包拷贝至服务端应用和客户端应用,注意保持两者包文件名、文件结构相同。最后在服务端应用的Manifest文件设置Service的IntentFilter,这样我们就可以隐式调用了:

        <service            android:name=".MyService"            android:enabled="true"            android:exported="true">            <intent-filter>                <action android:name="com.example.zxc.aidlserver.MyService"/>                <category android:name="android.intent.category.DEFAULT" />            </intent-filter>        </service>

需要提到一个地方:客户端在绑定时隐式调用intent,由于安卓5.0以上不允许隐式调用的方式,会抛出异常

Service Intent must be explicit

解决方法是在隐式调用时,声明intent的时候同时调用setAction和setPackage方法,会被认为是显示调用,最后,客户端绑定服务:

        //注意,Intent的action客户端和服务端保持相同即可,packageName要是aidl文件的包名        bindService(new Intent("com.example.zxc.aidlserver.MyService").setPackage("com.example.zxc.aidlserver"), mConn, Context.BIND_AUTO_CREATE);

操作时先打开服务端应用,再打开客户端应用绑定服务即可。可以看到客户端的应用打出了日志

08-22 21:45:31.543 31624-31624/com.example.zxc.myapplication:remote D/TAG: bind
08-22 21:45:31.553 31624-31624/com.example.zxc.myapplication:remote D/TAG: 1471873531564

0 0
原创粉丝点击