Android日常开发中工作中经常到遇到这种情况,一些不能与源码编译的第三方APP想调用一些framework 层或者platfom 签名应用才有权限调用的一些方法,比如更改系统时间,更改系统字体,写入系统级属性,开启关闭系统设备等,这些方法如果没有底层去开接口支持,第三方应用真是一愁莫展。本文将以一个实例讲解如何以AIDL的方式给上层应用开调用底层方法的接口。
接口需求:
将Android源码frameworks/base/core/java/android/service/persistentdata/PersistentDataBlockManager.java类透出接口给上层调用,关于PersistentDataBlockManager的作用,有兴趣的朋友,可以参考: Android L集成新特性之恢复出厂设置保护之如何实现,类似苹果ID的远程控制功能
先来看AIDL的简介:
AIDL可以跨进程访问其他应用程序,和其他应用程序通讯,说到进程间通讯,很多技术都可以访问,比如广播应用B向A发送指定Action的广播,A就能收到信息,这样也能看成不同应用之间完成了通讯(但是这种通讯是单向的);还如ContentProvider,通过URI接口暴露数据给其他应用访问;但是这种都算不上是应用之间的通讯。
Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.
“只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL”,其他情况下你都可以选择其他方法,如使用Messager,也能跨进程通讯。可见AIDL是处理多线程、多客户端并发访问的。而Messager是单线程处理。
一. 在参与源码编译的应用平台项目中定义AIDL Server
1. 创建一个ADIL 接口文件
在项目中新建包名:com.asus.cnfindphone.service.persistentdata
包下新建文件IDataBlockService.aidl
package com.asus.cnfindphone.service.persistentdata;
// Declare any non-default types here with import statements
interface IDataBlockService {
byte[] read();
int write(in byte[] data);
void wipe();
}
IDataBlockService 接口提供三个接口方法 读 ,写 ,擦除。
2. 创建AIDL Service实现类
包名下创建类文件 DataBlockService.java
目的是要将 PersistentDataBlockManager类透出去给上层应用读写擦
package com.asus.cnfindphone.service.persistentdata;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.service.persistentdata.PersistentDataBlockManager;
/**
* Created by Qinghua_Liu on 2017-2-28.
*/
public class DataBlockService extends Service {
private static String TAG = "Qinghua";
@Override
public void onCreate() {
super.onCreate();
}
private boolean bInPSTList(String calling) {
boolean bRes = false;
if (calling.equalsIgnoreCase("asus.findmyphone")) {
bRes = true;
}
Log.d(TAG, "bInPSTList==" + bRes);
return bRes;
}
@Override
public IBinder onBind(Intent intent) {
//String callingApp = intent.getComponent().getPackageName();
return mBind;
}
private final IDataBlockService.Stub mBind = new IDataBlockService.Stub() {
@Override
public byte[] read() throws RemoteException {
byte[] bytes = null;
Log.d(TAG, "DataBlockService.read()");
String callingApp = getApplicationContext().getPackageManager().getNameForUid(Binder.getCallingUid());
Log.d(TAG, "callingApp==" + callingApp);
if (bInPSTList(callingApp)) {
PersistentDataBlockManager pdbManager = (PersistentDataBlockManager) getApplicationContext().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
bytes = pdbManager.read();
//String id ="shenshiid";
//return id.getBytes();
}
Log.d(TAG, "pdbManager.read()==" + new String(bytes));
return bytes;
}
@Override
public int write(byte[] data) throws RemoteException {
Log.d(TAG, "DataBlockService.write()" + new String(data));
String callingApp = getApplicationContext().getPackageManager().getNameForUid(Binder.getCallingUid());
Log.d(TAG, "callingApp==" + callingApp);
if (bInPSTList(callingApp)) {
if (data != null && data.length > 0) {
PersistentDataBlockManager pdbManager = (PersistentDataBlockManager) getApplicationContext().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
pdbManager.write(data);
}
}
return 0;
}
@Override
public void wipe() throws RemoteException {
}
};
}
3.Manifest 静态注册Service
<service android:name=".service.persistentdata.DataBlockService">
<intent-filter>
<action android:name="com.asus.cnfindphone.service.persistentdata.DataBlockService"/>
</intent-filter>
</service>
4. 过滤Service 调用者package:
细心的读者可能 已经发现 2 步骤中的代码:
@Override
public byte[] read() throws RemoteException {
byte[] bytes = null;
Log.d(TAG, "DataBlockService.read()");
String callingApp = getApplicationContext().getPackageManager().getNameForUid(Binder.getCallingUid());
Log.d(TAG, "callingApp==" + callingApp);
if (bInPSTList(callingApp)) {
PersistentDataBlockManager pdbManager = (PersistentDataBlockManager) getApplicationContext().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
bytes = pdbManager.read();
//String id ="shenshiid";
//return id.getBytes();
}
Log.d(TAG, "pdbManager.read()==" + new String(bytes));
return bytes;
}
private boolean bInPSTList(String calling) {
boolean bRes = false;
if (calling.equalsIgnoreCase("asus.findmyphone")) {
bRes = true;
}
Log.d(TAG, "bInPSTList==" + bRes);
return bRes;
}
代码应该很好明白:只允许asus.findmyphone package 调用接口,如果需要让多个包调用可以写入config.xml 定义字串array
需要注意的是获取Service调用者真实包名,防止伪包名:
String callingApp = getApplicationContext().getPackageManager().getNameForUid(Binder.getCallingUid());
5.源码编译AIDL Android.mk的写法:
LOCAL_SRC_FILES := $(call all-java-files-under)
LOCAL_SRC_FILES += \
src/com/asus/cnfindphone/service/persistentdata/IDataBlockService.aidl
6.源码编译安装平台应用。
二. 在Android Studio 中第三方应用调用AIDL接口
上面已经将AIDL Server 部分做完,下面将讲述如果在Android Studio 环境个调试的第三方应用去实现上面AIDL的调用Client
1. Android Studio如何正确导入aidl
Android Studio与Eclipse及源码文件 目录结构不一样,其aidl 文件目录需要单独在app/src/main/ 目录下创建aidl 目录
再在此aidl 目录下创建将要导入的xxx.aidl的包名,最后将xxx.aidl 文件导入,描述的复杂,还是看图吧:
注意这里的IDataBlockService.aidl文件是server 提供的
2.Activity 类中调用接口:
import com.asus.cnfindphone.service.persistentdata.IDataBlockService;
private IDataBlockService dataBlockService;
private ServiceConnection conn;
private void initService(){
conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("Qinghua","onServiceDisconnected! ");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
dataBlockService = IDataBlockService.Stub.asInterface((IBinder)service);
Log.d("Qinghua","bind success! ");
}
};
Intent i = new Intent("com.asus.cnfindphone.service.persistentdata.DataBlockService");
i.setPackage("com.asus.cnfindphone");
bindService(i, conn, Context.BIND_AUTO_CREATE);
}
protected void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
setContentView(R.layout.activity_main);
initService();
this.location.setOnClickListener(new View.OnClickListener() {
public void onClick(View paramView) {
try {
dataBlockService.write("cissy".getBytes());
}catch (RemoteException e) {
e.printStackTrace();
}
}
});
this.test.setOnClickListener(new View.OnClickListener() {
public void onClick(View paramView) {
try {
byte[] bytes = dataBlockService.read();
Log.d("Qinghua","result:"+new String(bytes));
}catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
@Override protected void onDestroy()
{
super.onDestroy();
unbindService(conn);
dataBlockService = null;
}
三.调试与Log分析:
Log片断及分析:
#往block 写入数据 cissy
03-01 13:45:00.211 5655-5669/com.asus.cnfindphone D/Qinghua: DataBlockService.write()cissy
#调用者包名过滤
03-01 13:45:00.212 5655-5669/com.asus.cnfindphone D/Qinghua: callingApp==asus.findmyphone
03-01 13:45:00.212 5655-5669/com.asus.cnfindphone D/Qinghua: bInPSTList==true
#调用写入成功,system 级权限允许
03-01 13:45:00.213 3001-3127/system_process E/Qinghua: mAllowedUid==1000callingUid==1000
#读block 数据
03-01 13:45:04.315 5655-5668/com.asus.cnfindphone D/Qinghua: DataBlockService.read()
#调用者包名过滤
03-01 13:45:04.316 5655-5668/com.asus.cnfindphone D/Qinghua: callingApp==asus.findmyphone
03-01 13:45:04.316 5655-5668/com.asus.cnfindphone D/Qinghua: bInPSTList==true
#调用读数据,system 级权限允许
03-01 13:45:04.316 3001-5234/system_process E/Qinghua: mAllowedUid==1000callingUid==1000
#读取之前写的数据成功
03-01 13:45:04.327 5655-5668/com.asus.cnfindphone D/Qinghua: pdbManager.read()==cissy
03-01 13:45:04.327 6488-6488/asus.findmyphone D/Qinghua: result:cissy
————————————————
版权声明:本文为CSDN博主「S三杯两盏淡酒」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lqh517/article/details/59088540