背景:
前面文章 安卓prop/SystemProperties如何监听值变化 已经详细介绍了prop如何监听接受prop变化通知。但是也有学员反馈文章只是介绍了有apk的和有java代码的情况,如果是完全一个native进程呢?是否有办法实现native层面也进行prop属性变化后的通知和prop变化的回调。
java相关进程通知流程如下:
native通知流程应该如何做呢?
native端实现通知调研
java端可以,那么可以先分析java端的代码情况:
frameworks/base/core/java/android/os/SystemProperties.java
/**
* Add a callback that will be run whenever any system property changes.
*
* @param callback The {@link Runnable} that should be executed when a system property
* changes.
* @hide
*/
@UnsupportedAppUsage
public static void addChangeCallback(@NonNull Runnable callback) {
synchronized (sChangeCallbacks) {
if (sChangeCallbacks.size() == 0) {
native_add_change_callback();
}
sChangeCallbacks.add(callback);
}
}
可以看到这里有调native_add_change_callback方法
frameworks/base/core/jni/android_os_SystemProperties.cpp
void SystemProperties_add_change_callback(JNIEnv *env, jobject clazz)
{
// This is called with the Java lock held.
if (sVM == nullptr) {
env->GetJavaVM(&sVM);
}
if (sClazz == nullptr) {
sClazz = (jclass) env->NewGlobalRef(clazz);
sCallChangeCallbacks = env->GetStaticMethodID(sClazz, "callChangeCallbacks", "()V");
//这里是核心,把这个do_report_sysprop_change方法放入回调
add_sysprop_change_callback(do_report_sysprop_change, -10000);
}
}
来看看add_sysprop_change_callback方法实现
system/core/libutils/misc.cpp
void add_sysprop_change_callback(sysprop_change_callback cb, int priority) {
pthread_mutex_lock(&gSyspropMutex);
if (gSyspropList == nullptr) {
gSyspropList = new Vector<sysprop_change_callback_info>();
}
sysprop_change_callback_info info;//构造sysprop_change_callback_info对象
info.callback = cb;//传递来的sysprop_change_callback赋值给info
info.priority = priority;
bool added = false;
for (size_t i=0; i<gSyspropList->size(); i++) {
if (priority >= gSyspropList->itemAt(i).priority) {
gSyspropList->insertAt(info, i);
added = true;
break;
}
}
if (!added) {
gSyspropList->add(info);
}
pthread_mutex_unlock(&gSyspropMutex);
}
上面就已经完成了cb的保存,下面就该看通知情况了。
这里就可能主要看跨进程通讯的情况
status_t BBinder::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t /*flags*/)
{
case SYSPROPS_TRANSACTION: {
//跨进程调用到这里,会触发report_sysprop_change方法
if (!binder::os::report_sysprop_change()) return INVALID_OPERATION;
return NO_ERROR;
}
default:
return UNKNOWN_TRANSACTION;
}
}
这里可以看调用到了report_sysprop_change
void report_sysprop_change() {
do_report_sysprop_change();
}
void do_report_sysprop_change() {
#if !defined(_WIN32)
pthread_mutex_lock(&gSyspropMutex);
Vector<sysprop_change_callback_info> listeners;
if (gSyspropList != nullptr) {
listeners = *gSyspropList;
}
pthread_mutex_unlock(&gSyspropMutex);
//ALOGI("Reporting sysprop change to %d listeners", listeners.size());
for (size_t i=0; i<listeners.size(); i++) {
listeners[i].callback();//这里会进行相关的native回调
}
#endif
}
明显可以得出如下结论:
1、无论java还是native,通知prop变化其实都是通过跨进程通讯调用SYSPROPS_TRANSACTION
2、prop变化回调部分,native其实本质就是放入了一个方法指针既可以,有prop变化通知后,会执行传递进来的方法
所以实现native端的prop变化通知只需要如下两个步骤:
1、通知部分只需要跨进程调用一下既可以,code为SYSPROPS_TRANSACTION
2、接受部分只需要native代码中调用
add_sysprop_change_callback(sysprop_change_callback cb, int priority)
这里的sysprop_change_callback其实就是个方法类型
system/core/libutils/include/utils/misc.h
typedef void (*sysprop_change_callback)(void);
实战开发:
通知端代码:
这里可以通知端其实可以native代码,也可以java代码,都可以起到通知作用,因为跨进程最后都会通过native的代码,这里只展示native端代码:
void testBinderProp() {
android::sp<android::IServiceManager> sm = android::defaultServiceManager();
//这里最重要要寻找相关的通知进程,需要有一个服务,apk一般都是ams服务,这里native用audio进程作为接收端
android::sp<android::IBinder> binder = sm->checkService(android::String16("media.audio_flinger"));
android::Parcel _aidl_data;
_aidl_data.markForBinder(binder);
_aidl_data.writeInterfaceToken(android::String16("android.media.IAudioFlingerService"));
android::Parcel _aidl_reply;
android::status_t _aidl_ret_status = binder->transact(IBinder::SYSPROPS_TRANSACTION, _aidl_data, NULL);
}
接受prop变化通知相关的代码:
frameworks/av/media/audioserver/main_audioserver.cpp
整体非常非常简单,只需要调用add_sysprop_change_callback既可以,参数就是callback方法。
结果打印如下:
运行通知端:
130|emu64x:/data/local/tmp # ./android_thread
Number of displays: 1
Display ID: 0
Error in response or non-zero resultCode.
通过日志看接收端情况
130|emu64x:/ # logcat | grep propVhangeCallback
02-08 20:34:38.977 362 609 E audioserver: ---------------- propVhangeCallback 1-------------
可以看到有propVhangeCallback的相关打印,说明已经正常回调到了。
如果换成java代码进行通知:
SystemProperties.set("debug.test.callback","true");
SystemPropPoker.getInstance().poke();
也可以接受到,可以看看相关打印:
test@test:~/aosp15$ adb logcat | grep propVhangeCallback
02-08 20:46:54.467 374 605 E audioserver: ---------------- propVhangeCallback 1-------------
02-08 20:46:54.468 374 605 E audioserver: ---------------- propVhangeCallback 1-------------
可以看到确实有相关的,不过这里会有连续两次回调打印,哈哈,这个你知道为啥吗?这个留给大家的思考作业哈
更多framework实战干货,请关注下面“千里马学框架”