确定是否是首次使用Android应用


112

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


1
刚开始制作应用程序时,我只是在考虑安装应用程序后的首次运行。后来我意识到,升级后我还需要处理和区分首次运行。@以下schnatterer的答案,我的答案在这里展示了如何做到这一点。请谨慎对待未考虑升级的答案。
Suragch

@Suragch的行为是不考虑升级的不正确做法,但在某些情况下,例如您不想进行应用介绍:)
creativecreatoror

@creativecreatoror可能不是这样。有时您只关心初始安装,而不关心后续升级。对于这些情况,一个简单的布尔值就足够了。但是,如果将来您想在当前用户中为您在上次更新中添加的所有新功能添加不同的介绍该怎么办?在我看来,检查版本号比布尔值更具有远见。至少这为您将来提供了一种选择,以一种方式进行新安装并以另一种方式进行升级。
Suragch

1
然后,您只需为该版本添加即可,但我得到了赞扬
creativecreatorormaybenot

Answers:


56

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


3
请注意,这种方法无法在装有Android Froyo的Samsung Galaxy S上使用。这是因为SharedPreferences保存中的错误。这是对此问题的链接:stackoverflow.com/questions/7296163/…,这是Google代码上的票据:code.google.com/p/android/issues/detail?id=14359
Francesco Rigoni

4
请注意,默认情况下,请启用Android 6.0(API 23-棉花糖)或更高版本的自动备份功能(developer.android.com/guide/topics/data/autobackup.html)。如果用户先卸载然后重新安装该应用程序,则共享的首选项将被恢复。因此,在重新安装时,如果有任何问题,您将无法检查它是否在重新安装后首次运行。
艾伦(Alan)

1
@Alan,您是对的,此答案在Android Marshmallow中不再有效
艾奥恩·沙尔瓦兹

1
@Alan,您无法想象我正在寻找您这样的答案多长时间。你让我今天一整天都感觉很好。谢谢!
安东尼奥

@Alan但是自动备份也会保存大多数其他数据。因此,预计重新安装的应用程序将处于非首次运行状态。并且用户之前已经使用过该应用程序,因此无需指导。因此,我认为在大多数情况下,这是一件好事。
smdufb

112

您可以使用SharedPreferences来确定它是否是应用程序的“首次”启动。只需使用布尔变量(“ my_first_time”)并将其值更改为false在“首次”任务结束时将。

这是我的代码,可以在您第一次打开应用程序时使用:

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

17
它会在应用程序何时更新到Google Play商店的下一个版本时处理吗?
Shajeel Afzal 2014年

4
共享首选项在升级期间维护。因此,我假设从PlayStore升级时可以使用旧值。实际上,它也适用于其他方法,即也检查文件的存在。因此,在这种情况下,快捷方式是使用不同的首选项/文件名或值。
Tejasvi Hegde 2014年

