android:handle.postDelayed 与 removeCallbacks 用法

本文详细介绍了Android中Handler机制的postDelayed方法和removeCallbacks方法的使用。postDelayed方法用于延迟执行任务,removeCallbacks方法则用于取消已安排的任务。通过具体示例展示了如何在启动活动前设置延迟时间,并在适当时候取消这些任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         postDelayed方法和removeCallbacks方法的使用:

      方法postDelayed的作用是延迟多少毫秒后开始运行,而removeCallbacks方法是删除指定的Runnable对象,使线程对象停止运行。

    postDelayed使用方法:

<strong>  </strong><pre name="code" class="java" style="font-weight: bold;">package com.way.activity;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.FragmentActivity;
import android.text.TextUtils;

import com.way.util.PreferenceConstants;
import com.way.util.PreferenceUtils;
import com.way.xx.R;

public class SplashActivity extends FragmentActivity {
	private Handler mHandler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.splash);
		mHandler = new Handler();
		String password = PreferenceUtils.getPrefString(this,
				PreferenceConstants.PASSWORD, "");
		if (!TextUtils.isEmpty(password)) {
			mHandler.postDelayed(gotoMainAct, 3000);
		} else {
			mHandler.postDelayed(gotoLoginAct, 3000);
		}
	}

	Runnable gotoLoginAct = new Runnable() {

		@Override
		public void run() {
			startActivity(new Intent(SplashActivity.this, LoginActivity.class));
			finish();
		}
	};

	Runnable gotoMainAct = new Runnable() {

		@Override
		public void run() {
			startActivity(new Intent(SplashActivity.this, MainActivity.class));
			finish();
		}
	};
}
removeCallbacks使用方法:

  handler.removeCallbacks(gotoMainAct);



                
