确定是不是是第一次使用 Android 应用程序

Posted

技术标签:

【中文标题】确定是不是是第一次使用 Android 应用程序【英文标题】:Determine if Android app is being used for the first time确定是否是第一次使用 Android 应用程序 【发布时间】:2011-06-05 21:18:47 【问题描述】:

我目前正在开发一个安卓应用程序。我需要在第一次启动应用程序时做一些事情,即代码只在程序第一次启动时运行。

【问题讨论】:

当我第一次开始制作应用程序时,我只想到应用程序安装后的第一次运行。后来我意识到我还需要处理和区分升级后的首次运行。下面@schnatterer 的回答和我的回答here 展示了如何做到这一点。请注意不考虑升级的答案。 @Suragch 你表现得好像不考虑升级是不好的做法,但在某些情况下,比如有一个你不想做的应用介绍:) @creativecreatorormaybenot,这是真的。有时您只关心初始安装而不关心后续升级。对于这些情况,一个简单的布尔值就足够了。但是,如果将来某个时候您想为当前用户添加关于您在上次更新中刚刚添加的所有新功能的不同介绍怎么办?在我看来,检查版本号而不是布尔值更有远见。这至少让您在未来可以选择以一种方式进行新安装,另一种方式进行升级。 那你只为这个版本添加它,但我明白了 您可以查看***.com/a/7217834/2689076 【参考方案1】:

您可以使用 SharedPreferences 来确定是否是“第一次”启动应用。 当您的“第一次”任务结束时,只需使用一个布尔变量(“my_first_time”)并将其值更改为false。 p>

这是我第一次打开应用时捕获的代码:

final String PREFS_NAME = "MyPrefsFile";

SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);

if (settings.getBoolean("my_first_time", true)) 
    //the app is being launched for first time, do something        
    Log.d("Comments", "First time");

             // first time task

    // record the fact that the app has been started at least once
    settings.edit().putBoolean("my_first_time", false).commit(); 

【讨论】:

在 google play store 上更新应用到下一个版本时会处理吗? SharedPreferences 在升级期间得到维护。因此,我假设当它从 PlayStore 升级时,旧值可用。事实上,它也适用于其他方法,即检查文件是否存在。因此,在这种情况下,快捷方法是使用不同的首选项/文件名或值。 @ShajeelAfzal 这样的事情可能会帮助你 public void CheckAndInitAppFirstTime() final String PREFS_NAME = "TheAppVer";最终字符串 CHECK_VERSION = "1"; //必需的版本... final String KEY_NAME = "CheckVersion"; SharedPreferences 设置 = getSharedPreferences(PREFS_NAME, 0); if (!settings.getString(KEY_NAME, "0").equals(CHECK_VERSION)) //应用程序是第一次启动,做一些事情或 CHECK_VERSION 不同 //... settings.edit().putString( KEY_NAME, CHECK_VERSION).commit(); @aman verma:根据 developer.android.com/reference/android/content/… 的 getBoolean 描述,如果第一个参数不退出,getBoolean 的第二个参数是默认值,所以如果“my_first_time”没有设置表达式默认为真。【参考方案2】:

我建议不仅存储布尔标志,还存储完整的版本代码。 这样你也可以在开始时查询它是否是新版本中的第一次开始。例如,您可以使用此信息显示“最新消息”对话框。

以下代码应适用于任何“作为上下文”(活动、服务等)的 android 类。如果您希望将其放在单独的 (POJO) 类中,则可以考虑使用“静态上下文”,例如 here 所述。

/**
 * Distinguishes different kinds of app starts: <li>
 * <ul>
 * First start ever (@link #FIRST_TIME)
 * </ul>
 * <ul>
 * First start in this version (@link #FIRST_TIME_VERSION)
 * </ul>
 * <ul>
 * Normal app start (@link #NORMAL)
 * </ul>
 * 
 * @author schnatterer
 * 
 */
public enum AppStart 
    FIRST_TIME, FIRST_TIME_VERSION, NORMAL;


/**
 * The app version code (not the version name!) that was used on the last
 * start of the app.
 */
private static final String LAST_APP_VERSION = "last_app_version";

