为什么可运行的回调会自动破坏活动?

我想知道我们是否有可能在Android上处理/检测具有延迟(方法)的可运行回调?postDelayed

例如,我的应用程序(用于测试目的的应用程序)上有一个或多个初始屏幕(与 一起运行)。在这个应用程序中,我还有一个库(我正在创建并在应用程序中使用它)和一些在类上运行的可用的类。handler.postDelayed(new Runnable()...IntentService

有时,当应用程序运行我正在创建的库的“活动(for)”时,可能会在 UI 中自动弹出一些活动。但是,如果这些活动出现在某个活动上并且正在被销毁,则这些活动(自动弹出的)也将被销毁,并在logcat中记录“泄漏窗口”消息。splashscreenTesting purposesplashscreensplashscreen

问题是:

  • 那些自动出现在UI中的活动不应该自动关闭,这是被禁止的。它需要来自用户的交互来关闭该活动,并返回到应用程序的正常行为。
  • 此外,该库对应用程序的 UI 一无所知。

所以我的问题是(相对于我正在创建的库端,没有UI应用程序流的信息):

  • 有没有办法检测是否在应用程序中相对于库端创建了某些 postDelayed 方法?如果是,我该如何处理这个问题?

附言:请注意,通常情况下,我正在为假设自动出现的活动使用对话框。

更新

Diagram

图表说明:

现在我有一个正在执行初始屏幕的情况。

扩展 IntentService 类的类已收到来自 Internet 的请求,该请求将启动一个活动

同时,初始屏幕打开,另一个活动已创建并显示在 UI 中。当 X 秒过去并且其他活动尚未销毁时,将创建下一个活动并自动销毁其他活动。在这样做的过程中,Android会相对于活动抛出“泄漏窗口”消息。postdelayed


答案 1

有没有办法检测某个 postDelayed 方法是否在应用程序中相对于库端创建了?

您可以使用 MessageQueue.IdleHandler API。请参阅LooperIdlingResource,了解浓缩咖啡如何找出是否适合在断言中开火。


    @Override
    public boolean queueIdle() {

      QueueState queueState = myInterrogator.determineQueueState();
      if (queueState == QueueState.EMPTY || queueState == QueueState.TASK_DUE_LONG) {
        ...
      } else if (queueState == QueueState.BARRIER) {
        ...
      }

      return true;
    }

这将帮助您了解 中是否有消息,但它不会告诉您确切的消息。MessageQueue

我采用的解决方案是取消调度您在活动中的计划,因为如果(来自库的那个)已经启动,则称为:RunnablepostDelayedonStopActivityonStopSplashScreen


public class SplashActivity extends AppCompatActivity {

  private final Runnable myRunnable = () -> {
    // launch `NextActivity`
  };
  private final Handler handler = new Handler();

  @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_splash);

    handler.postDelayed(myRunnable, 3000);
  }

  @Override
  protected void onStop() {
    super.onStop();
    handler.removeCallbacks(myRunnable);
  }

}


答案 2

您需要更好地解释问题。我在初始屏幕和其他活动的关系之间感到困惑,以及问题是否与活动生命周期相关或与活动生命周期有关。我建议用一个小的图形图表来解释哪些活动启动了其他活动。postDelayed()

关于 ,一般来说,如果您这样做postDelayed()

 mHandler.postDelayed(new Runnable() { ... });

您每次都发布一个匿名的、可运行的全新内容,因此无法将其删除。我建议采用以下方法,将 Runnables 声明为库中的类成员:

Runnable mLaunchSplashRunnable = new Runnable() { ... };
Runnable mLaunchContactsRunnable = new Runnable() { ... };

.
.

mHandler.postDelayed (mLaunchSplashRunnable, DELAY);
mHandler.postDelayed (mLaunchContactsRunnable, DELAY);

.
.

由于 runnables 现在不是匿名的,因此您可以随时从队列中删除它们:

void removeLibraryDelayedRunnables() {
   mHandler.removeCallbacks(mLaunchSplashRunnable);
   mHandler.removeCallbacks(mLaunchContactsRunnable);
}

请注意,如果没有任何已发布的 runnables,前面的方法不会失败,因此随时调用它是安全的。

一种查询特定对象是否排队的方法,afaik 不存在,但您可以使用一个标志,在 runnable 排队时设置它,并在运行时重置它,以指示一个 runnable 处于挂起状态。HandlerRunnablebooleanRunnable

如果我更了解你的问题,我就能提供更多的帮助。


推荐