Android 通过AIDL在两个APP之间Service通信

原文地址:https://www.cnblogs.com/xqz0618/p/aidl_service.html
https://blog.csdn.net/yiranfeng/article/details/105313562

一、项目介绍

【知识准备】

①Android Interface definition language(aidl,android接口定义语言),其目的实现跨进程的调用。进程是程序在os中执行的载体,一个程序对应一个进程,不同进程就是指不同程序,aidl实现不同程序之间的调用。

②主线程与子线程通信使用handler,handler可以在子线程中发出消息,在主线程处理消息,从而完成线程之间的通信,即使有多个线程,仍然是一个程序。

③不同程序之间需要通过aidl通信,通信方式可以有多种,aidl是其中一种。实现的结果就像自己的程序调用自己的其他方法一样,感觉就像一个程序。

④业务场景:例如购物app需要支付,购物app是淘宝,支付app是支付宝。所以就需要不同的程序进行通信。

二、首先介绍一个App之间的Service和Activity之间的通信

【项目结构】

在这里插入图片描述
【MyService】

【提示】

①创建Service
在这里插入图片描述
 ②如果不是通过上述方法创建,一定要记得注册

1 <service
2             android:name=".MyService"
3             android:enabled="true"
4             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才能访问到它

Ⅱ、添加『』。由于不是同一个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不需要。

④可以根据上方简单的例子实现很多类似的功能。

五、实践问题

1、Attempt to invoke interface method ‘void com.example.aidl_.IPsy.pay()’ on a null object

在这里插入图片描述
  错误原因:提供远程服务的应用和调用远程服务的应用不是同一个应用,两个应用使用的applicationId 相同。要想在同一个手机上安装,他们得设置不同的 applicationId (buidle.gradle)

有关ApplicationId:applicationId默认为应用的包名,可以修改,也有相应的命名规则。在bindService intent 设置 setPackage 这里不是字面上的应用包名,而是应用的 ApplicationId,这也会导致 bindService 返回 false。 更多请参考博客:Gradle学习之设置applicationId( https://www.jianshu.com/p/4653c8a38771)

2、api 30 Android 11 bindservice 返回 false,在 API 28 (Android 9)的模拟器上是可以的 bindService 返回 true,但是运行到 API 30(Android 11)上就返回 false。

我刚开始解决问题的方向一直放在代码上面,是为了兼容需要在代码上做什么处理,看了官网并没有得到什么信息。

园友说将 targetSDKVersion 设置为 28就可以了。

targetSDKVersion 设置应用的目标版本,当Android版本低于APP的目标的API时,将使用Android手机版本API即28,当APP目标版本低于Android手机版本,将以APP目标版本API在Android手机上以目标版本的行为运行,也就是 目标版本设置为 28 在api30 的手机上,以api 28行为运行,这也就是APP的向上兼容。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android AIDLAndroid Interface Definition Language)是一种Android中用于实现跨进程通信的机制。它允许一个应用程序的进程与另一个应用程序的进程进行交互。 在AIDL中,双向通信可以通过以下步骤实现: 1. 创建AIDL接口:首先,在服务提供方的应用程序中创建一个AIDL接口类,该接口定义了需要在两个应用程序之间进行通信的方法。 2. 实现AIDL接口:在服务提供方的应用程序中实现AIDL接口。这些方法将用于处理由客户端发起的请求。 3. 绑定Service:在客户端应用程序中,通过绑定Service与提供方建立连接。这可以通过Intent和bindService()方法完成。 4. 获取Service接口:一旦客户端与服务提供方建立了连接,客户端将获得Service的接口。这个接口将通过onServiceConnected()方法返回给客户端。 5. 调用方法:客户端可以使用接口对象来调用服务提供方的方法,从而向服务提供方发送请求。 6. 返回结果:服务提供方在接收到请求后,根据请求的类型进行相应的处理,并将处理结果返回给客户端。 通过以上步骤,双向通信将会在两个应用程序之间建立起来。服务提供方可以处理客户端的请求,并将结果返回给客户端。而客户端可以调用服务提供方的方法,并获取所需的数据。 需要注意的是,为了保证AIDL双向通信的正常运行,需要在AndroidManifest.xml文件中声明相应的权限和服务。 总结来说,使用Android AIDL可以实现双向通信。服务提供方与客户端通过AIDL接口进行交互,客户端发送请求给服务提供方,并获取处理结果。这种机制可以方便地实现不同应用程序之间的数据交换和通信

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值