背景:
在安卓开发过程中,无论是系统还是app层面都会经常接触到相关的prop相关的设置和读取,这个和settings数据其实一样的,但是prop相比settings少了一个时刻监听prop的变化,即prop变化后要及时通知到其他进程,这样其他进程接受到了prop变化后也可以及时进行相关的业务处理,如果prop变化后没有相关的通知回调方法的话,那就只能靠不断轮询getprop,这样即无法保证实时性,也严重损坏性能。
所以基于以上的背景,今天带大家实现一下prop变化后相关的监听方法及触发prop变化回调方法。
监听和通知方法介绍
这里先来一个实战案例给大家展示一下具体如何对prop的值的进行监听变化及通知变化。
1、监听方法
/**
* 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);
}
}
这里监听比较简单,只需要添加一个callback既可以,也可以看出这里并没办法和settings那样针对某一个值进行监听,这里相当于是只要有prop变化通知发出,就会收到回调。
2、通知prop变化的相关方法
这里可以用2个现成的方法实现:
2.1 ActivityManager中的notifySystemPropertiesChanged方法
frameworks/base/core/java/android/app/ActivityManager.java
/**
* Notifies {@link #getRunningAppProcesses app processes} that the system properties
* have changed.
*
* @see SystemProperties#addChangeCallback
*
* @hide
*/
@TestApi
public void notifySystemPropertiesChanged() {
// Note: this cannot use {@link ServiceManager#listServices()} to notify all the services,
// as that is not available from tests.
final var binder = ActivityManager.getService().asBinder();
if (binder != null) {
var data = Parcel.obtain();
try {
binder.transact(IBinder.SYSPROPS_TRANSACTION, data, null /* reply */,
0 /* flags */);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
data.recycle();
}
}
2.2 SystemPropPoker的poke()方法
frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/SystemPropPoker.java
public void poke() {
if (!mBlockPokes) {
createPokerTask().execute();
}
}
@VisibleForTesting
PokerTask createPokerTask() {
return new PokerTask();
}
public static class PokerTask extends AsyncTask<Void, Void, Void> {
@VisibleForTesting
String[] listServices() {
return ServiceManager.listServices();
}
@VisibleForTesting
IBinder checkService(String service) {
return ServiceManager.checkService(service);
}
@Override
protected Void doInBackground(Void... params) {
String[] services = listServices();
if (services == null) {
Log.e(TAG, "There are no services, how odd");
return null;
}
for (String service : services) {
IBinder obj = checkService(service);
if (obj != null) {
Parcel data = Parcel.obtain();
try {
obj.transact(IBinder.SYSPROPS_TRANSACTION, data, null, 0);
} catch (RemoteException e) {
// Ignore
} catch (Exception e) {
Log.i(TAG, "Someone wrote a bad service '" + service
+ "' that doesn't like to be poked", e);
}
data.recycle();
}
}
return null;
}
}
可以看出这里通知的本质都是调用transact(IBinder.SYSPROPS_TRANSACTION, data, null, 0)即跨进程通知出去,不过相比之下SystemPropPoker是会通知所有的service,而ActivityManager的notifySystemPropertiesChanged只是通知到ams这个服务,所以通知范围来说notifySystemPropertiesChanged小一些。
各个普通app进程接受其实也是靠ams这个通知的,具体可以看ams服务端源码:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
if (code == SYSPROPS_TRANSACTION) {
// We need to tell all apps about the system property change.
ArrayList<IBinder> procs = new ArrayList<IBinder>();
synchronized (mProcLock) {
final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
mProcessList.getProcessNamesLOSP().getMap();
final int numOfNames = pmap.size();
for (int ip = 0; ip < numOfNames; ip++) {
SparseArray<ProcessRecord> apps = pmap.valueAt(ip);
final int numOfApps = apps.size();
for (int ia = 0; ia < numOfApps; ia++) {
ProcessRecord app = apps.valueAt(ia);
final IApplicationThread thread = app.getThread();
if (thread != null) {
procs.add(thread.asBinder());
}
}
}
}
int N = procs.size();
for (int i=0; i<N; i++) {
Parcel data2 = Parcel.obtain();
try {
//通知各个进程
procs.get(i).transact(IBinder.SYSPROPS_TRANSACTION, data2, null,
Binder.FLAG_ONEWAY);
} catch (RemoteException e) {
}
data2.recycle();
}
}
实战使用
demo设计如下:
settings进程进行prop的设置,通知
Launcher进程进行监听变化
代码如下:
Settings端的代码
packages/apps/Settings/src/com/android/settings/homepage/SettingsHomepageActivity.java
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 7a2f52a8674..b8f91887e21 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -26,6 +26,7 @@ import android.animation.LayoutTransition;
import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
@@ -35,6 +36,7 @@ import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -305,6 +307,11 @@ public class SettingsHomepageActivity extends FragmentActivity implements
updateSplitLayout();
enableTaskLocaleOverride();
+ SystemProperties.set("debug.test.callback","true");
+ ActivityManager am = (ActivityManager) getSystemService(
+ Context.ACTIVITY_SERVICE);
+ am.notifySystemPropertiesChanged();
}
设置"debug.test.callback"这个prop后,需要主动调用notifySystemPropertiesChanged来通知prop变化
Launcher端监听代码
packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4e566abddc..8a4b5803a9 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -130,6 +130,7 @@ import android.os.Bundle;
import android.os.Parcelable;
import android.os.StrictMode;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -1288,7 +1289,7 @@ public class Launcher extends StatefulActivity<LauncherState>
protected Optional<EventEnum> getAllAppsExitEvent() {
return Optional.of(LAUNCHER_ALLAPPS_EXIT);
}
-
+ static boolean register = false;
@Override
protected void onResume() {
TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT);
@@ -1302,6 +1303,15 @@ public class Launcher extends StatefulActivity<LauncherState>
DragView.removeAllViews(this);
TraceHelper.INSTANCE.endSection();
+ if (!register) {
+ register = true;
+ SystemProperties.addChangeCallback(new Runnable() {
+ @Override
+ public void run() {
+ android.util.Log.i("lsm888888","SystemProperties ChangeCallback value = "+ SystemProperties.get("debug.test.callback","defalut"));
+ }
+ });
+ }
}
@Override
注意这里要 SystemProperties.addChangeCallback只添加一次,不要多次重复添加,不然回调多次。
测试结果:
第一次进入Settings App既可以看到Launcher有相关的日志打印:
02-07 11:21:28.940 1217 1257 I lsm888888: SystemProperties ChangeCallback value = true
更多framework实战开发干货,请关注下面“千里马学框架”