@ShajeelAfzal这样的事情可能会帮助您公开void CheckAndInitAppFirstTime(){final String PREFS_NAME =“ TheAppVer”; 最后的字符串CHECK_VERSION =“ 1”; //需要版本号... final String KEY_NAME =“ CheckVersion”; SharedPreferences设置= getSharedPreferences(PREFS_NAME,0); 如果(!settings.getString(KEY_NAME,“ 0”)。equals(CHECK_VERSION)){//首次启动该应用程序,则执行某项操作或CHECK_VERSION有所不同// ... settings.edit()。putString( KEY_NAME,CHECK_VERSION).commit(); }
Tejasvi Hegde 2014年

@aman verma:按照developer.android.com/reference/android/content/…上的getBoolean描述进行操作。如果第一个参数没有退出,则getBoolean的第二个参数是默认值,因此如果未设置“ my_first_time”该表达式默认为true。
user2798692

62

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

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

/**
 * 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)。对编写测试感兴趣吗?:)

请注意,以上代码仅在您不搞乱android:versionCodeAndroidManifest.xml中的情况下才能正常工作!


2
请说明如何使用此方法。在哪里初始化SharedPreferences对象?
Shajeel Afzal 2014年

1
不适用于我-它总是启动我的第一次教程
pzo 2014年

1
这段代码更加简单明了,而没有在其他地方声明上下文和首选项的副作用,这public AppStart checkAppStart(Context context, SharedPreferences sharedPreferences)是一种更好的方法签名
2014年

2
在这里做了一个更新的要点gist.github.com/williscool/2a57bcd47a206e980eee我的原始代码有一个问题,因为它永远不会卡在我的演练循环中,因为在第一个checkAppStart块中从未重新计算过版本号。因此,我决定分享更新后的代码,看看是否有人对此提出建议
2014年

1
@将感谢您的输入。没错,代码可以简化并变得更健壮。当我第一次发布答案时,我从一个更复杂的场景中提取了代码,我想AppStart从不同的活动中访问它。因此,我将逻辑放在单独的服务方法中。这就是为什么存在一个context变量并将AppStart其存储在静态变量中以促进幂等方法调用的原因。
schnatterer 2014年

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:它只启动一次,但不是该版本,即它是一个更新。

3

您可以使用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;
    }

}

现在打开您的活动并初始化

 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
 }

2

这是一些代码-

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

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

if (!exists) {
    doSomething();                                      
}
else {
    doSomethingElse();
}

1

您可以简单地检查是否存在一个空文件(如果不存在),然后执行代码并创建该文件。

例如

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

我当时正在考虑这样做,但认为必须有更好的方法
Boardy 2011年

我什么都不知道,但是您缺少的资源是什么呢?文件的4个字节,开头是一个“ if”。系统例程将执行相同的操作,它们将执行完全相同的操作或使用已启动的应用程序创建表
MechMK1 2011年

您可以以类似的方式使用sharedpreferences,如果不存在该参数,则会显示启动屏幕等...并仅在程序首次运行时创建它(检查后为obv)。参见上述凯文的答案
Stealthcopter 2011年

1

我做了一个简单的类来检查您的代码是否是第一次/ 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


1

支持库版本23.3.0(在v4中表示支持Android 1.6的兼容性)仅对此提供支持。

在启动器活动中,首先调用:

AppLaunchChecker.onActivityCreate(activity);

然后致电:

AppLaunchChecker.hasStartedFromLauncher(activity);

如果这是应用程序的首次启动,它将返回。


一旦调用AppLaunchChecker.onActivityCreate(),这些调用的顺序必须颠倒,AppLaunchChecker.hasStartedFromLauncher()将返回true。
加里·基普尼斯

这是相当误导的。它没有说应用程序是否“曾经启动过”;而是说该应用是否“由用户从启动器启动”。因此,其他应用程序或深层链接可能已经启动了该应用程序。
Farid

1

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

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

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

1

对于科特林

    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
MMG

0

为什么不使用数据库助手?这将具有一个不错的onCreate,仅在首次启动应用程序时才调用。这将帮助那些想要在安装了初始应用程序后进行跟踪而不跟踪的人。


这会创建数据库吗?如何在不创建实际数据库的情况下使用DatabaseHelper?我认为,onCreate()每个新版本都需要它。另外,会不会被认为是多余的或出于非预期目的使用某些东西?
ADTC

仅在首次安装应用程序时触发onCreate。当数据库版本增加时,将触发onUpdated。
洛特

好多余的话是一个苛刻的词:)-如果您可以选择ie。您的应用尚未上线,请设置一个SharedPrefs标志,并使用它来确定它是否是首次启动。我遇到了一个情况,该应用程序已经使用了一段时间,而我们使用的是数据库,因此onCreate对我来说是完美的选择。
洛特

0

我喜欢在共享首选项中添加一个“更新计数”。如果不存在(或默认为零),则这是我的应用程序的“首次使用”。

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


-1
    /**
     * @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);

-2

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

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


嗯,不要这样硬编码路径!如果您只是这样做Context.getSharedPreferences(),它将最终在同一个地方,除了它可以在任何地方使用
Takhion

同意您的意见:)
DropAndTrap's
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.