【发布时间】:2018-02-02 00:06:33
【问题描述】:
我正在尝试在 Android 中打开“概览/最近”屏幕。从Android: Programmatically open "Recent Apps" dialog,我可以使用以下代码:
try {
Class serviceManagerClass = Class.forName("android.os.ServiceManager");
Method getService = serviceManagerClass.getMethod("getService", String.class);
IBinder retbinder = (IBinder) getService.invoke(null, "statusbar");
Class statusBarClass = Class.forName(retbinder.getInterfaceDescriptor());
Object statusBarObject = statusBarClass.getClasses()[0]
.getMethod("asInterface", IBinder.class).invoke(null, retbinder);
Method toggleRecentApps = statusBarClass.getMethod("toggleRecentApps");
toggleRecentApps.setAccessible(true);
toggleRecentApps.invoke(statusBarObject);
} catch (InvocationTargetException| NoSuchMethodException | RemoteException | IllegalAccessException | ClassNotFoundException e){
handleError(e);
}
但是,toggleRecentApps() 自 Nougat 以来就是 no longer in IStatusBarService,它会导致 NoSuchMethodException。
还有其他方式(不包括 AccessibilityService)可以用来打开概览/最近的屏幕吗?
编辑:实现 IStatusBarService 的类似乎是 StatusBarManagerService。但是,调用statusBarObject.getClass().getCanonicalName() 会返回com.android.internal.statusbar.IStatusBarService.Stub.Proxy,因此通过私有字段mBar 获取IStatusBar(它确实实现了toggleRecentApps())似乎不是一种有效的获取方式。
编辑2:看到StatusBarManagerInternal.java是一个包含void toggleRecentApps()的接口,我试图找到它的实现,我在StatusBarManagerService找到了:
private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() { ... }
所以它是 StatusManagerService 中的一个匿名类。 >:(。
不过,我也发现:
/**
* Construct the service, add the status bar view to the window manager
*/
public StatusBarManagerService(Context context, WindowManagerService windowManager) {
mContext = context;
mWindowManager = windowManager;
LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
}
所以它显然在LocalServices 中注册它,而后者又在ArrayMap 中管理它:
private static final ArrayMap<Class<?>, Object> sLocalServiceObjects =
new ArrayMap<Class<?>, Object>();
因此,我尝试访问它:
try {
Field services = Class.forName("com.android.server.LocalServices")
.getDeclaredField("sLocalServiceObjects");
services.setAccessible(true);
ArrayMap<Class<?>, Object> serviceMap = (ArrayMap<Class<?>, Object>) services.get(null);
Set<Map.Entry<Class<?>, Object>> serviceSet = serviceMap.entrySet();
for (Map.Entry<Class<?>, Object> serviceEntry : serviceSet) {
if (serviceEntry.getKey().isInterface()) {
if ("com.android.server.statusbar.StatusBarManagerInternal"
.equals(serviceEntry.getKey().getName())) {
Object statusBarInternalObject = serviceEntry.getValue();
Class statusBarInternalClass = serviceEntry.getKey();
Method openRecents = statusBarInternalClass.getMethod("toggleRecentApps");
openRecents.setAccessible(true);
openRecents.invoke(statusBarInternalObject);
return;
}
}
}
} catch (Exception e) {
handleError(e);
}
但是:
/**
* This class is used in a similar way as ServiceManager, except the services registered here
* are not Binder objects and are only available in the same process.
*
* Once all services are converted to the SystemService interface, this class can be absorbed
* into SystemServiceManager.
*
* {@hide}
*/
唉,我必须在 SystemUI 进程中(或者看起来如此)才能访问它。 (实际上,ArrayMap 是空的,使我的尝试毫无用处。)
任何关于如何从这里开始的指导将不胜感激。
【问题讨论】:
-
这是通过 API 可以更改的反射调用的方法的问题。
-
该方法也不一定在任何给定设备上都可用,因为欢迎设备制造商和自定义 ROM 开发人员根据他们认为合适的方式修改 Android 内部结构。
-
@CommonsWare 是的,但我至少需要它才能在普通 Android 上工作。
-
好的,刚刚再次尝试,但失败了。我可以访问持有
StatusBarInternal的同一张地图吗?或者以某种方式访问该StatusBarInternal实例?或者至少是IStatusBar?
标签: android