Android studio使用系统源码的AIDL接口

2,418 阅读3分钟

前言

最近使用Android studio开发的时候,需要用到系统类,导入jar包可以解决该问题,但由于该应用需要做到平台兼容,导入jar包的方式会显得APP很臃肿。最后考虑使用AIDL方式,APP端作为客户端,系统源码实现服务端。

正文

AIDL是一个缩写,全称是Android Interface Definition Language(Android接口定义语言)。主要用于进程间通信。下面详细说说服务端和客户端该如何创建AIDL,并让两个进程进行通讯。

服务端

这里服务端是在系统源码中实现,网上查找资料,都说先创建一个AIDL文件,然后在Android.mk文件中添加AIDL文件的编译,就会生成一个对应的Java文件。折腾了很久,都没生出一个Java文件,坑。 如果没有Java文件生成,则自己创建一个即可,下面说说操作。

创建AIDL文件

AIDL接口,需要有一个AIDL文件进行描述说明,说明服务端支持什么样的接口。AIDL的接口样式如下:

//ITestService.aidl
package com.xxx.yyy;
interface ITestService {
    boolean write(String str);
    String read();
}

这里的AIDL文件,引用的包名为服务端所在的进程包名。

Android.mk文件中引用AIDL文件

Android.mk文件中为整个进程的编译规则,这里添加了AIDL文件后,需要将AIDL文件添加进去,否则会编译失败,增加如下语句即可:

LOCAL_SRC_FILES += $(call all-Iaidl-files-under, src)

这里表示编译AIDL文件。

创建AIDL相应的Java文件

既然不能生成AIDL相应的Java文件,那我们就要自己去创建Java文件。Java文件的创建内容如下:

//TestService.java
package com.xxx.yyy;

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

public class TestService extends Service {
    static private final String TAG = "TestService";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    ITestService.Stub mBinder = new ITestService.Stub() {
        @Override
        public boolean write(String str) throws RemoteException {
            return false;      
        }

        @Override
        public String read() throws RemoteException {
            return null;      
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

Androidmanifest.xml注册服务

添加服务端的AIDL和Java文件后,还需要注册服务,在Androidmanifest.xml文件中注册如下:

<service android:name=".TestService"
        android:process=":remote"
        android:exported="true">
        <intent-filter>
            <action android:name="com.xxx.yyy.TestService"/>
        </intent-filter>
</service>

到这里,服务端的工作就完成了。

客户端

客户端是用AS开发的,所以我们需要在AS中也创建一个AIDL文件,不然AS没接口可调用。这里直接将服务端的AIDL文件拷贝过来即可。这里要特别注意的是,AIDL文件的路径。如果路径不匹配,AS就会编译错误。这里说的路径匹配,是指AIDL文件中,AIDL文件所在的文件路径要符合AIDL文件引用的包名。这里结合本例说明。先放上AIDL文件的路径:

as_project/src/main/aidl/com/xxx/yyy/ITestService.aidl

首先先在main目录下,创建一个aidl的文件夹,接下来的目录级别就是跟随着AIDL文件中的包名来了:

package com.xxx.yyy;

客户端绑定服务

拷贝AIDL文件后,接下来就是客户端绑定服务,使用服务端的接口。先贴上代码:

import com.xxx.yyy.ITestService;
static private ITestService mTestService;
static private ServiceConnection mCon;

void init(Context context) {
    mCon = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mTestService = ITestService.Stub.asInterface((IBinder) service);
            Log.d(TAG,"onServiceConnected! ");
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG,"onServiceDisconnected! ");
        }
    };
    Intent intent = new Intent();
    intent.setPackage("com.xxx.yyy");
    Boolean res = context.bindService(intent, mCon, Context.BIND_AUTO_CREATE);
}

init方法放在activity的onCreate方法中调用即可。

这里需要注意的地方有两点:

1.bindservice的时候,传入的intent需要setpackage,这个package名为服务端的包名,否则bindservice的时候会返回false。

2.bindservice成功后,onServiceConnected不是马上回调,会延迟,所以绑定成功后,需要等待onServiceConnected成功回调后,再去调用服务端的接口,否则你会看到报空异常。

服务端方法调用

try {
    boolean res = mTestService.write(str);
}catch (Exception e) {
    e.printStackTrace();
}

结语

AIDL的实现到此结束。

互动

如果文章存在错误描述,可直接留言,一起探讨!

可能感兴趣的文章

Android系统框架

关于Android状态栏高度为0仍显示的问题

踩坑之NavigationBar 的隐藏与否

最后

我在微信公众号也有写文章,更新比较及时,有兴趣者可以关注如下公众号!