/**
 * Finds out started for the first time (ever or in the current version).<br/>
 * <br/>
 * Note: This method is <b>not idempotent</b> only the first call will
 * determine the proper result. Any subsequent calls will only return
 * @link AppStart#NORMAL until the app is started again. So you might want
 * to consider caching the result!
 * 
 * @return the type of app start
 */
public AppStart checkAppStart() 
    PackageInfo pInfo;
    SharedPreferences sharedPreferences = PreferenceManager
            .getDefaultSharedPreferences(this);
    AppStart appStart = AppStart.NORMAL;
    try 
        pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
        int lastVersionCode = sharedPreferences
                .getInt(LAST_APP_VERSION, -1);
        int currentVersionCode = pInfo.versionCode;
        appStart = checkAppStart(currentVersionCode, lastVersionCode);
        // Update version in preferences
        sharedPreferences.edit()
                .putInt(LAST_APP_VERSION, currentVersionCode).commit();
     catch (NameNotFoundException e) 
        Log.w(Constants.LOG,
                "Unable to determine current app version from pacakge manager. Defenisvely assuming normal app start.");
    
    return appStart;


public AppStart checkAppStart(int currentVersionCode, int lastVersionCode) 
    if (lastVersionCode == -1) 
        return AppStart.FIRST_TIME;
     else if (lastVersionCode < currentVersionCode) 
        return AppStart.FIRST_TIME_VERSION;
     else if (lastVersionCode > currentVersionCode) 
        Log.w(Constants.LOG, "Current version code (" + currentVersionCode
                + ") is less then the one recognized on last startup ("
                + lastVersionCode
                + "). Defenisvely assuming normal app start.");
        return AppStart.NORMAL;
     else 
        return AppStart.NORMAL;
    

它可以在这样的活动中使用:

public class MainActivity extends Activity         
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        switch (checkAppStart()) 
        case NORMAL:
            // We don't want to get on the user's nerves
            break;
        case FIRST_TIME_VERSION:
            // TODO show what's new
            break;
        case FIRST_TIME:
            // TODO show a tutorial
            break;
        default:
            break;
        

        // ...
    
    // ...

可以使用这个 JUnit 测试来验证基本逻辑:

public void testCheckAppStart() 
    // First start
    int oldVersion = -1;
    int newVersion = 1;
    assertEquals("Unexpected result", AppStart.FIRST_TIME,
            service.checkAppStart(newVersion, oldVersion));

    // First start this version
    oldVersion = 1;
    newVersion = 2;
    assertEquals("Unexpected result", AppStart.FIRST_TIME_VERSION,
            service.checkAppStart(newVersion, oldVersion));

    // Normal start
    oldVersion = 2;
    newVersion = 2;
    assertEquals("Unexpected result", AppStart.NORMAL,
            service.checkAppStart(newVersion, oldVersion));

再多一点努力,您可能也可以测试与 android 相关的东西(PackageManager 和 SharedPreferences)。 有人有兴趣写测试吗? :)

请注意,上述代码只有在您不乱用 AndroidManifest.xml 中的 android:versionCode 时才能正常工作!

【讨论】:

请说明如何使用此方法。您在哪里初始化 SharedPreferences 对象? 对我不起作用 - 它总是启动我的第一次教程 这段代码更直接,没有在别处声明上下文和偏好的副作用public AppStart checkAppStart(Context context, SharedPreferences sharedPreferences)是一个更好的方法签名 在这里更新了这个答案的要点gist.github.com/williscool/2a57bcd47a206e980eee我遇到了原始代码的问题,它会永远卡在我的演练循环中,因为版本号从未在第一个 @987654329 中重新计算@ 堵塞。所以我决定分享我更新的代码,看看是否有人对此有建议 @Will 感谢您的意见。你是对的,代码可以简化并变得更健壮。当我第一次发布答案时,我从a more complex scenario 中提取了代码,我想从不同的活动中访问AppStart。所以我把逻辑放在一个单独的服务方法中。这就是为什么有一个context 变量和AppStart 存储在一个静态变量中以方便幂等方法调用的原因。【参考方案3】:

另一个想法是使用共享首选项中的设置。与检查空文件的一般想法相同,但是您没有空文件浮动,不用于存储任何内容

【讨论】:

请注意,这种方法不适用于装有 Android Froyo 的三星 Galaxy S。这是因为 SharedPreferences 保存中的一个错误。这是一个 SO 问题的链接:***.com/questions/7296163/…,这是谷歌代码上的票:code.google.com/p/android/issues/detail?id=14359 注意 Android 6.0 (API 23 - Marshmallow) 或更高版本的自动备份(developer.android.com/guide/topics/data/autobackup.html)is 默认启用。如果用户卸载然后重新安装应用程序,共享首选项将被恢复。所以重新安装你如果有任何问题,则无法检查它是否在重新安装后第一次运行。 @Alan 你是对的,这个答案不再有效来自 Android Marshmallow。 @Alan 你无法想象我要多久才能找到像你这样的答案。你让我今天一整天都感觉很好。谢谢! @Alan 但是自动备份还可以保存大多数其他数据。因此,重新安装的应用程序可能会处于非首次运行状态。并且用户之前已经使用过该应用程序,因此无需指导。所以我认为在大多数情况下,发生这种情况是件好事。【参考方案4】:

我解决了确定该应用程序是否是您的第一次,这取决于它是否是更新。

private int appGetFirstTimeRun() 
    //Check if App Start First Time
    SharedPreferences appPreferences = getSharedPreferences("MyAPP", 0);
    int appCurrentBuildVersion = BuildConfig.VERSION_CODE;
    int appLastBuildVersion = appPreferences.getInt("app_first_time", 0);

    //Log.d("appPreferences", "app_first_time = " + appLastBuildVersion);

    if (appLastBuildVersion == appCurrentBuildVersion ) 
        return 1; //ya has iniciado la appp alguna vez

     else 
        appPreferences.edit().putInt("app_first_time",
                appCurrentBuildVersion).apply();
        if (appLastBuildVersion == 0) 
            return 0; //es la primera vez
         else 
            return 2; //es una versión nueva
        
    

计算结果:

0:如果这是第一次。 1:它已经开始了。 2:已经启动过一次,但不是那个版本,即是更新。

【讨论】:

【参考方案5】:

您可以使用Android SharedPreferences

Android SharedPreferences 允许我们存储私有原语 键值对形式的应用数据。

代码

创建自定义类SharedPreference

 public class SharedPreference 

    android.content.SharedPreferences pref;
    android.content.SharedPreferences.Editor editor;
    Context _context;
    private static final String PREF_NAME = "testing";

    // All Shared Preferences Keys Declare as #public
    public static final String KEY_SET_APP_RUN_FIRST_TIME       =        "KEY_SET_APP_RUN_FIRST_TIME";


    public SharedPreference(Context context) // Constructor
    
        this._context = context;
        pref = _context.getSharedPreferences(PREF_NAME, 0);
        editor = pref.edit();

    

    /*
    *  Set Method Generally Store Data;
    *  Get Method Generally Retrieve Data ;
    * */


    public void setApp_runFirst(String App_runFirst)
    
        editor.remove(KEY_SET_APP_RUN_FIRST_TIME);
        editor.putString(KEY_SET_APP_RUN_FIRST_TIME, App_runFirst);
        editor.apply();
    

    public String getApp_runFirst()
    
        String  App_runFirst= pref.getString(KEY_SET_APP_RUN_FIRST_TIME, "FIRST");
        return  App_runFirst;
    


现在打开您的Activity & Initialize

 private     SharedPreference                sharedPreferenceObj; // Declare Global

现在在 OnCreate 部分中调用它

 sharedPreferenceObj=new SharedPreference(YourActivity.this);

正在检查

if(sharedPreferenceObj.getApp_runFirst().equals("FIRST"))
 
   // That's mean First Time Launch
   // After your Work , SET Status NO
   sharedPreferenceObj.setApp_runFirst("NO");
 
else
  
   // App is not First Time Launch
 

【讨论】:

【参考方案6】:

这里有一些代码 -

String path = Environment.getExternalStorageDirectory().getAbsolutePath() +
                    "/Android/data/myapp/files/myfile.txt";

boolean exists = (new File(path)).exists(); 

if (!exists) 
    doSomething();                                      

else 
    doSomethingElse();

【讨论】:

【参考方案7】:

在支持库修订版 23.3.0 中支持这一点(在 v4 中,这意味着可兼容回 Android 1.6)。

