You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中InputMethodService监听网络变化弹窗崩溃问题求助

问题描述

开发Android自定义键盘时,需求是键盘弹出后,网络中断时显示提示弹窗。此前在Activity中实现正常,但基于InputMethodService的场景下运行崩溃,报错如下:

错误信息

Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
                    at android.view.ViewRootImpl.setView(ViewRootImpl.java:1159)
                    at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:399)
                    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:133)
                    at android.app.Dialog.show(Dialog.java:345)
                    at com.prasath.openaikeyboard.util.NetworkChangeListener.onReceive(NetworkChangeListener.java:25)
                    at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0$LoadedApk$ReceiverDispatcher$Args(LoadedApk.java:1679)

相关代码:

InputMethodService类代码

public class MyInputMethodService  extends InputMethodService implements KeyboardView.OnKeyboardActionListener {
    private static final String TAG = "MyInputMethodService";
    private NetworkChangeListener networkChangeListener=new NetworkChangeListener();

    @Override
    public View onCreateInputView() {
        KeyboardView keyboardView=(KeyboardView) getLayoutInflater().inflate(R.layout.keyboard_view,null);
        Keyboard keyboard=new Keyboard(this,R.xml.numberpad);
        keyboardView.setKeyboard(keyboard);
        keyboardView.setOnKeyboardActionListener(this);
        networkChangeListener=new NetworkChangeListener();
        return keyboardView;

    }
}

NetworkChangeListener类代码

public class NetworkChangeListener extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if(!Common.isConnectedToInternet(context)){
            AlertDialog.Builder builder=new AlertDialog.Builder(context);
            View view= LayoutInflater.from(context).inflate(R.layout.check_internet_dialog,null);
            builder.setView(view);

            Button button=view.findViewById(R.id.btnrefreshid);

            AlertDialog alertDialog=builder.create();
            alertDialog.show();
            alertDialog.setCancelable(false);

            alertDialog.getWindow().setGravity(Gravity.CENTER);

            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    alertDialog.dismiss();
                    onReceive(context,intent);
                }
            });
        }
    }
}
问题原因

AlertDialog依赖Activity类型的Context创建窗口,但你在onReceive中传入的是InputMethodService的Context(属于Service类型),没有有效的窗口token,导致无法添加Dialog窗口,触发BadTokenException。

解决方法

方法1:绑定输入法窗口token创建Dialog

利用InputMethodService自身的窗口token,让Dialog依附在输入法窗口上,具体步骤如下:

调整MyInputMethodService代码

public class MyInputMethodService extends InputMethodService implements KeyboardView.OnKeyboardActionListener {
    private static final String TAG = "MyInputMethodService";
    private NetworkChangeListener networkChangeListener;

    @Override
    public void onCreate() {
        super.onCreate();
        networkChangeListener = new NetworkChangeListener(this);
        // 注册网络状态广播(务必在onDestroy中注销)
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(networkChangeListener, filter);
    }

    @Override
    public View onCreateInputView() {
        KeyboardView keyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.keyboard_view, null);
        Keyboard keyboard = new Keyboard(this, R.xml.numberpad);
        keyboardView.setKeyboard(keyboard);
        keyboardView.setOnKeyboardActionListener(this);
        return keyboardView;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (networkChangeListener != null) {
            unregisterReceiver(networkChangeListener);
        }
    }

    // 提供获取输入法窗口token的方法
    public IBinder getInputWindowToken() {
        return getWindow().getWindow().getAttributes().token;
    }
}

修改NetworkChangeListener代码

public class NetworkChangeListener extends BroadcastReceiver {
    private MyInputMethodService inputMethodService;

    public NetworkChangeListener(MyInputMethodService service) {
        this.inputMethodService = service;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (!Common.isConnectedToInternet(context)) {
            AlertDialog.Builder builder = new AlertDialog.Builder(inputMethodService);
            View view = LayoutInflater.from(inputMethodService).inflate(R.layout.check_internet_dialog, null);
            builder.setView(view);

            Button button = view.findViewById(R.id.btnrefreshid);
            AlertDialog alertDialog = builder.create();

            // 设置Dialog依附于输入法窗口
            alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
            WindowManager.LayoutParams params = alertDialog.getWindow().getAttributes();
            params.token = inputMethodService.getInputWindowToken();
            params.gravity = Gravity.CENTER;
            alertDialog.getWindow().setAttributes(params);

            alertDialog.setCancelable(false);
            alertDialog.show();

            button.setOnClickListener(v -> {
                alertDialog.dismiss();
                // 避免递归调用,直接重新检查网络
                if (!Common.isConnectedToInternet(context)) {
                    // 可添加重试提示或逻辑
                }
            });
        }
    }
}

方法2:改用无依赖的提示组件

如果不需要强交互弹窗,可直接使用Toast或依附于输入法View的Snackbar,这类组件无需Activity Context:

@Override
public void onReceive(Context context, Intent intent) {
    if (!Common.isConnectedToInternet(context)) {
        Toast.makeText(context, "网络连接中断,请检查网络", Toast.LENGTH_LONG).show();
    }
}

注意事项

  • 广播需在onCreate注册、onDestroy注销,避免内存泄漏;
  • 使用TYPE_INPUT_METHOD_DIALOG确保Dialog显示在输入法上层;
  • 禁止在onReceive中递归调用自身,避免无限循环。

内容的提问来源于stack exchange,提问作者Prasath S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 08:47:21