如果用户不使用应用程序,则终止 Android 计时器(防止 运行 在后台运行)

Terminating Android Timer if user doesn't use the App(Prevent running in the background)

我的应用程序中有一个 Timer,无限 运行 是一个 Animation。像这样:

Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {

    @Override
    public void run() {
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                //Running Animation Code
            }
        });
    }
}, 1000, 1000);

现在我意识到,即使用户单击 android 的 Back Button,这段代码 运行s 也是如此。事实上,它 运行 在后台运行,而且似乎占用了大量内存。

我需要这个代码 运行 ONLY if user in the app。事实上,当用户点击 Back Button 时,这个 Timer 就会结束,如果用户点击 Home Button,一段时间后该用户不再使用应用程序,就会终止这个 Timer.

我需要的是防止使用内存。因为我意识到如果这段代码 运行 一会儿,应用程序就会冻结!我需要一个正常的行为。

你可以这样做,在onBackPressed()onDestroy(),任何适合你的。

if (t != null) {
   t.cancel();
}

如果你需要,你可以在onResume()开始计时,在onStop()取消计时,这完全取决于你的要求。

If a caller wants to terminate a timer's task execution thread rapidly, the caller should invoke the timer's cancel method. - Android Timer documentation

您还应该看到 purgeHow to stop the Timer in android?

免责声明:这可能不是 100% 最好的方法,有些人可能认为这是不好的做法。

我在生产应用程序中使用了以下代码并且它有效。不过,我已将其编辑(删除了特定于应用程序的参考和代码)为一个基本示例,这应该会给您一个很好的开始。

静态 mIsAppVisible 变量可以在您的应用中的任何地方调用(通过您的 App class),以检查代码是否应该 运行 基于以下条件应用程序需要在 focus/visible.

您还可以在扩展 ParentActivity 的活动中检查 mIsAppInBackground 以查看该应用程序是否真正具有交互性等。


public class App extends Application {
    public static boolean mIsAppVisible = false;


    ...
}

创建一个 "Parent" activity class,您的所有其他活动都会扩展。

public class ParentActivity extends Activity {
    public static boolean mIsBackPressed = false;
    public static boolean mIsAppInBackground = false;
    private static boolean mIsWindowFocused = false;
    public boolean mFailed = false;
    private boolean mWasScreenOn = true;

    @Override
    protected void onStart() {
        applicationWillEnterForeground();

        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();

        applicationDidEnterBackground();
    }

    @Override
    public void finish() {
        super.finish();

        // If something calls "finish()" it needs to behave similarly to
        // pressing the back button to "close" an activity. 
        mIsBackPressed = true;
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        mIsWindowFocused = hasFocus;

        if (mIsBackPressed && !hasFocus) {
            mIsBackPressed = false;
            mIsWindowFocused = true;
        }

        if (!mIsWindowFocused && mFailed)
            applicationDidEnterBackground();

        if (isScreenOn() && App.mIsAppVisible && hasFocus) {
            // App is back in focus. Do something here...

            // this can occur when the notification shade is
            // pulled down and hidden again, for example.
        }

        super.onWindowFocusChanged(hasFocus);
    }

    @Override
    public void onResume() {
        super.onResume();

        if (!mWasScreenOn && mIsWindowFocused)
            onWindowFocusChanged(true);
    }

    @Override
    public void onBackPressed() {
        // this is for any "sub" activities that you might have
        if (!(this instanceof MainActivity))
            mIsBackPressed = true;

        if (isTaskRoot()) {
            // If we are "closing" the app
            App.mIsAppVisible = false;
            super.onBackPressed();
        } else
            super.onBackPressed();
    }

    private void applicationWillEnterForeground() {
        if (mIsAppInBackground) {
            mIsAppInBackground = false;
            App.mIsAppVisible = true;

            // App is back in foreground. Do something here...

            // this happens when the app was backgrounded and is
            // now returning
        } else
            mFailed = false;
    }

    private void applicationDidEnterBackground() {
        if (!mIsWindowFocused || !isScreenOn()) {
            mIsAppInBackground = true;
            App.mIsAppVisible = false;

            mFailed = false;

            // App is not in focus. Do something here...
        } else if (!mFailed)
            mFailed = true;
    }

    private boolean isScreenOn() {
        boolean screenState = false;
        try {
            PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);

            screenState = powerManager.isInteractive();
        } catch (Exception e) {
            Log.e(TAG, "isScreenOn", e);
        }

        mWasScreenOn = screenState;

        return screenState;
    }
}

为了您的使用,您可能希望在 activity(代码片段假定 MainActivity)中创建一个方法来处理动画以调用 t.cancel(); 方法 建议。然后,您可以在 ParentActivity.applicationDidEnterBackground() 方法中添加以下内容:

if (this instanceof MainActivity) {
    ((MainActivity) this).cancelTimer();
}

或者您可以将计时器添加到 ParentActivity class 然后不需要 instanceof 检查或额外的方法。

如果您的 ActivityBackStack 中的最后一个元素,那么它将置于背景中,就像您按下 Home 按钮一样。

因此,onPause()方法被触发。

因此您可以在那里取消动画。

@Override protected void onPause() {
    this.timer.cancel();
}

您还应该在 onResume() 方法中启动动画。 请注意 onResume() 也在 onCreate() 之后调用;所以它甚至适合从应用程序冷启动开始动画。

@Override protected void onResume() {
    this.timer.scheduleAtFixedRate(...);
}
如果您从您的应用程序启动另一个应用程序(例如:铃声选择器),也会调用

onPause()。同样,当您返回您的应用程序时,将触发 onResume()


不需要在onBackPressed().

中添加相同的代码行

此外,在 onStop()onDestroy() 中停止动画有什么意义?

已经在 onPause() 中完成了。当您的应用程序进入后台时,动画将被取消并且不会使用那么多内存

不知道为什么我看到这么复杂的答案。