在您的 Launcher 活动中,首先调用:

AppLaunchChecker.onActivityCreate(activity);

然后调用:

AppLaunchChecker.hasStartedFromLauncher(activity);

如果这是第一次启动应用程序,将会返回。

【讨论】:

这些调用的顺序必须颠倒,一旦 AppLaunchChecker.onActivityCreate() 被调用,AppLaunchChecker.hasStartedFromLauncher() 将返回 true。 这是相当误导的。它没有说明应用程序是否“曾经启动”;相反,它表示该应用程序是否“曾经由用户从启动器启动”。因此,其他应用或深层链接可能已经启动了该应用。【参考方案8】:

如果您正在寻找一种简单的方法,这里就是。

像这样创建一个实用程序类,

public class ApplicationUtils 

  /**
  * Sets the boolean preference value
  *
  * @param context the current context
  * @param key     the preference key
  * @param value   the value to be set
  */
 public static void setBooleanPreferenceValue(Context context, String key, boolean value) 
     SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
     sp.edit().putBoolean(key, value).apply();
 

 /**
  * Get the boolean preference value from the SharedPreference
  *
  * @param context the current context
  * @param key     the preference key
  * @return the the preference value
  */
 public static boolean getBooleanPreferenceValue(Context context, String key) 
     SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
     return sp.getBoolean(key, false);
 


在您的主要活动中,onCreate()

if(!ApplicationUtils.getBooleanPreferenceValue(this,"isFirstTimeExecution"))
Log.d(TAG, "First time Execution");
ApplicationUtils.setBooleanPreferenceValue(this,"isFirstTimeExecution",true);
// do your first time execution stuff here,

【讨论】:

这是迄今为止最好的答案!【参考方案9】:

您可以简单地检查是否存在空文件,如果不存在,则执行您的代码并创建文件。

例如

if(File.Exists("emptyfile")
    //Your code here
    File.Create("emptyfile");

【讨论】:

我想这样做,但认为必须有更好的方法 我什么都不知道,但是您收到的资源不足是什么?文件有 4 个字节,开头有一个“if”。系统例程会做同样的事情,他们会做同样的事情,或者用已经启动的应用程序制作一个表格 以类似的方式,您可以使用 sharedpreferences,如果它不存在,则显示启动屏幕等...并在程序第一次运行时创建它(检查后 obv) .见上面凯文的回答【参考方案10】:

我做了一个简单的类来检查你的代码是否第一次/n次运行!

例子

创建独特的偏好

FirstTimePreference prefFirstTime = new FirstTimePreference(getApplicationContext());

使用 runTheFirstTime,选择一个键来检查你的事件

if (prefFirstTime.runTheFirstTime("myKey")) 
    Toast.makeText(this, "Test myKey & coutdown: " + prefFirstTime.getCountDown("myKey"),
                   Toast.LENGTH_LONG).show();

使用runTheFirstNTimes,选择一个键和执行多少次

if(prefFirstTime.runTheFirstNTimes("anotherKey" , 5)) 
    Toast.makeText(this, "ciccia Test coutdown: "+ prefFirstTime.getCountDown("anotherKey"),
                   Toast.LENGTH_LONG).show();

使用 getCountDown() 更好地处理您的代码

FirstTimePreference.java

【讨论】:

【参考方案11】:

科特林

    fun checkFirstRun() 

    var prefs_name = "MyPrefsFile"
    var pref_version_code_key = "version_code"
    var doesnt_exist: Int = -1;

    // Get current version code
    var currentVersionCode = BuildConfig.VERSION_CODE

    // Get saved version code
    var prefs: SharedPreferences = getSharedPreferences(prefs_name, MODE_PRIVATE)
    var savedVersionCode: Int = prefs.getInt(pref_version_code_key, doesnt_exist)

    // Check for first run or upgrade
    if (currentVersionCode == savedVersionCode) 

        // This is just a normal run
        return;

     else if (savedVersionCode == doesnt_exist) 

        // TODO This is a new install (or the user cleared the shared preferences)


     else if (currentVersionCode > savedVersionCode) 

        // TODO This is an upgrade
    

    // Update the shared preferences with the current version code
    prefs.edit().putInt(pref_version_code_key, currentVersionCode).apply();


【讨论】:

非常感谢您在 Kotlin 中给出答案【参考方案12】:

为什么不使用数据库助手?这将有一个很好的 onCreate ,它只在应用程序第一次启动时被调用。这将有助于那些希望在安装初始应用后不跟踪的人进行跟踪。

【讨论】:

这会创建数据库吗?如何在不创建实际数据库的情况下使用 DatabaseHelper?我认为,每个新版本都会调用onCreate()。另外,这不会被认为是多余的或将某些东西用于非预期目的吗? onCreate 仅在首次安装应用时触发。当数据库版本增加时,会触发 onUpdated。 那么多余是一个如此苛刻的词:) - 如果你有选择,即。你的应用还没有上线,然后设置一个 SharedPrefs 标志并使用它来确定它是否是第一次启动。我有一个案例,该应用程序已经流行了一段时间,而我们正在使用数据库,所以 onCreate 非常适合我。【参考方案13】:

