安卓prop/SystemProperties如何监听值变化

背景:

在安卓开发过程中,无论是系统还是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实战开发干货,请关注下面“千里马学框架”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值