Android 如何优雅地监听SystemProperties属性值变化

这篇具有很好参考价值的文章主要介绍了Android 如何优雅地监听SystemProperties属性值变化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

我司项目中会频繁用到persist.sys.xxx的属性值,系统中预埋接口,通过属性值控制,以应对客户多样化的需求定制。
以往都是先设置属性值,再重启设备使能生效,抽空研究一下实时监听属性值变化,最后在csdn上查到监听SystemProperties变化 这篇文章。
博主的实现方法给了我很大的启发,在该基础上,分别在第三方应用的ActivityService中实现了SystemProperties属性值的实时监听,app需要系统签名

注:阅读本博客前,请先阅读监听SystemProperties变化这篇博客。

Activity

		Parcel _data = Parcel.obtain();
        try {
            final Class<?> systemPropertyClass = Class.forName("android.os.SystemProperties");
            final Method addChangeCallback = systemPropertyClass.getDeclaredMethod(
                    "addChangeCallback", Runnable.class);
            final Method getMethod = systemPropertyClass.getDeclaredMethod(
                    "get", String.class, String.class);
            final Method setMethod = systemPropertyClass.getDeclaredMethod(
                    "set", String.class, String.class);
            addChangeCallback.invoke(null, new Runnable() {
                @Override
                public void run() {
                    Log.e("DebugUtil",  "SystemProperties change");
                    try {
                        String result = (String) getMethod.invoke(
                                null, "persist.sys.test", "77777");
                        Log.e("DebugUtil",  "SystemProperties change to " + result);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            });
            
            setMethod.invoke(null, "persist.sys.test", "88888");

            IBinder amsBinder = getService(Context.ACTIVITY_SERVICE);
            _data.writeInterfaceToken("android.app.IActivityManager");
            Boolean result = amsBinder.transact(
                    ('_'<<24)|('S'<<16)|('P'<<8)|'R'/*IBinder.SYSPROPS_TRANSACTION*/,
                    _data, null, 0);
            Log.e("DebugUtil", "result = " + result);
        } catch (RemoteException | IllegalAccessException
                | InvocationTargetException | ClassNotFoundException
                | NoSuchMethodException e) {
            e.printStackTrace();
        } finally {
            _data.recycle();
        }

Service

		try {
            Class<?> systemPropertyClass = Class.forName("android.os.SystemProperties");
            Method addChangeCallback = systemPropertyClass.getDeclaredMethod(
                    "addChangeCallback", Runnable.class);
            Method getMethod = systemPropertyClass.getDeclaredMethod(
                    "get", String.class, String.class);
            Method setMethod = systemPropertyClass.getDeclaredMethod(
                    "set", String.class, String.class);
            addChangeCallback.invoke(null, new Runnable() {
                @Override
                public void run() {
                    DebugUtil.e(TAG, "SystemProperties change");
                    try {
                        String result = (String) getMethod.invoke(null, "persist.sys.test", "123");
                        DebugUtil.e(TAG,  "SystemProperties change to " + result);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            });

            setMethod.invoke(null, "persist.sys.test", "456");

            String[] services = DeviceManagerUtil.listServices();
            if (services == null) {
                DebugUtil.e(TAG, "There are no services, how odd");
            }
            for (String service : services) {
                IBinder obj = DeviceManagerUtil.checkService(service);
                if (obj != null) {
                    Parcel data = Parcel.obtain();
                    try {
                        obj.transact(
                                ('_'<<24)|('S'<<16)|('P'<<8)|'R'/*IBinder.SYSPROPS_TRANSACTION*/,
                                data, null, 0);
                    } catch (RemoteException e) {
                        // Ignore
                    } catch (Exception e) {
                        DebugUtil.e(TAG, e.getMessage());
                    }
                    data.recycle();
                }
            }
        } catch (IllegalAccessException | InvocationTargetException
                | ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        }

DeviceManagerUtil.java

	public static String[] listServices() {
        try {
            Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager");
            Method listServices = serviceManagerClass.getDeclaredMethod("listServices");
            return (String[]) listServices.invoke(null);
        } catch (ClassNotFoundException | NoSuchMethodException
                | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static IBinder checkService(String service) {
        try {
            Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager");
            Method listServices = serviceManagerClass.getDeclaredMethod(
                    "checkService", String.class);
            return (IBinder) listServices.invoke(null, service);
        } catch (ClassNotFoundException | NoSuchMethodException
                | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

总结+分析

1.监听方需要添加回调函数
SystemProperties.addChangeCallback()设置监听回调函数:非源码环境下,通过反射实现;源码环境下,直接调用addChangeCallback()方法即可。
2.设置属性值后,需要通知监听方
通知监听方的方式其实大同小异,遍历所有的Activity和Service,获取到它们的IBinder对象,调用onTransact()方法,通过Binder实现跨进程通信,最终调用SystemProperties的callChangeCallbacks()方法,遍历所有的callbacks的run()方法,通知监听方。
IBinder.SYSPROPS_TRANSACTION的值请查找自己的项目源码获取。
此处有两点需要说明:一是这里涉及到的Binder原理,博主的理解程度还达不到完全讲解清楚的程度(仅意会不会言传),请自行查阅Binder相关原理;二是addChangeCallback()不可执行多次,否则回调也会回调多次。

ActivityManagerService的onTransact()方法如下:

@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 (this) {
                final int NP = mProcessList.mProcessNames.getMap().size();
                for (int ip = 0; ip < NP; ip++) {
                    SparseArray<ProcessRecord> apps =
                            mProcessList.mProcessNames.getMap().valueAt(ip);
                    final int NA = apps.size();
                    for (int ia = 0; ia < NA; ia++) {
                        ProcessRecord app = apps.valueAt(ia);
                        if (app.thread != null) {
                            procs.add(app.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();
            }
        }
        try {
            return super.onTransact(code, data, reply, flags);
        } catch (RuntimeException e) {
            // The activity manager only throws certain exceptions intentionally, so let's
            // log all others.
            if (!(e instanceof SecurityException
                    || e instanceof IllegalArgumentException
                    || e instanceof IllegalStateException)) {
            /// M: Enhance wtf excetpion @{
                if (mAmsExt.IsBuildInApp()) {
                    Slog.wtf(TAG, "Activity Manager Crash."
                        + " UID:" + Binder.getCallingUid()
                        + " PID:" + Binder.getCallingPid()
                        + " TRANS:" + code, e);
                } else {
                    Slog.e(TAG, "Activity Manager Crash."
                        + " UID:" + Binder.getCallingUid()
                        + " PID:" + Binder.getCallingPid()
                        + " TRANS:" + code, e);
                }
            /// @}
            }
            throw e;
        }
    }

SystemPropPoker.java文章来源地址https://www.toymoban.com/news/detail-453262.html

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.development;

import android.os.AsyncTask;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

public class SystemPropPoker  {
    private static final String TAG = "SystemPropPoker";

    private static final SystemPropPoker sInstance = new SystemPropPoker();

    private boolean mBlockPokes = false;

    private SystemPropPoker() {}

    @NonNull
    public static SystemPropPoker getInstance() {
        return sInstance;
    }

    public void blockPokes() {
        mBlockPokes = true;
    }

    public void unblockPokes() {
        mBlockPokes = false;
    }

    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;
        }
    }
}

到了这里,关于Android 如何优雅地监听SystemProperties属性值变化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • powershell 雅地关闭UDP监听器

    在PowerShell中优雅地关闭UDP监听器意味着你需要一种机制来安全地停止正在运行的 UdpClient 实例。由于 UdpClient 类本身没有提供直接的停止或关闭方法,你需要通过其他方式来实现这一点。通常,这涉及到在监听循环中添加一个检查点,以便在接收到停止信号时能够退出循环。

    2024年02月21日
    浏览(44)
  • Android 监听音频焦点变化

    在 Android 中,监听音频焦点变化意味着您可以获得关于音频焦点状态的通知,并针对焦点变化执行相应的操作。音频焦点是指哪个应用程序或组件有权播放音频的能力。 通过监听音频焦点变化,您可以根据不同的焦点状态进行适当的音频处理,以提供更好的用户体验和避免音

    2024年04月26日
    浏览(41)
  • Android 监听网络状态变化

    此篇存在的主要意义在于解决用户使用app中网络状态发生了变化,需要我们去动态监听网络连接状态(有网、无网)、网络类型 (包括wifi、移动网络 - 3G、4G等等) 门前授课 关于网络状态的监听,主要是基于 Android 广播 - BroadcaseReceiver组件 ~ 同时关于广播的注册方面,从An

    2024年02月10日
    浏览(50)
  • Android 之 监听 EditText 的内容变化

    在前面我们已经学过EditText控件了,本节来说下如何监听输入框的内容变化! 这个再实际开发中非常实用,另外,附带着说下如何实现EditText的密码可见与不可见! 由题可知,是基于监听的事件处理机制,好像前面的点击事件是OnClickListener,文本内容 变化的监听器则是:Te

    2024年02月12日
    浏览(46)
  • 微信小程序——自定义组件组件的创建和引用,修改组件的样式隔离选项,stylesolation的可选值,properties属性,data数据,methods方法,数据监听器,用法,监听对象属性的变化

    ①在项目的根目录中,鼠标右键,创建 components - test 文件夹 ②在新建的 components - test 文件夹上,鼠标右键,点击\\\"新建 Component \\\" ③键入组件的名称之后回车,会自动生成组件对应的4个文件,后缀名分别为 js , json ,. wxml 和. wxss 注意,为了保证目录结构的清晰,建议把不同的

    2024年02月15日
    浏览(75)
  • Android网络状态变化监听 -- 结合registerNetworkCallback和广播(kotlin)

        说明   AndroidAndroid针对网络状态变化的监听,在应用内我们通用需要监听设备网络状态的变化,作出相应的业务处理,需要一个方便的、全局的监听实现。。   针对不同设备的系统版本,使用不同的API方法实现;   注意使用广播监听网络状态在高版本的适配问题

    2024年02月03日
    浏览(47)
  • nuxt3 如何监听路由变化?

    nuxt3 如何监听路由变化?

    2024年02月11日
    浏览(56)
  • Vue3组件不发生变化,如何监听pinia中数据变化?

    在开发过程中,我们需要将一些跨组件使用的的数据在pinia中进行状态管理,组件在初始化的时候我们能通过onMounted,computed,watch,watchEffect获取到存储在pinia state中的内容,有一些特殊情况,在组件初始化之后我们无法通过以上四种情况获取state中的内容,这时候我们怎么做呢?

    2024年02月11日
    浏览(54)
  • vue2 如何监听数组的变化

    在Vue 2中,底层是通过重写数组的原型方法来实现对数组变化的监听。具体来说,Vue 2使用了一个名为Observer的类来劫持数组的原型方法,使其在调用这些方法时能够触发相应的变化通知。 当Vue 2初始化一个响应式对象时,如果对象是一个数组,Vue会将数组的原型指向一个经过

    2024年02月12日
    浏览(50)
  • vue如何实现实时监听页面宽度高度变化

    运用的主要技术:watch监听 话不多说直接上代码,自行研究

    2024年02月11日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包