【问题标题】:Is it possible to know if the app is starting after the user has force quit through settings?在用户通过设置强制退出后,是否可以知道应用程序是否正在启动?
【发布时间】:2012-02-22 18:09:52
【问题描述】:

我想知道,可能在我的活动的 onCreate() 中,应用程序之前是否按照正常的主页/返回按钮关闭,或者用户是否实际进入 Android 设置并选择强制关闭。

这两种情况可以区分吗?

编辑:

我想我可能没有很好地解释我的意思。我已经阅读了活动生命周期文档,并且对此非常了解。

基本上,我只想知道活动何时创建,用户之前是否进入过 Android 设置并点击了强制停止。如果应用程序被强制停止,我想采取行动(特别是在启动时显示启动画面)。

下面的很多答案都说我可以在 onStop() 或 onDestroy() 中放置一个标志,并且如果用户按下强制停止,这些方法将不会被调用。问题是这些方法在到达那个点之前就已经被调用了,因为这个顺序:

  1. 应用处于活动状态,正在使用中
  2. 用户点击返回按钮(调用 onStop()、onDestroy())或主页按钮(调用 onStop())或最近应用按钮(调用 onStop())
  3. 用户进入安卓设置,点击强制停止

在这种情况下,我会在 onStop() 上将标志放入共享首选项中,但随后用户点击强制停止,并且标志在 onCreate() 中仍然处于活动状态。

我不想显示启动画面,除非用户在设置中点击了强制停止。我知道这不是它应该做的方式......但这个决定不是我做出的。

【问题讨论】:

  • 始终在 onCreate() 中显示启动画面。仅当应用程序完全完成时才会调用 onCreate()。点击“返回”按钮不会调用 onDestroy()。退出主屏幕然后重新启动应用程序将调用 onResume(),而不是 onCreate()。我原来的答案是成立的。您误解了生命周期。
  • 事实上,当用户按下后退按钮时,总是会调用 onDestroy()。尝试一下!按回,然后重新启动应用程序将导致 onCreate() 被调用。
  • 另外,onDestroy()/onCreate() 也会在设备方向改变时被调用。我绝对不希望我的初始屏幕在方向改变时显示。
  • 看起来你是对的。那是我的误解。继续,祝你好运!

标签: android android-lifecycle


【解决方案1】:

我昨天遇到了同样的问题,this 是我发现 Mark Murphy 所说的。 但是我已经成功破解了。

实际上,只要按下“强制停止”,系统就会清除应用程序占用的所有内存,除了两件事。

1:the shared preference2:sqlite database

所以我通过在应用程序中存储一个布尔变量,操作该值然后在每次启动应用程序时检查它来完成这个技巧。它已经完成了。

CONSTANTS.java

package com.mehuljoisar.forcestopdemo;

public class CONSTANTS {

//the changes made to value of below variable will be cleared on force stop,so whenever force stop occurs,the value of variable will be "isForceStopped=true" again.
    public static boolean isForceStopped = true;
}

MainActivity.java

package com.mehuljoisar.forcestopdemo;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.widget.Toast;

public class MainActivity extends Activity {

    private Context mContext;
    private Intent i;

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

        initialize();


        if(CONSTANTS.isForceStopped)
        {
        //this block of code will be executed in 2 scenario,
        //1: when application is started for very first time
        //2: when application is started after force stopping
            Toast.makeText(mContext, "Display Splash-screen and move to next screen", Toast.LENGTH_SHORT).show();
        //don't forget this part,it's important to make change so that next time splash screen can be avoided
             CONSTANTS.isForceStopped=false;

        //and then launch next screen
            launchSecondScreen();
        }
        else
        {
        //directly launch next screen
            launchSecondScreen();
        }
    }

    private void launchSecondScreen() {
        i.setClass(mContext, SecondActivity.class);
        startActivity(i);
        finish();
    }

    private void initialize() {
        mContext = this;
        i = new Intent();
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

}

SecondActivity.java

package com.mehuljoisar.forcestopdemo;

import android.app.Activity;
import android.os.Bundle;

public class SecondActivity extends Activity{

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

}

AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mehuljoisar.forcestopdemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.mehuljoisar.forcestopdemo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.mehuljoisar.forcestopdemo.SecondActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>

</manifest>

希望对你有帮助!!

【讨论】:

  • @bala:有什么问题?它在我所有的应用程序中都运行良好。
  • 打开您的应用程序,然后返回主屏幕。按住主页按钮以查看您最近使用的应用程序列表。现在从该列表中滑动并关闭您的应用程序。在这种情况下,此解决方案失败。 isForceStopped=true 在这种情况下。这就是为什么我告诉这个解决方案不能正常工作的原因。我对么?有什么想法吗?
  • @bala:是的,它可能会发生,因为在滑动时,系统会终止应用程序并释放 RAM 中占用的内存。为了获得有效的解决方案,您将不得不做不推荐的事情。即:在后台保持无限运行服务的实例,当用户单击“强制停止”时,服务将停止,您可以相应地更新 SharedPreference 以便开启下次启动您的应用时,您可以识别过去的行为。
