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

我想知道是否有一种可能性,我们可以处理/检测延迟( postDelayed方法)在Android上的可运行的callback?

例如,我有一个或几个splashscreen(在我的应用程序(用于testing目的的应用程序)上运行handler.postDelayed(new Runnable()... )。在这个应用程序中,我也有一个库(我正在创build和使用它在应用程序中)以及一些在IntentService类上运行的类。

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

问题是:

  • 那些自动出现在UI中的活动不应该自动closures,这是被禁止的。 它需要用户交互来closures该活动并返回到应用程序的正常行为。
  • 此外,图书馆不知道有关应用程序的用户界面。

所以我的问题是(相对于我创build的库没有提供UI应用程序stream的信息):

  • 有没有办法来检测是否有一些postDelayed方法是在应用程序相对于库一侧创build的? 如果是的话,我怎么处理这个问题呢?

PS:注意,通常,我正在使用一个对话框来表示正在自动出现的活动。

UPDATE

图

图解释:

现在我有一个情况,一个飞溅屏幕正在执行。

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

与此同时,闪屏在postdelayed ,另一个Activity已经创build并显示在UI中。 当X秒过去,另一个活动没有被销毁时, 下一个活动被创build并自动销毁其他活动 。 在这样做的过程中,Android会相对于Activity抛出一个“泄漏的窗口”消息。

Solutions Collecting From Web of "为什么可运行的callback会自动破坏活动?"

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

您可以使用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; }
@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是否存在消息,但是它不会告诉您有什么确切的消息。

我想要的解决scheme是postDelayed在活动的onStop中已经postDelayedRunnable ,因为如果已经启动Activity (来自库的Activity ),那么将调用SplashScreen onStop

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

你需要更好地解释这个问题。 如果问题与postDelayed()或活动的生命周期有关,我会在启animation面和其他活动的关系之间感到困惑。 我会build议一个小图解释说明哪些活动启动其他活动。

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

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

您每次发布匿名,全新的可运行代码,因此您将无法删除它。 我build议以下方法,将Runnables声明为库中的类成员:

 Runnable mLaunchSplashRunnable = new Runnable() { ... }; Runnable mLaunchContactsRunnable = new Runnable() { ... }; . . mHandler.postDelayed (mLaunchSplashRunnable, DELAY); mHandler.postDelayed (mLaunchContactsRunnable, DELAY); . . 

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

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

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

一个查询Handler方法,如果一个特定的Runnable排队,afaik不存在,但也可以使用boolean标志,在可运行列队列时设置它,并在运行Runnable时将其重置,以指示可运行悬而未决。

如果我更好地理解你的问题,我将能够帮助更多。

为什么不使用静态布尔variables来确定您的Splash Screen是否正在运行,当您打算调用它时。

问题是,一旦你开始一个接一个地开始游戏活动,一段时间之后,当你的应用程序消耗了大量的内存,所以android会假设以前的活动可以被破坏,以优化系统,避免设备的放慢,这就是移动设备的工作原理,所以消费API的主要活动是由系统破坏的:请理解活动生命周期:

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

在onStop()后面查看…这是您的应用程序的情况。

希望对你有帮助…

我想你应该反过来在程序中的逻辑。 活动应该从您的活动开始,而不是从服务开始。

为了实现这一点,你可以注册BroadcastReceiver创build每个活动https://developer.android.com/reference/android/content/BroadcastReceiver.html,并使用sendBroadcast从服务https://developer.android.com/guide/components /broadcasts.html当需要开始一个活动时,要命令广播接收机启动需要的活动。

使用时间耦合活动的callback可能不是您的SDK的一个很好的devise。 考虑使用observables从networking层获取数据到需要数据的活动。

通过向需要数据的活动添加观察者,观察networking调用是否完成,只将数据暴露给活动的观察者。 这样,如果活动在呼叫完成之前closures,则不会将数据“推送”到closures的活动。

这也可以让你创build你的库的弱引用,你不需要担心漏窗

因为我们还没有看到你的代码。 我的答案会过于通用,所以对此表示歉意。

我想你应该首先检查logcat,看看是否有任何exception正在closures这些活动。 如果没有什么关于它。 只要检查所有的try catch块,以确保它。 因此,在确定它不涉及任何exception情况之后,请检查AndroidManifest文件以查看活动的“启动模式”。 它也可能导致自动closures。

如果您的所有活动都处于标准模式,则尝试更改至less一个活动启动模式,以禁止其closures并重试。 如果这没有任何意义,请检查finish()调用的代码。

还是没有运气? 那么我想这可能是一个ANR的情况下,你应该检查内存泄漏,冻结你的应用程序,并可能closures活动。 可能这些runnables在某种程度上毁了你的应用程序。

我想你应该有一个机制,如事件总线或广播接收器在你的屏幕之间进行内部通信。 他们将在你的活动的生命周期中运行,不会导致任何forms的exception或情况。

任何时候活动都可以被销毁,所以当发生这种情况时,你也需要照顾那些被销毁的对话框。 在活动的生命周期中,您将需要销毁“对话框”,否则您将获得“泄漏窗口”。

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

 @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 } 

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

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