Интересно, что ни один из ответов на этой странице не упоминает два крайних случая, надеюсь, никто не возражает, если я их добавлю:
Родовые словари в .NET не являются потокобезопасными, а иногда могут бросать NullReference
или даже (чаще) a KeyNotFoundException
при попытке получить доступ к ключу из двух параллельных потоков. Исключение в этом случае является довольно ошибочным.
Если код NullReferenceException
задан кодом unsafe
, вы можете посмотреть на переменные указателя , и проверьте их на IntPtr.Zero
или что-то в этом роде. Это одно и то же («исключение нулевого указателя»), но в небезопасном коде переменные часто переводятся в типы значений / массивы и т. Д., И вы ударяете головой о стену, задаваясь вопросом, как тип значения может исключение.
(Еще одна причина для небезопасного использования небезопасного кода, если вам это нужно)
SharedPreferences кажется уродливым решением для меня. Это гораздо более удобно, когда вы используете конструктор приложений для таких целей.
Все, что вам нужно, это использовать свой собственный класс приложения, а не по умолчанию.
public class MyApp extends Application {
public MyApp() {
// this method fires only once per application start.
// getApplicationContext returns null here
Log.i("main", "Constructor fired");
}
@Override
public void onCreate() {
super.onCreate();
// this method fires once as well as constructor
// but also application has context here
Log.i("main", "onCreate fired");
}
}
Затем вы должны зарегистрировать этот класс как ваш класс приложения в AndroidManifest.xml
<application android:label="@string/app_name" android:name=".MyApp"> <------- here
<activity android:name="MyActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
Вы даже можете нажать кнопку «Назад», поэтому приложение перейдет на задний план и не будет тратить ресурсы процессора, только ресурс памяти, а затем вы можете запустить его снова и конструктор все еще не горит, так как приложение еще не закончено.
Вы можете очистить память в диспетчере задач, чтобы все приложения были закрыты, а затем перезапустите приложение, чтобы убедиться, что ваш код инициализации снова запущен.
Я просто решил сделать это сам, я повторно открываю свое основное действие несколько раз во время исполнения приложения. Хотя конструктор является допустимым подходом к некоторым вещам, он не позволяет вам получить доступ к текущему контексту приложения, чтобы писать тосты между прочим.
Мое решение состояло в том, чтобы создать простой булевский набор firstRun для true в классе моего MainActivity, оттуда я запустил содержимое инструкции if, а затем установил его в true. Пример:
public class MainActivity extends AppCompatActivity
{
private static boolean firstRun = true;
@Override
protected void onCreate(Bundle savedInstanceState)
{
if(firstRun)
{
Toast.makeText(getApplicationContext(), "FIRST RUN", Toast.LENGTH_SHORT).show();
//YOUR FIRST RUN CODE HERE
}
firstRun = false;
super.onCreate(savedInstanceState);
//THE REST OF YOUR CODE
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (!prefs.getBoolean("onlyonce", false)) {
// <---- run your one time code here
// mark once runned.
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("onlyonce", true);
editor.commit();
}
}
Подход с общими предпочтениями бесполезен, и класс приложения не имеет доступа к активности.
Другой вариант, который я использовал, - иметь экземпляр сохраненного фрагмента и в этом случае намного больше материал может быть выполнен, особенно если вам нужен доступ к основному пользовательскому интерфейсу.
В этом примере я использовал асинтекс в сохраненном фрагменте. Мой AsyncTask имеет обратные вызовы родительской активности. Он гарантированно запускается только один раз для каждого приложения, потому что фрагмент никогда не уничтожается - воссоздается, когда одна и та же деятельность уничтожается - воссоздана. Это сохраняемый фрагмент.
public class StartupTaskFragment extends Fragment {
public interface Callbacks {
void onPreExecute();
void onProgressUpdate(int percent);
void onCancelled();
void onPostExecute();
}
public static final String TAG = "startup_task_fragment";
private Callbacks mCallbacks;
private StartupTask mTask;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (Callbacks) activity;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true); // this keeps fragment in memory even if parent activity is destroyed
mTask = new StartupTask();
mTask.execute();
}
@Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
private class StartupTask extends AsyncTask<Void, Integer, Void> {
@Override
protected void onPreExecute() {
if (mCallbacks != null) {
mCallbacks.onPreExecute();
}
}
@Override
protected Void doInBackground(Void... ignore) {
// do stuff here
return null;
}
@Override
protected void onProgressUpdate(Integer... percent) {
if (mCallbacks != null) {
mCallbacks.onProgressUpdate(percent[0]);
}
}
@Override
protected void onCancelled() {
if (mCallbacks != null) {
mCallbacks.onCancelled();
}
}
@Override
protected void onPostExecute(Void ignore) {
if (mCallbacks != null) {
mCallbacks.onPostExecute();
}
}
}
}
Затем в основном (или родительском) действии, где вы хотите, чтобы этот фрагмент запуска запуска выполнялся один раз.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
StartupTaskFragment st = (StartupTaskFragment) fm.findFragmentByTag(StartupTaskFragment.TAG);
if(st == null) {
fm.beginTransaction().add(mStartupTaskFragment = new StartupTaskFragment(), StartupTaskFragment.TAG).commit();
}
...
}
Идеи для сохраненного фрагмента пришел отсюда: http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html . Я просто вычислил его другие варианты использования, кроме изменений конфигурации.
try {
boolean firstboot = getSharedPreferences("BOOT_PREF",MODE_PRIVATE)
.getBoolean("firstboot", true);
if(firstboot){
//place your code that will run single time
getSharedPreferences("BOOT_PREF",MODE_PRIVATE).edit().
putBoolean("firstboot", false)
.commit();
}
Да, вы можете это сделать, используя концепцию Android для SharedPrefernce. Просто создайте булевский флаг и сохраните его в SharedPrefernce и проверьте его значение в методе onCreate ().
Я делаю это так же, как описано в другом ответе. У меня просто есть глобальная переменная в первом действии, которая соответствует номеру выпуска из манифеста. Я увеличиваю его для каждого обновления и когда чек видит большее число, он выполняет одноразовый код.
В случае успеха он записывает новый номер в общие настройки, поэтому он не будет делать это снова до следующего upgrade.
Убедитесь, что вы назначили значение по умолчанию -1, когда вы извлекаете версию из общих настроек, чтобы вы снова ошиблись на стороне запуска кода, а не запустили его и не имели обновления для своего приложения правильно.
Используйте SharedPreference для этого -
вы можете использовать для этого значение счетчика или логическое значение.
Вот документ SharedPreference:
http: //developer.android.com/reference/android/content/SharedPreferences.html
похоже, что вам, возможно, придется сделать что-то вроде этого
PackageInfo info = getPackageManager().getPackageInfo(PACKAGE_NAME, 0);
int currentVersion = info.versionCode;
this.versionName = info.versionName;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
int lastVersion = prefs.getInt("version_code", 0);
if (currentVersion > lastVersion) {
prefs.edit().putInt("version_code", currentVersion).commit();
// do the activity that u would like to do once here.
}
Вы можете делать это каждый раз, чтобы проверить, обновлено ли приложение, поэтому он запускается только один раз для обновления приложения