Android 系统开发提供Api供三方应用(二、实现系统添加接
我们一起来开发一款对外SDK,可以让第三方应用调用需要系统级权限的接口,并且调用方式比Google的原生接口还要人性化,不需要Context,随时随地都可以调用。
一、开发流程简要
接口封装流程第一步实现.png二、撰写基础代码
(1)、创建属于自己的文件夹
在系统的frameworks/base/core/java/android/里面创建文件夹htyera/wp/(个人测试使用,如有雷同侵权,纯属巧合,请通知修改)
(2)、创建AIDL文件
创建aidl文件(ILookMessage.aidl),添加了两个方法,一个setValue方法,一个getValue方法,代码如下
package android.htyera.wp;
interface ILookMessage {
void setValue(String message);
String getValue();
}
我是通过Android Studio创建的,如果跟我一样千万别忘记修改包名,否则编译的时候会提示找不到文件的错误。
(3)、配置aidl文件
虽然我们创建了aidl文件,但是编译Android的时候是不会编译到这个文件的,如果要用编译到对应的文件,需要修改frameworks/base/Android.mk文件,在对应的LOCAL_SRC_FILES宏中添加对应的文件即可,代码如下
LOCAL_SRC_FILES += \
core/java/android/htyera/wp/ILookMessage.aidl \
我是第一次本着创建一个完成一个的原则,马上就执行了对文件的编译,在make之前一定要执行make update-api的命令,更新一下api,否则一定会报错的。切记。(注:执行完该命令后,发现frameworks/base/api/current.txt文件有更新,可以看到,文件中增加了一个WRTSZ_SERVICE 的常量和一个com.wrtsz.api的package,该package中就有对应的接口声明。)
(4)、编写系统级服务
创建LookMessageService.java并且继承ILookMessage.Stub实现里面的所有方法,(别忘改包名,别忘改报名,别忘改包名)代码如下:
package android.hytera.wp;
import android.os.RemoteException;
/**
* this is my SecondService for system
* test aidl file
*/
public class LookMessageService extends ILookMessage.Stub {
public String value = "I am a value";
@Override
public void setValue(String message) throws RemoteException {
this.value = message;
}
@Override
public String getValue() throws RemoteException {
return value;
}
}
(5)、定义Java接口类
在注册之前我们先在frameworks/base/core/java/android/content/Context.java中添加一个常量
public static final String LOOK_MESSAGE_WP = "lookmessagewp";
添加常量的目的:就是给服务起一个名字,好比我们再应用层使用服务的时候注册的名字一样,以后使用这个服务的时候都用这个名字就可以了。
123.png
创建java文件(LookMessageManager.java),这个类就是暴露给其它程序使用的Manager接口类,添加的方法跟ILookMessage里面一样就可以了,构造方法有很多种,自己定义吧。
package android.hytera.wp;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
public class LookMessageManager {
private static String TAG = "hty_wp_LookMessageManager";
private ILookMessage mService;
private static LookMessageManager mInstance = null;
public LookMessageManager() {
mService = ILookMessage.Stub.asInterface(ServiceManager.getService(Context.LOOK_MESSAGE_WP));
}
public static synchronized LookMessageManager getInstance() {
if (mInstance == null) {
synchronized (LookMessageManager.class) {
if (mInstance == null) {
mInstance = new LookMessageManager();
}
}
}
return mInstance;
}
public void setValue(String message) {
try {
mService.setValue(message);
} catch (RemoteException e) {
Log.e(TAG, "setValue fail.");
}
}
public String getValue() {
try {
return mService.getValue();
} catch (RemoteException e) {
Log.e(TAG, "getValue fail.");
return "";
}
}
}
此时先不要运行编译,因为运行编译一定会报错的,为什么呢,因为我们ServiceManager.getService(Context.LOOK_MESSAGE_WP)去找这个Service,而系统根本都不知道这是个啥,所以接下来我们要执行注册服务。
(6)、注册服务
仅仅将服务写好是不够的,系统中的每个原生服务都需要注册,相当于给系统留一个备案,方便需要使用的时候快速查找,否则即使编译通过,也是无法找到这个服务的。
在frameworks/base/core/java/android/app/SystemServiceRegistry.java文件中添加如下注册代码:
registerService(Context.LOOK_MESSAGE_WP, LookMessageManager.class,
new StaticServiceFetcher<LookMessageManager>() {
@Override
public LookMessageManager createService() {
return LookMessageManager.getInstance();
}});
(7)、将自定义Service 加入到SystemServer
注册完service后,系统知道有这么一个service,而我们要在开机的时候启动自己的Service,我是在SystemService中的startOtherServices中进行启动的。
traceBeginAndSlog("start htyera_wp service");//log日志
try {
Slog.i(TAG, "add htyera_wp Service");//log日志
// Serial port support
LookMessageService look = new LookMessageService();//创建service
ServiceManager.addService(Context.LOOK_MESSAGE_WP, look);//添加Service
} catch (Throwable e) {
Slog.e(TAG, "Failure starting SerialService", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
(8)、权限策略配置
system/sepolicy/service_contexts lookmessagewp u:object_r:lookmessagewp_service:s0
system/sepolicy/service.te type lookmessagewp_service, system_api_service, system_server_service, service_manager_type;
策略1.png
策略2.png
(9)、编译系统
1、在根目录里面执行:source build/envsetup.sh
2、在根目录里面执行:lunch
3、选择对用的编译版本
4、执行 make update-api更新接口
5、编译 make -j8 编译系统
(10)、刷机重启系统
(11)、创建应用编写调用接口类
首先创建一个项目,并且给项目创建一个moudle包,里面主要是放我们的接口类,不建议直接放app里面,比较乱,可以创建一个moudle或者自己将文件打成jar包。
其中moudle的包名一定要和我们系统源码中的LookMessageManager.java的包名一致
我之门就直接将系统中的LookMessageManager.java文件拷贝到这个包下
jar.png
package android.hytera.wp;
public class LookMessageManager {
private static String TAG = "hty_wp_LookMessageManager";
private static LookMessageManager mInstance = null;
public static synchronized LookMessageManager getInstance() {
throw new RuntimeException("API not supported!");
}
public void setValue(String message) {
throw new RuntimeException("API not supported!");
}
public String getValue() {
throw new RuntimeException("API not supported!");
}
}
(11)、应用层调用
public class MainActivity extends AppCompatActivity {
private Button getValue;
private Button setValue;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getValue = findViewById(R.id.getValue);
setValue = findViewById(R.id.setValue);
getValue.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("hty_wp",LookMessageManager.getInstance().getValue()+"");//获取value
}
});
setValue.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LookMessageManager.getInstance().setValue("我要变化");//设置value
}
});
}
}
通过日子查看,我们可以看到,我们的调用成功,截止到现在我们就实现了第一步,应用程序调用framework的方法。接下来就是根据实际业务进行实现具体功能了,而且还要实现注册监听framework的变化。