AIDL的使用

1、服务端

在main目录右键New>Folder>AIDL Folder,操作完后main目录下会自动新建一个aidl目录。
在aidl目录下新建包名:com.test.binder_aidl,在包内创建一个ICalcAIDL.aidl文件:

package com.test.binder_aidl;

interface ICalcAIDL
{
	int add(int x , int y);
	int min(int x , int y );
}

选择Make Project编译一下,在build/generated/aidl_source_output_dir…目录下会生成ICalcAIDL.java文件,然后我们在项目中新建一个Service,代码如下:

package com.test.binderserver;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.test.binder_aidl.ICalcAIDL;


public class CalcService extends Service {

    private static final String TAG = "server";

    public void onCreate()
    {
        Log.e(TAG, "onCreate");
    }

    public IBinder onBind(Intent t)
    {
        Log.e(TAG, "onBind");
        return mBinder;
    }

    public void onDestroy()
    {
        Log.e(TAG, "onDestroy");
        super.onDestroy();
    }

    public boolean onUnbind(Intent intent)
    {
        Log.e(TAG, "onUnbind");
        return super.onUnbind(intent);
    }

    public void onRebind(Intent intent)
    {
        Log.e(TAG, "onRebind");
        super.onRebind(intent);
    }

    private final ICalcAIDL.Stub mBinder = new ICalcAIDL.Stub() {
        @Override
        public int add(int x, int y) throws RemoteException {
            return x + y;
        }

        @Override
        public int min(int x, int y) throws RemoteException {
            return x - y;
        }
    };

}

记得在AndroidManifest中注册Service:

 <service android:name=".CalcService"
     android:exported="true">
     <intent-filter>
         <action android:name="com.test.binderserver.aidl.calc" />
     </intent-filter>
 </service>

这个不需要Activity,所以我也就没写Activity,安装完成也看不到安装图标,悄悄在后台运行着。
到此,服务端编写完毕。下面开始编写客户端。

2、客户端

客户端也必须要有对应的aidl文件,并且aidl的包名要与服务端保持一致,我们可以把服务端的aidl复制到客户端。
客户端的代码比较简单,创建一个布局,里面包含4个按钮,分别为绑定服务,解除绑定,调用加法,调用减法。布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical" >

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="bindService"
        android:text="BindService" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="unbindService"
        android:text="UnbindService" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="addInvoked"
        android:text="12+12" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="minInvoked"
        android:text="50-12" />

</LinearLayout>

MainActivity代码如下:

package com.test.binderclient;

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.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.test.binder_aidl.ICalcAIDL;

public class MainActivity extends AppCompatActivity {

    private ICalcAIDL mCalcAidl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void bindService(View view) {
        Intent intent = new Intent();
        intent.setAction("com.test.binderserver.aidl.calc");
        intent.setPackage("com.test.binderserver");
        boolean result = bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
        Toast.makeText(this, "result:" + result, Toast.LENGTH_SHORT).show();
    }

    public void unbindService(View view) {
        unbindService(mServiceConn);
    }

    public void addInvoked(View view) throws RemoteException {
        if(mCalcAidl != null){
            int addRes = mCalcAidl.add(12, 12);
            Toast.makeText(this, addRes + "", Toast.LENGTH_SHORT).show();
        } else
        {
            Toast.makeText(this, "服务器被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT)
                    .show();

        }
    }

    public void minInvoked(View view) throws RemoteException {
        if (mCalcAidl != null)
        {
            int addRes = mCalcAidl.min(50, 12);
            Toast.makeText(this, addRes + "", Toast.LENGTH_SHORT).show();
        } else
        {
            Toast.makeText(this, "服务端未绑定或被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT)
                    .show();

        }
    }

    ServiceConnection mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e("client", "onServiceConnected");
            mCalcAidl = ICalcAIDL.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e("client", "onServiceDisconnected");
            mCalcAidl = null;
        }
    };
}

注意android 5.0以后需要调用setPackage()或setComponent(),android在这里添加了校验,具体位置在ContextImpl.binderService()>ContextImpl.bindServiceCommon()>ContextImpl.validateServiceIntent():

    private void validateServiceIntent(Intent service) {
        if (service.getComponent() == null && service.getPackage() == null) {
            if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                IllegalArgumentException ex = new IllegalArgumentException(
                        "Service Intent must be explicit: " + service);
                throw ex;
            } else {
                Log.w(TAG, "Implicit intents with startService are not safe: " + service
                        + " " + Debug.getCallers(2, 3));
            }
        }
    }

此时运行客户端,点击BindService按钮,结果弹出的Toast显示result:false,bindService调用不成功。
想要成功调用,第一种方式是把targetSdkVersion 版本修改到30以下;第二种方式是在客户端的manifest中指定服务端的包名(谷歌的文档说明 https://developer.android.google.cn/guide/topics/manifest/queries-element?hl=en),在客户端的manifst文件中添加如下配置:

<manifest>
    <queries>
        <!--如果想要与其他的应用进行AIDL通信的话,需要在这里注册包名的信息-->
        <package android:name="com.test.binderserver" />
    </queries>
</manifest>

重新运行,点击BindService按钮,调用成功,打印如下:

2022-08-02 10:42:00.731 25654-25654/com.test.binderserver E/server: onCreate
2022-08-02 10:42:00.732 25654-25654/com.test.binderserver E/server: onBind
2022-08-02 10:42:00.734 25709-25709/com.test.binderclient E/client: onServiceConnected

然后点击12+12,50-12可以成功的调用服务端的代码并返回正确的结果。
最后点击UnbindService,打印如下:

2022-08-02 10:42:02.299 25654-25654/com.test.binderserver E/server: onUnbind
2022-08-02 10:42:02.299 25654-25654/com.test.binderserver E/server: onDestroy

发现客户端的onServiceDisconnected并没有打印,我们继续点击12+12,50-12发现依然可以正常执行,也就是说即使onUnbind被调用,连接也是不会断开的,那么什么时候会断开呢?即当服务端被异常终止的时候,比如我们现在在手机设置>正在执行的程序中找到该服务,点击Force Stop,此时查看log:

2022-08-02 11:03:35.413 26801-26801/com.test.binderclient E/client: onServiceDisconnected

可以看到onServiceDisconnected打印了,此时连接被断开,现在点击12+12,50-12的按钮,则会弹出Toast服务端断开的提示。

注意到连续两次点击BindService按钮,bindService()都能调用成功;然后连续两次点击UnbindService按钮,此时客户端会崩溃,报错信息:Caused by: java.lang.IllegalArgumentException: Service not registered,所以在我们unbindService()前必须先bindService()。

参考:http://blog.csdn.net/lmj623565791/article/details/38461079

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值