【解决方案2】:

我不确定是否有更简单的方法,但您可以创建一个 SharedPreferences 对象,该对象使用 Activity 的 onStop 函数中的标志进行更新。并检查 onCreate 中的标志。如果用户强制关闭应用程序,我认为不会调用 onStop 或 onDestroy。

【讨论】:

    【解决方案3】:

    如果用户使用“强制关闭”关闭您的应用程序,则“onStop”方法不会执行。您可以在该方法中放置一个标志(例如保存在文件中、用户首选项、sql 等)。下次启动应用程序时,请检查该标志!

    【讨论】:

      【解决方案4】:

      在您的主页活动中覆盖onBackButtonPressed(在正常情况下,当使用后退按钮时,应用程序主活动已关闭)。

      在此函数中设置一个变量(例如,lastforceClose 为 false)SharedPreferences,然后调用 onbackbuttonpressed() 或 finish() 的超级方法来完成活动。

      下次启动活动时,您可以检查 sharedpreferences 中的变量,以查看 lastforceClose 变量是否设置为 false(您自己现在将其更改为 true)...

      当用户通过 android 设置拥有deleted data 时,您可能还想在这里处理案例。(即共享首选项 xml 文件中不存在密钥等)

      【讨论】:

      • 这并没有说明用户点击 home 而不是返回。它也不考虑用户回击,然后进入设置并按下强制关闭。
      • 当用户点击主页按钮时,活动根本没有被杀死......你可以在一定程度上覆盖它,但你永远不想在主页按钮点击时停止你的应用程序(评论中的案例 1) ...您也可以自行定义 closed as per normal behaviour ,因为您可以通过覆盖后退按钮(评论中提到的第二种情况)调用实际 properly close as per normal behaviour
      • 我知道当用户点击主页按钮时活动没有被杀死。我不想以编程方式停止应用程序。我只想知道活动何时开始,无论用户是否在设置中点击了强制停止。
      【解决方案5】:

      我不是 100% 确定,但这应该可行。
      1)在一些持久存储上的Activity的onDestroy中保存状态(正常关闭)。 (SharedPreferences 或 SQLite)
      2)在 onCreate 检查状态,如果是正常关闭则应用程序正常关闭,否则它被强制关闭。重置状态。

      【讨论】:

        【解决方案6】:

        首先,听起来您可能会将恢复与创建混淆。一定要通读 App 基础知识,特别是关于 Activity Lifecycle 的内容。那个状态图很有帮助。

        当用户“退出”应用程序时,它不一定会被销毁。事实上,它可能不会被销毁,除非开发人员在 onPause() 中放置一些明确销毁 Activity 的钩子。

        Android 应用生命周期中最重要的一个概念是:

        Android 操作系统绝对不保证您的应用程序将存活多久,除了保证它随时可能死亡,无论您是否愿意。

        您应该始终相应地编写您的应用程序,并且填充生命周期中的每一个启动和结束钩子是一种很好的做法。当用户从您的应用程序中退出硬件按钮时,应用程序不会必然被破坏,但是当应用程序通过强制退出关闭时,它会被完全破坏。与其试图弄清楚应用程序是如何关闭的,“正确”的处理方法是设置您的 onPause()onStop()onDestroy() 方法以执行适当数量的根据活动的状态进行清理,并且可能有某种 Preference 对象或状态变量,只有在调用 onDestroy() 时才会翻转,因为从技术上讲,操作系统仍然可以为您“强制退出”应用程序,即使用户不通过任务管理器这样做。

        【讨论】:

          【解决方案7】:

          您可以尝试保留 所有 应用程序生命周期事件的记录,然后检查您的各种 onCreate()s 记录的最后一个事件是否正常。如果不是,您可以合理地推断出发生了什么事。我猜测用户调用的任何强制关闭只会导致系统向应用程序发送kill(2),在这种情况下您不会收到任何指示。 This 可能会有所帮助。

          您还可以考虑是否可以通过不同的方式实现您所追求的目标。如果您的应用程序完全是无状态的,那么任何类型的关闭都不应该有问题。如果您的应用有状态,那么理论上您的所有数据/应用事务都可以达到ACIDity,因此您可以避免由于崩溃/强制关闭而导致的不一致。

          这对你有帮助还是你在追求别的东西?

          【讨论】:

            【解决方案8】:

            从服务而不是活动写入状态标志怎么样?活动生命周期仍将完成,但服务生命周期不应完成。

            它与您的要求不同,但可能足够接近。如果您显示的活动没有此要求,您可能希望停止服务(并稍后重新启动)。或者只是让服务编写某种事件流并使用它来确定启动时要显示的内容。

            【讨论】:

              猜你喜欢
              • 2023-03-08
              • 1970-01-01
              • 2022-09-23
              • 2019-04-16
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多