android.view.WindowManager$BadTokenException解决

coding

一般报错日志如下

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

回到顶部