17

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

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

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

问题是:

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

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

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

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

更新

图表

图表说明:

现在我有一个正在执行Splashscreen的案例。

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

同时启动画面打开postdelayed,另一个Activity已创建并显示在 UI 中。当 X 秒过去且另一个Activity没有被销毁时,会创建下一个 Activity并自动销毁另一个Activity在这样做时,Android 会相对于Activity抛出“泄漏窗口”消息。

4

8 回答 8

4

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

您可以使用MessageQueue.IdleHandlerAPI。看看LooperIdlingResourceespresso 如何判断是否是触发断言的适当时间。


    @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,但不会告诉您确切的消息是什么。

我要采用的解决方案是取消安排RunnablepostDelayedonStop活动中的活动,因为如果Activity(来自库的活动)已经启动,那么将调用onStopof :SplashScreen


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);
  }

}

于 2017-08-20T23:17:42.470 回答
2

当您要调用它时,为什么不使用静态布尔变量来确定您的启动画面是否正在运行。

于 2017-08-25T07:45:49.803 回答
2

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

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

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

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

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

.
.

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

.
.

由于可运行对象现在不是匿名的,您可以随时将它们从队列中删除:

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

请注意,如果没有任何已发布的可运行对象,则前面的方法不会失败,因此可以随时调用它。

一种查询Handler特定Runnable是否排队的方法,afaik,不存在,但您可以使用一个boolean标志,在可运行的排队时设置它,并在运行时重置它Runnable,以指示可运行的待处理。

如果我更好地了解您的问题,我将能够提供更多帮助。

于 2017-08-18T12:00:02.477 回答
2

问题是,一旦你开始一个接一个地触发活动,一段时间后你的应用程序已经消耗了大量内存,所以android假设可以销毁以前的活动以优化系统并避免设备变慢,这就是移动设备的工作原理,因此您正在使用 API 的主要活动被系统破坏:请了解活动生命周期:

https://developer.android.com/guide/components/activities/activity-lifecycle.html

在 onStop() 之后查看...您的应用就是这种情况。

希望对你有帮助...

于 2017-08-24T10:23:10.750 回答
1

我认为您应该在程序中颠倒逻辑。活动应该从您的活动开始,而不是从服务开始。

为此,您可以在创建每个活动时注册 BroadcastReceiver https://developer.android.com/reference/android/content/BroadcastReceiver.html并使用服务中的 sendBroadcast https://developer.android.com/guide/components /broadcasts.html当需要启动一个活动时,命令广播接收器开始需要的活动。

于 2017-08-18T15:38:37.323 回答
1

因为我们还没有看到你的代码。我的回答太笼统了,很抱歉。

我认为您应该首先检查 logcat 以查看关闭这些活动是否有任何异常。如果没有什么。只需检查所有 try catch 块即可确定。因此,在您确定它与任何类型的异常无关后,请检查 AndroidManifest 文件以查看活动的“启动模式”。它也可能导致自动关闭。

如果您的所有活动都处于标准模式,则尝试更改至少一种活动启动模式以使其无法关闭,然后重试。如果这也没有任何意义,请检查完成()调用的代码。

还是没有运气?然后我想这可能是 ANR 的情况,您应该检查导致应用程序冻结并可能关闭活动的内存泄漏。可能这些可运行文件会以某种方式破坏您的应用程序。

我认为你应该有一个像事件总线或广播接收器这样的机制来在你的屏幕之间进行内部通信。它们将在您的活动的生命周期中运行,并且不会导致任何类型的异常或 anr。

于 2017-08-25T09:22:26.377 回答
1

对您的 SDK 使用具有时间耦合活动的回调可能不是一个很好的设计。考虑使用 observables 从网络层获取数据到需要数据的活动。

通过将观察者添加到需要数据的活动中,观察者查看网络调用是否完成,您只需将数据暴露给活动的观察者。这样,如果活动在调用完成之前关闭,则不会将数据“推送”到关闭的活动。

这还允许您以无需担心泄漏窗口的方式创建对库的弱引用

于 2017-08-25T09:19:12.967 回答
1

活动随时可能被销毁,因此当这种情况发生时,您也需要注意正在被销毁的对话框。在 Activity 的生命周期中,您还需要销毁“Dialogs”,否则您将获得“Leaked Window”。

此外,引用活动的对话框将受益于使用对活动的弱引用,然后您将使用弱引用来报告用户对活动的操作。

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    listener = new WeakReference<>((MyActionListener) activity);
    activityWeakReference = new WeakReference<>(activity);
}

在显示对话框之前,我还要确保 Activity 不在完成过程中:

if (myActivityWeakReference.get() != null && !myActivityWeakReference.get().isFinishing()) {
  // show dialog
}

然后,当您想控制设备的旋转时,重新创建活动时,您可以将以下内容与对话框一起使用:

    @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
}

/**
 * Prevent the dialog being dismissed when rotated, when using setRetainInstance
 */
@Override
public void onDestroyView() {
    if (getDialog() != null && getRetainInstance()) {
        getDialog().setDismissMessage(null);
    }
    super.onDestroyView();
}
于 2017-08-25T11:29:35.293 回答