我喜欢在我的共享偏好中设置“更新计数”。如果它不存在(或默认零值),那么这是我的应用的“首次使用”。

private static final int UPDATE_COUNT = 1;    // Increment this on major change
...
if (sp.getInt("updateCount", 0) == 0) 
    // first use
 else if (sp.getInt("updateCount", 0) < UPDATE_COUNT) 
    // Pop up dialog telling user about new features

...
sp.edit().putInt("updateCount", UPDATE_COUNT);

所以现在,只要有用户应该知道的应用更新,我就会增加 UPDATE_COUNT

【讨论】:

【参考方案14】:

我的 kotlin 版本如下所示:

PreferenceManager.getDefaultSharedPreferences(this).apply 
        // Check if we need to display our OnboardingSupportFragment
        if (!getBoolean("wasAppStartedPreviously", false)) 
            // The user hasn't seen the OnboardingSupportFragment yet, so show it
            startActivity(Intent(this@SplashScreenActivity, AppIntroActivity::class.java))
         else 
            startActivity(Intent(this@SplashScreenActivity, MainActivity::class.java))
        
    

【讨论】:

【参考方案15】:
    /**
     * @author ALGO
     */
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.util.UUID;

    import android.content.Context;

    public class Util 
        // ===========================================================
        //
        // ===========================================================

        private static final String INSTALLATION = "INSTALLATION";

        public synchronized static boolean isFirstLaunch(Context context) 
            String sID = null;
            boolean launchFlag = false;
            if (sID == null) 
                File installation = new File(context.getFilesDir(), INSTALLATION);
                try 
                    if (!installation.exists()) 

                        writeInstallationFile(installation);
                    
                    sID = readInstallationFile(installation);
launchFlag = true;
                 catch (Exception e) 
                    throw new RuntimeException(e);
                
            
            return launchFlag;
        

        private static String readInstallationFile(File installation) throws IOException 
            RandomAccessFile f = new RandomAccessFile(installation, "r");// read only mode
            byte[] bytes = new byte[(int) f.length()];
            f.readFully(bytes);
            f.close();

            return new String(bytes);
        

        private static void writeInstallationFile(File installation) throws IOException 
            FileOutputStream out = new FileOutputStream(installation);
            String id = UUID.randomUUID().toString();
            out.write(id.getBytes());
            out.close();
        
    

> Usage (in class extending android.app.Activity)

Util.isFirstLaunch(this);

【讨论】:

【参考方案16】:

大家好,我正在做这样的事情。它对我有用

在共享首选项中创建一个布尔字段。默认值为 true isFirstTime:true 第一次后将其设置为假。在android系统中没有比这更简单可靠的了。

【讨论】:

呃,不要这样硬编码路径!如果你只是简单地做Context.getSharedPreferences(),它会在同一个地方结束,除了它可以在任何地方工作 同意你的观点:)

以上是关于确定是不是是第一次使用 Android 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

CoreData - 如何使用 validateForDelete:确定是不是应删除托管对象

Android 如何确定应用在“最近的位置请求”下是不是存在“高电量使用”?

如何确定Android应用程序是不是被强制关闭?

Android GPS 查询位置数据不正确

如何解释 Excel 数字格式字符串以确定值是不是应由 DateTime.FromOADate 解析

使用intent和get方法时Android应用程序崩溃