【问题标题】:How can I execute something just once per application start?如何在每个应用程序启动时只执行一次?
【发布时间】:2011-11-13 17:18:42
【问题描述】:

我想在应用程序中实现一个更新检查器,我显然只需要在您启动应用程序时显示一次。如果我在onCreate()onStart() 方法中调用,每次创建活动时都会显示,这不是一个可行的解决方案。

所以我的问题是:有没有办法做某事,比如检查更新,每次应用程序启动/启动一次?

如果有点难以理解,我很抱歉,我在解释这一点时遇到了困难。

【问题讨论】:

    标签: android android-3.0-honeycomb auto-update


    【解决方案1】:

    SharedPreferences 对我来说似乎是丑陋的解决方案。当您将应用程序构造函数用于此类目的时,它会更加简洁。

    您只需要使用自己的 Application 类,而不是默认的。

    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>
    

    你甚至可以按下后退按钮,让应用程序进入后台,不会浪费你的处理器资源,只浪费内存资源,然后你可以再次启动它,构造函数仍然没有触发,因为应用程序还没有完成。

    您可以在任务管理器中清除内存,这样所有应用程序都将关闭,然后重新启动您的应用程序,以确保您的初始化代码再次触发。

    【讨论】:

    • +1 这是真正的解决方案,SharedPreferences 让一切变得复杂,让事情变得一团糟!
    • 这也不是真正的解决方案。当您从应用程序管理器执行“清除数据”时,构造函数将再次执行。代码应仅在重新安装应用程序或新安装时运行,而不是在执行清除数据操作时运行:'(
    • @Rauf 抱歉,目前无法测试,但“清除数据”不会关闭应用程序吗?
    • 清除数据和卸载之间几乎没有区别..清除数据使应用程序恢复到新安装时的状态..即它只是删除了与之相关的所有数据..但我需要赶上新的安装
    • @Rauf 这个主题是关于应用程序的启动,而不是应用程序的安装。
    【解决方案2】:

    看起来你可能不得不做这样的事情

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

    您可以每次都这样做,以检查应用是否已升级,因此它只运行一次以进行应用升级

    【讨论】:

      【解决方案3】:

      共享首选项方法很混乱,应用程序类无法访问活动。

      我使用的另一种选择是保留片段实例,在该实例中,可以完成更多工作,尤其是在您需要访问主活动 UI 时。

      对于这个例子,我在保留片段中使用了 asynctask。我的 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。我只是想出了它除了配置更改之外的其他用途。

      【讨论】:

      • 当您的启动任务需要比创建应用程序时可用的更多信息时,这非常有用。就我而言,我需要用户在运行启动任务之前登录,所以这种方法对我来说效果更好。
      【解决方案4】:

      是的,您可以使用 Android 的 SharedPrefernce 概念来做到这一点。只需创建一个布尔标志并将其保存在 SharedPrefernce 中并在您的 onCreate() 方法中检查其值。

      【讨论】:

      • 唯一的问题是当您退出应用程序时,SharedPreference 没有被清除。幸运的是,我的应用程序包含一个启动屏幕,可用于在每次启动应用程序时重置 SharedPreference。不知道为什么我以前没有想到这一点-感谢您朝着正确的方向前进:-)
      • 所以这不是每次应用升级一次,而是每次启动一次?
      • 是的,你是绝对正确的......你可以做的另一件事是,每当你开始你的活动时,你已经编写了更新检查器的代码,你可以使用 intent.setFlag(FLAG_ACTIVITY_REORDER_TO_FRONT ) 以便不会再次调用 onCreate()。
      • 如果您需要一次应用升级,请查看我的答案。
      • 我基本上只是最终使用 Intent extras 和初始屏幕中的布尔值来判断它是否正在启动(如果来自初始屏幕,则为 true,否则为 false)。效果很好。
      【解决方案5】:
       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();
        }
      }
      

      【讨论】:

        【解决方案6】:

        我这样做的方式与其他答案中描述的方式相同。我在第一个活动中只有一个全局变量,它与清单中的版本号相匹配。每次升级时我都会增加它,当检查发现更高的数字时,它会执行一次性代码。

        如果成功,它将新号码写入共享首选项,因此在下次升级之前不会再次这样做。

        确保在从共享首选项中检索版本时将默认值指定为 -1,以便在再次运行代码时出错,而不是不运行代码并且无法正确更新应用。

        【讨论】:

        • 因为我认为所有具有共享首选项的解决方案最终都是混乱的,当您有用于此类目的的应用程序构造函数时。我对所有带有 SharedPreferences 的答案投了反对票,如果它能让你感觉好一点:)
        【解决方案7】:

        为此使用 SharedPreference-

        1. 如果您在应用处于活动状态后没有再次重新启动启动器活动,那么在这种情况下,您可以使用它。

        2. 如果您在应用程序中实现它,请在启动画面中使用它。

        3. 如果您没有使用任何启动屏幕,那么您需要创建一个没有设置视图的 Activity,并且在它的 oncreate 调用上您可以启动更新并启动您的主要 Activity。

        您可以为此使用计数器值或布尔值。

        这里是 SharedPreference 文档:

        http://developer.android.com/reference/android/content/SharedPreferences.html

        【讨论】:

          【解决方案8】:
                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();
          
          
                              }
          

          【讨论】:

            【解决方案9】:

            我自己解决了这个问题,我在整个应用程序执行过程中多次重新打开我的主要活动。虽然构造函数对于某些事情来说是一种有效的方法,但它不允许您访问当前的应用程序上下文来写入 toast 以及其他事情

            我的解决方案是在我的 MainActivity 类中创建一个简单的“firstRun”布尔值设置为 true,然后我运行 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
            }
            

            【讨论】:

            • 这似乎不起作用。每当创建应用程序时,布尔属性就会再次重新初始化为 true。
            【解决方案10】:

            @Vitalii 的answer 继续。

            在设置好Application类之后,如果需要访问Activity,我们可以使用恰当命名的android库“Once”https://github.com/jonfinerty/Once

            在Application类的onCreate方法中

            Once.initialise(this)
            

            在Activity/Fragment类的onCreate/onViewCreated方法中。

            val helloTag = "hello"
            if (!Once.beenDone(Once.THIS_APP_SESSION, helloTag)) {
                //Do something that needs to be done only once
                Once.markDone(helloTag) //Mark it done
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-09-28
              • 1970-01-01
              • 2012-06-18
              • 1970-01-01
              • 1970-01-01
              • 2020-07-05
              相关资源
              最近更新 更多