需求背景
Android系统定制中,无可避免需要对外提供一些定制化的接口,使app能完成一些定制化需求。之前用过aidl文件和jar用于app和系统同步接口,但效果都不尽人意。aidl文件对接口有顺序要求,不好增删改查,且接口调用的逻辑诡异;jar需要单独编译,且不能够直接看到接口信息,需要反编译。经过一番探索,发现可以让app使用抽象类来调用系统中的接口,该抽象类类似C语言中的.h文件,只描述接口,实际实现类在系统framework中。
示意图如下:

原理介绍
CustomManager.java作为中间桥梁,内容如下:
java
public abstract class CustomManager {
public static CustomManager getInstance(Context context) {
return (CustomManager) context.getSystemService("CustomManager");
}
public abstract String getVersion();
}
app将CustomManager.java导入到项目中,并import即可使用CustomManager中的接口。
为了app导入CustomManager.java后能顺利编译通过,CustomManager.java中不能使用超出标准api以外的类,但可交由抽象方法中的实现(CustomManagerImpl)或后端CustomManagerService去做。
app导入CustomManager.java后, 先调用CustomManager.getInstance(Context context)获取实例,然后再使用接口,如:
java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private CustomManager mCustomManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
//实例化
mCustomManager = CustomManager.getInstance(this)
//调用具体接口
mCustomManager.getVersion()
}
}
CustomManagerImpl.java继承了CustomManager.java并实现了在CustomManager.java中定义的接口:
java
public class CustomManagerImpl extends CustomManager {
private final ICustomManager mService;
public CustomManagerImpl(Context context, ICustomManager service) {
mService = service;
mContext = context;
}
public String getVersion() {
try {
return mService.getVersion();
} catch (Exception e) {
e.printStackTrace();
}
return "unknow";
}
}
CustomManagerImpl.java由系统端维护,编译进framework.jar, app在调用CustomManager.getInstance(Context context)后会返回一个CustomManagerImpl实例并转换为CustomManager。 为什么会返回一个CustomManagerImpl实例?因为在系统中注册了服务"CustomManager": SystemServiceRegistry:
java
registerService("CustomManager", com.so.CustomManagerImpl.class,
new CachedServiceFetcher<com.so.CustomManagerImpl>() {
@Override
public com.so.CustomManagerImpl createService(ContextImpl ctx)
throws ServiceNotFoundException {
IBinder b = ServiceManager.getService("CustomManagerService");
ICustomManager service = ICustomManager.Stub.asInterface(b);
return new com.so.CustomManagerImpl(ctx, service);
}});
因此在app中只要通过一个字符串"CustomManager"即可得到一个在framework中实现的CustomManagerImpl的实例:
java
public abstract class CustomManager {
public static CustomManager getInstance(Context context) {
return (CustomManager) context.getSystemService("CustomManager");
}
}
app获得CustomManager实例后,调用其接口,其实都是在调用CustomManagerImpl的接口。 如mCustomManager.getVersion(),其实走的是CustomManagerImpl.getVersion()。
与后端服务CustomManagerService的交互
CustomManagerImpl虽然在framework中实现,但运行时还在app域,受权限等的限制,能调用的系统功能有限,因此我们进一步在系统中新增一个系统级服务CustomManagerService来实现更复杂的功能。CustomManagerImpl通AIDL来与CustomManagerService交互, 新增aidl文件ICustomManager.aidl:
java
interface ICustomManager {
String getVersion();
}
CustomManagerService继承ICustomManager.Stub, 并实现接口:
java
public class CustomManagerService extends ICustomManager.Stub {
public String getVersion() {
return "V1.0.0";
}
}
SystemServiceRegistry在注册服务"CustomManager"时, 也将CustomManagerService的Binder引用传给了CustomManagerImpl.mService,所以在CustomManagerImpl.getVersion()中调用了mService.getVersion()即是调用CustomManagerService.getVersion()。
总结
将抽象类做"头文件"来对外提供接口,有维护简单,逻辑明了,可读性强的优点,甚至可以针对不同需求提供不完全的CustomManager.java来实现接口的隐藏。