android.view.WindowManager$BadTokenException解决
一般报错日志如下
android.view.WindowManager$BadTokenException
Unable to add window -- token android.os.BinderProxy@65000e for displayid = 0 is not valid; is your activity running?
还原失败(未找到符号表)(404_1_0_2_0_0_0_0_9_0)
1
android.view.ViewRootImpl.setView(ViewRootImpl.java:936)
2
android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:398)
3
android.view.WindowManagerImpl.addView(WindowManagerImpl.java:131)
4
android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4529)
5
android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:51)
6
android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecuto
问题分析
根据堆栈分析源码,发现wm.addView(decor, l);最终调用的是Activity#getWindowManager方法返回的WindowManager的addView,而addview方法属于WindowManager实现的接口ViewManager的方法。所以可以想到使用动态代理的方式拦截addview方法。
addView调用系统源码如下:
if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
代码实现
// 该方案有一定的弊端,因为强制将原WindowManager替换为我们代理类,系统获取windowManager时直接强转会出现类型异常。// 不过目前只在Activity#getSystemService方法遇到。需要处理下
// 该问题在华为手机遇到量比较大,所以建议只处理华为相关手机
public class HookWindowManager {
public static final String TAG = "HookWindowManager";
private Object mWindowManager;
public void init(Activity activity) {
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.P) {
return;
}
if (Build.BRAND.equalsIgnoreCase("HUAWEI") ||
Build.BRAND.equalsIgnoreCase("HONOR")) {
try {
Log.d(TAG, "invoke " + activity.getClass().getSimpleName());
Field wm = Activity.class.getDeclaredField("mWindowManager");
wm.setAccessible(true);
mWindowManager = wm.get(activity);
Class windowManagerCls = Class.forName("android.view.WindowManager");
Class[] classes = {windowManagerCls};
Object viewManageProxy = Proxy.newProxyInstance(
windowManagerCls.getClassLoader(),
classes,
new ViewManagerProxy(mWindowManager, activity));
wm.set(activity, viewManageProxy);
} catch (Exception e) {
Log.e(TAG, "" + e);
}
}
public Object getRealWindowManager() {
return mWindowManager;
}
private class ViewManagerProxy implements InvocationHandler {
private Object iViewManager;
private Activity activity;
public ViewManagerProxy(Object iActivityManager, Activity activity) {
this.iViewManager = iActivityManager;
this.activity = activity;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("addView".equals(method.getName())) {
try {
LOGGER.d(TAG, "addView invoke execute ");
return method.invoke(iViewManager, args);
} catch (Exception e) {
LOGGER.w(TAG, "addView exception: ", e);
if (e instanceof WindowManager.BadTokenException && activity != null) {
activity.finish();
}
return null;
}
}
return method.invoke(iViewManager, args);
}
}
}
只需对指定Activity加入这段代理代码即可达到拦截addView方法,捕获异常。
注意:
因为对Activity的windowmanger注入了我们实现的代理,而系统获取windowmanager时会强转,类型不一样所以会报错。
getSystemService调用的是Activity的方法,所以需要重写Activity#getSystemService
public Object getSystemService(@NonNull String name) { if (WINDOW_SERVICE.equals(name)) {
Object windowManager = mHookWindowManager.getRealWindowManager();
if (windowManager != null) {
return windowManager;
}
}
return super.getSystemService(name);
}
以上是 android.view.WindowManager$BadTokenException解决 的全部内容, 来源链接: utcz.com/z/510140.html