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

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.aidl
package com.example.zxc.aidlserver;

// Declare any non-default types here with import statements

interface 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值