引言
我司项目中会频繁用到persist.sys.xxx的属性值,系统中预埋接口,通过属性值控制,以应对客户多样化的需求定制。
以往都是先设置属性值,再重启设备使能生效,抽空研究一下实时监听属性值变化,最后在csdn上查到监听SystemProperties变化 这篇文章。
博主的实现方法给了我很大的启发,在该基础上,分别在第三方应用的Activity和Service中实现了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()方法如下:文章来源:https://www.toymoban.com/news/detail-453262.html
@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模板网!