08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: FATAL EXCEPTION: main 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: Process: com.android.provision, PID: 2483 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running? 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.view.ViewRootImpl.setView(ViewRootImpl.java:2101) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:546) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:402) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.app.Dialog.show(Dialog.java:366) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.app.AlertDialog$Builder.show(AlertDialog.java:1131) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.widget.VideoView$5.onError(VideoView.java:615) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.media.MediaPlayer$EventHandler.handleMessage(MediaPlayer.java:3640) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:249) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.os.Looper.loop(Looper.java:337) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:9562) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:648) 08-04 10:45:35.399 1000 2483 2483 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1005) 08-04 10:45:35.402 1000 2483 2483 D OOMEventManagerFK: checkEventAndDumpForJE: 0
最新发布
08-05
<think>嗯,用户遇到了Android开发中常见的WindowManager$BadTokenException异常,需要解决方案。这个异常通常发生在尝试显示对话框或弹出窗口时,上下文对象使用不当。 用户提供的引用信息很有价值。引用[3]明确指出问题根源在于使用getApplicationContext()创建对话框,而正确做法应该是使用Activity上下文。引用[4]则展示了在onWindowFocusChanged回调中处理弹出窗口时可能遇到的相同问题。 这个异常的核心在于窗口令牌(token)的缺失。Android系统要求每个窗口都必须关联有效的窗口令牌,而Activity上下文会提供这个令牌。当使用Application上下文时,系统无法获取必要的令牌,导致崩溃。 特别要注意生命周期问题。即使使用Activity上下文,如果在Activity尚未完全创建或已经销毁时尝试显示对话框,也会触发这个异常。引用[4]的代码就展示了在onWindowFocusChanged回调中处理不当的情况。 解决方法需要从两个维度考虑:上下文选择和时间点控制。必须确保使用当前有效的Activity实例作为上下文,并且只在Activity生命周期安全阶段(如onResume之后)显示对话框。对于PopupWindow等特殊控件,还需要注意setFocusable等属性的设置。 用户可能是中级Android开发者,因为这个问题在初学者中更常见,但调试起来确实需要一定经验。ta可能正在紧急修复线上崩溃,所以需要清晰直接的解决方案。我会提供具体代码示例和常见场景的注意事项,帮助ta快速定位问题。 考虑到用户可能需要预防类似问题,我还准备补充生命周期管理的最佳实践。比如使用isFinishing()检查Activity状态,或者通过View.postDelayed确保UI操作在安全时机执行。这些细节能帮助用户从根本上避免此类异常。</think>解决 `WindowManager$BadTokenException: Unable to add window -- token null is not valid` 错误的核心在于**确保在正确的上下文(`Context`)和正确的生命周期时机创建/显示窗口**。以下是详细解决方案和原因分析: --- ### ⚠️ 根本原因 该异常表明系统无法为窗口(`Dialog`、`PopupWindow`、`Toast`等)获取有效的窗口令牌(`Window Token`)。通常由以下原因导致: 1. **使用了错误的上下文**:如 `Application Context` 或已销毁的 `Activity Context`。 2. **生命周期时机错误**:在 `Activity` 未完全创建(`onResume()` 之前)或已销毁(`onDestroy()` 之后)时显示窗口。 3. **异步操作未检查状态**:在后台线程或延迟任务中显示窗口时,未验证 `Activity` 是否仍存活。 --- ### ✅ 解决方案 #### 1. **使用正确的上下文** - **错误做法**:`getApplicationContext()` 或 `getBaseContext()`。 - **正确做法**:使用当前 `Activity` 的实例 (`this` 或 `YourActivity.this`)。 ```java // ✅ 正确示例 new AlertDialog.Builder(MyActivity.this) // 使用 Activity Context .setTitle("警告") .setMessage("操作确认") .create() .show(); ``` #### 2. **确保在 Activity 生命周期安全期操作** - **窗口显示时机**:必须在 `onResume()` 之后、`onPause()` 之前。 - **避免在以下回调中直接显示**: - `onCreate()`:若需显示,使用 `View.post()` 延迟到视图就绪后。 ```java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ✅ 延迟到视图布局完成 findViewById(R.id.button).post(() -> { showDialog(); }); } ``` - `onWindowFocusChanged()`:需检查 `hasFocus` 且确认 `Activity` 未销毁[^4]。 ```java @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus && !isFinishing()) { // 检查 Activity 未销毁 showPopupWindow(); } } ``` #### 3. **处理异步任务中的窗口显示** 在 `AsyncTask`、`Thread` 或 `RxJava` 回调中显示窗口前,必须检查 `Activity` 状态: ```java // 示例:在网络请求回调中 api.fetchData(new Callback() { @Override public void onSuccess() { if (!isFinishing()) { // 关键检查! new AlertDialog.Builder(MyActivity.this).show(); } } }); ``` #### 4. **PopupWindow 的特殊处理** 若异常发生在 `PopupWindow`: - 设置 `setFocusable(true)` 避免令牌问题。 - 通过 `Activity` 的 `getWindow().getDecorView()` 获取根视图: ```java View rootView = getWindow().getDecorView(); PopupWindow popup = new PopupWindow(context); popup.setFocusable(true); // 必须设置 popup.showAtLocation(rootView, Gravity.CENTER, 0, 0); ``` #### 5. **全局 Dialog 的注意事项** 若需在 `Service` 或非 `Activity` 环境中显示 `Dialog`: - 添加权限:`<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />` - 使用 `Dialog` 的 `TYPE_APPLICATION_OVERLAY` 类型(API 26+): ```java Dialog dialog = new Dialog(getApplicationContext()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } ``` --- ### 💡 最佳实践总结 | **场景** | **正确做法** | |--------------------------|--------------------------------------------------| | 创建 `Dialog` | 使用 `Activity.this` 作为上下文 | | 显示时机 | `onResume()` 之后,`onPause()` 之前 | | 异步回调中显示 | 检查 `!isFinishing()` 或 `!isDestroyed()` (API 17+) | | `PopupWindow` | 设置 `setFocusable(true)` + 基于 Activity 的根视图 | | 非 Activity 环境显示窗口 | 申请悬浮窗权限 + 设置 `TYPE_APPLICATION_OVERLAY` | --- ### 📚 相关问题 1. **如何避免 `Activity` 销毁后的内存泄漏?** > 在 `onDestroy()` 中取消异步任务,使用 `WeakReference` 持有 `Context`,避免生命周期组件被长生命周期对象持有。 2. **`DialogFragment` 相比普通 `Dialog` 有什么优势?** > `DialogFragment` 自动管理生命周期,在屏幕旋转等配置变化时保留状态,推荐替代直接使用 `Dialog`[^3]。 3. **如何调试 `WindowManager` 相关的崩溃?** > 使用 `adb shell dumpsys window` 查看窗口令牌状态,检查日志中 `Token{...}` 的 `hashCode` 是否活跃 `Activity` 一致。 4. **`isFinishing()` 和 `isDestroyed()` 有何区别?** > `isFinishing()` 表示 `Activity` 正在结束(可能在 `onDestroy()` 前),`isDestroyed()` (API 17+) 表示已完全销毁,后者更可靠。 > 关键点:**永远不要跨生命周期使用 `Context`,异步操作必须检查 `Activity` 状态**。通过遵守生命周期规则和上下文使用规范,可彻底避免此异常[^1][^2][^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值