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

使用Dagger2在Module中实现ProgressDialog触发IllegalStateException求助

解决Dagger2中ProgressDialog的WindowManager$BadTokenException问题

兄弟,我一眼就看出你的问题出在哪了——你用Application级别的Context创建了ProgressDialog,但Dialog是必须依附于Activity窗口的组件,Application Context没有对应的窗口token,所以调用show()的时候直接炸了,就是你看到的WindowManager$BadTokenException。

问题根源拆解

你在ApplicationModule里提供的是ApplicationContext,并用来初始化ProgressDialog:

@Provides @Singleton 
public ProgressDialog provideProgressDialog(Context context) {
    return new ProgressDialog(context); // 这里的context是Application的!
}

Dialog需要绑定到一个活跃的Activity窗口,Application Context没有窗口,自然无法添加Dialog,就会抛出token无效的异常。

解决方案:给ProgressDialog绑定Activity生命周期

我们需要把ProgressDialog的创建逻辑移到Activity级别的Module里,让它依赖Activity的Context,并且和Activity生命周期绑定。

步骤1:创建自定义的Activity作用域

因为Singleton是全局生命周期,我们需要一个和Activity生命周期匹配的作用域:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

步骤2:创建ActivityModule,提供Activity级依赖

@Module
public class ActivityModule {
    private AppCompatActivity mActivity;

    public ActivityModule(AppCompatActivity activity) {
        this.mActivity = activity;
    }

    @Provides
    @ActivityScope
    public ProgressDialog provideProgressDialog() {
        ProgressDialog progressDialog = new ProgressDialog(mActivity);
        progressDialog.setTitle(mActivity.getString(R.string.str_progress_dialog_title));
        progressDialog.setMessage(mActivity.getString(R.string.str_progress_dialog_desc));
        progressDialog.setCancelable(false);
        return progressDialog;
    }

    @Provides
    @ActivityScope
    public Context provideActivityContext() {
        return mActivity;
    }
}

步骤3:创建ActivityComponent,依赖ApplicationComponent

这样既能复用Application级的单例(比如你的APIEndPoints),又能提供Activity专属的依赖:

@ActivityScope
@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
    void inject(BaseActivity baseActivity);
    void inject(HomeActivity homeActivity);
}

步骤4:修改BaseActivity的注入逻辑

把原来的ApplicationComponent注入改成ActivityComponent:

public class BaseActivity extends AppCompatActivity {
    @Inject
    protected ProgressDialog mProgressDialog;
    @Inject
    protected HomePresenter homePresenter;

    private ActivityComponent mActivityComponent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 构建ActivityComponent并注入
        mActivityComponent = DaggerActivityComponent.builder()
                .applicationComponent(((Dagger2DemoApplication) getApplication()).getmApplicationComponent())
                .activityModule(new ActivityModule(this))
                .build();
        mActivityComponent.inject(this);
    }

    public ActivityComponent getActivityComponent() {
        return mActivityComponent;
    }
}

步骤5:清理ApplicationModule

把原来在ApplicationModule里的provideProgressDialog方法删掉,避免冲突,只保留全局单例的提供:

@Module
public class ApplicationModule {
    private Dagger2DemoApplication mDagger2DemoApplication;

    public ApplicationModule(Dagger2DemoApplication dagger2DemoApplication) {
        this.mDagger2DemoApplication = dagger2DemoApplication;
    }

    @Provides
    @Singleton
    public APIEndPoints provideAPIEndPoints() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://reqres.in/")
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
        return retrofit.create(APIEndPoints.class);
    }

    @Provides
    @Singleton
    public Context provideApplicationContext() {
        return mDagger2DemoApplication.getApplicationContext();
    }
}

为什么这样改就没问题?

  • 用Activity的Context创建Dialog,有了有效的窗口token,show()方法就能正常添加窗口了。
  • ActivityScope保证ProgressDialog的生命周期和Activity一致,不会出现内存泄漏,也符合Dialog的使用场景(一个Activity对应一个Dialog实例)。
  • ActivityComponent依赖ApplicationComponent,之前的全局单例(比如APIEndPoints)仍然可以正常注入,不影响原有功能。

额外小建议

现在官方其实已经不推荐用ProgressDialog了,更建议用Material Design风格的ProgressBar或者自定义的加载弹窗,但不管用哪种,核心原则都是:弹窗类组件必须使用Activity的Context。

内容的提问来源于stack exchange,提问作者Maulik Dodia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:24:59