一, 项目介绍
[知识准备]
Android Interface definition language(aidl,android 接口定义语言), 其目的实现跨进程的调用. 进程是程序在 os 中执行的载体, 一个程序对应一个进程, 不同进程就是指不同程序, aidl 实现不同程序之间的调用.
主线程与子线程通信使用 handler,handler 可以在子线程中发出消息, 在主线程处理消息, 从而完成线程之间的通信, 即使有多个线程, 仍然是一个程序.
不同程序之间需要通过 aidl 通信, 通信方式可以有多种, aidl 是其中一种. 实现的结果就像自己的程序调用自己的其他方法一样, 感觉就像一个程序.
业务场景: 例如购物 app 需要支付, 购物 app 是淘宝, 支付 app 是支付宝. 所以就需要不同的程序进行通信.
二, 首先介绍一个 App 之间的 Service 和 Activity 之间的通信
[项目结构]
[MyService]
[提示]
创建 Service
如果不是通过上述方法创建, 一定要记得注册
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
[代码]
- public class MyService extends Service {
- public MyService() {
- }
- @Override
- public IBinder onBind(Intent intent) {
- return new MyBinder();//return MyBinder 通过 ServiceConnection 在 activity 中拿到 MyBinder
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- return super.onStartCommand(intent, flags, startId);
- }
- public void payService(){
- Log.i("MyService", "payService: --------");
- }
- class MyBinder extends Binder{
- public void pay(){
- payService();
- }// 通过 Binder 实例将 service 中的方法暴露出去
- }
- }
- [layout_main]
添加按钮, 点击便于调用
- <Button
- android:id="@+id/btn_paly"
- android:text="Pay"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- [MainActivity]
- public class MainActivity extends AppCompatActivity {
- MyService.MyBinder binder = null;
- ServiceConnection conn;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Button btnPlay = (Button) findViewById(R.id.btn_paly);
- conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
- binder = (MyService.MyBinder) iBinder;
- }
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- }
- };
- Intent intent = new Intent(MainActivity.this,MyService.class);
- bindService(intent,conn,BIND_AUTO_CREATE);// 开启服务
- btnPlay.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (binder!=null){
- binder.play();
- }
- }
- });
- }
- }
[效果]
点击后输出 service 中 pay 方法中的内容
三, 两个 App 之间的 Service 通信
[项目结构]
[步骤]
在 AppPayProvider 中创建 MyService
代码同上
[注册]
, 注册时 (android:enabled="true" android:exported="true") 设置为 true, 将 Service 暴露出去, 另一个 App 才能访问到它
, 添加<intent-filter>. 由于不是同一个 App, 通过 intent-filter 对 Intent 进行过滤, 让另一个 app 通过 action 开启服务
- <service
- android:name=".MyService"
- android:enabled="true"
- android:exported="true">
- <!--enable:ture 设置可用
- exported:ture 对外暴露 -->
- <intent-filter>
- <action android:name="com.xqz.apppayprovider.MyService" />
- </intent-filter>
- </service>
MainActivity 和 layout_main 保留创建时不作任何修改, 但也不要删掉, 因为安装程序必须提供起始页面, 否则将会出错
在 AppPayProvider 中添加 AIDL
[代码]
[提示] 接口中定义中方法要和 Service 中的 MyBinder 中的方法一致
再创建好 AIDL, 添加完方法后, android studio 需要对这个 aidl 进行编译, 会自动按 aidl 规范生成一个 Binder 子类的代码.
对 MyService 中的 MyBinder 进行修改
[提示] 继承 IPay.Stub. 在这之前必须 Make Project, 否则将没有只能联想
创建 AppPayUser 对 AppPayProvider 中的 MyService 进行操作
- [layout-main]
- <Button
- android:id="@+id/btnPay"
- android:text="pay"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
将 AppPayProvider 中 AIDL 拷贝到 AppPayUser 中
[提示] , 包名要相同, 按目录位置复制, 通过下述方法, 直接在文件夹进行复制.此处可以查看项目结构, 可以看到包名是相同的
, 同样拷贝过来后需要 Make Project
- [AppPayUser-MainActivity]
- public class MainActivity extends AppCompatActivity {
- Button btnPay;
- private IPay myBinder;// 定义 AIDL
- ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
- myBinder = IPay.Stub.asInterface(iBinder);
- }
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Intent intent = new Intent();
- intent.setAction("com.xqz.apppayprovider.MyService");
- // 表示按照什么进行过滤, 启动意图
- /*android5.0 之后, 如果 servicer 不在同一个 App 的包中,
- 需要设置 service 所在程序的包名
- (包名可以到 App 的清单文件 AndroidManifest 中查看)*/
- intent.setPackage("com.xqz.apppayprovider");
- bindService(intent,conn,BIND_AUTO_CREATE);// 开启 Service
- btnPay = (Button) findViewById(R.id.btnPay);
- btnPay.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- try {
- myBinder.pay();
- } catch (RemoteException e) {
- // 因为是跨程序调用服务, 可能会出现远程异常
- e.printStackTrace();
- }
- }
- });
- }
- }
[安装]
先安装 AppPayProvider 再安装 AppPayUser.
[效果]
将 run 中的 视图调到 AppPayProvider, 点击模拟器 AppPayUser 中的 pay 按钮, 将会执行 AppPayProvider 中 MyService 中 pay 方法中的内容.
四, 总结
[跨 App 和同 App 之间的区别]
跨 App 开启服务是提供服务的 App 需要设置 intent-filter 过滤器, 控制服务的 App 需要通过. setAction 和 setPackage 方法进行设置 action 和包名, 才能开启服务. 而同 App 只需要指定启动的 service 就可.
跨 App 的 MyBinder 实例要通过 AIDL 获取, 两个应用定义同样的接口的方法, 通过对应的 AIDL 名称. Stub.asInterface 方法得到 binder 实例, 然后就和同 App 的 myBinder 使用么有区别了.
跨 App 的 MyBinder 对象的使用必须捕获异常, 而同 App 不需要.
可以根据上方简单的例子实现很多类似的功能.
来源: https://www.cnblogs.com/xqz0618/p/aidl_service.html