【问题标题】:Run code when Android app is closed/sent to background当 Android 应用程序关闭/发送到后台时运行代码
【发布时间】:2018-09-05 15:17:06
【问题描述】:

我有一个向网络服务发送一些数据的 Android 应用程序。我需要在应用程序关闭或发送到后台后立即发送此数据。但是我该如何完成呢?

我目前的解决方案是在我的家庭活动的 OnPause() 上运行它,但是无论用户在关闭应用程序时处于哪个活动上,我都需要运行它。这可能还是我必须添加所有活动的 OnPause 方法?

【问题讨论】:

  • 我认为 Service 执行长期操作,并在用户退出应用程序时启用。

标签: android


【解决方案1】:

在您决定使用下面的代码之前,请先检查此解决方案https://stackoverflow.com/a/5862048/1037294


要检查您的应用程序是否被发送到后台,您可以在应用程序中的每个活动上调用onPause()onStop() 上的此代码:

 /**
   * Checks if the application is being sent in the background (i.e behind
   * another application's Activity).
   * 
   * @param context the context
   * @return <code>true</code> if another application will be above this one.
   */
  public static boolean isApplicationSentToBackground(final Context context) {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
      ComponentName topActivity = tasks.get(0).topActivity;
      if (!topActivity.getPackageName().equals(context.getPackageName())) {
        return true;
      }
    }

    return false;
  }

为此,您应该将其包含在您的AndroidManifest.xml

<uses-permission android:name="android.permission.GET_TASKS" />

【讨论】:

  • 难道没有允许接收者这样做的事件吗?
  • 这里有问题。这并不能保证应用程序在后台。该应用程序可能仍在运行,因此 am.getRunningTasks() 可以将您的应用程序作为正在运行的应用程序返回。在这种情况下,此方法将返回 false。从他们的文档中:请注意,“正在运行”并不意味着当前已加载任何任务的代码或活动-该任务可能已被系统冻结,因此下次进入前台时可以以之前的状态重新启动它.更多信息:developer.android.com/reference/android/app/…, int)
  • 我通过调用onStop()中的代码解决了上面提到的问题
  • 请注意,建议的解决方案存在一些问题。看到这个:stackoverflow.com/a/5862048/1037294
  • API 调用 getRunningTasks 已弃用
【解决方案2】:

这是我使用的方法,似乎效果很好:

我有一个我自己的顶级应用程序类,它扩展了应用程序

public class MyApplication extends Application implements Application.ActivityLifecycleCallbacks {

您还需要在清单文件中注册此应用程序对象:

<application android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:name=".MyApplication">

请注意我还如何实现 ActivityLifeCycleCallbacks 接口。该接口有以下方法:

public static interface ActivityLifecycleCallbacks {
    void onActivityCreated(android.app.Activity activity, android.os.Bundle bundle);

    void onActivityStarted(android.app.Activity activity);

    void onActivityResumed(android.app.Activity activity);

    void onActivityPaused(android.app.Activity activity);

    void onActivityStopped(android.app.Activity activity);

    void onActivitySaveInstanceState(android.app.Activity activity, android.os.Bundle bundle);

    void onActivityDestroyed(android.app.Activity activity);
}

您需要实现这些方法,然后在您的应用程序 onCreate() 中注册这些事件,如下所示

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(this);
}

这将在活动生命周期方法发生时调用回调(MyApplication 对象),例如 onCreate()、onPause 等。 在您的 onActivityPaused() 中,您可以通过调用 @peceps 方法检查应用程序是否在后台:isApplicationSentToBackground(...)

这就是我的代码的样子...

/**
 * Application.ActivityLifecycleCallbacks methods
 */
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {

}

@Override
public void onActivityStarted(Activity activity) {
}

@Override
public void onActivityResumed(Activity activity) {
}

@Override
public void onActivityStopped(Activity activity) {
    try {
        boolean foreground = new ForegroundCheckTask().execute(getApplicationContext()).get();
        if(!foreground) {
            //App is in Background - do what you want
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}

@Override
public void onActivityPaused(Activity activity) {
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}

@Override
public void onActivityDestroyed(Activity activity) {
}

创建一个新类来进行前台检查(这是一个异步任务)。请参阅check android application is in foreground or not? 了解更多信息。

class ForegroundCheckTask extends AsyncTask<Context, Void, Boolean> {
    @Override
    protected Boolean doInBackground(Context... params) {
        final Context context = params[0];
        return isAppOnForeground(context);
    }

    private boolean isAppOnForeground(Context context) {
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
        if (appProcesses == null) {
            return false;
        }
        final String packageName = context.getPackageName();
        for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
            if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
                return true;
            }
        }
        return false;
    }
}

【讨论】:

  • 您仍然需要清单中的
  • 可从 API 14 获得 - 很好的解决方案
  • 很好的解决方案!当应用程序进入后台(onPause)时,我需要获取活动,这就像一个魅力。谢谢
【解决方案3】:

编辑

此答案仅用于一个目的,即在onPause() 中为所有活动运行代码。当您的应用被发送到后台时,它不允许您运行代码。

原答案

创建一个名为 YourBasicActivity 的 Activity 并覆盖其 onPause() 方法并从 YourBasicActivity 扩展每个 Activity

【讨论】:

  • 嗯。这是一个很好的建议 :) 但我的应用中有许多不同的活动.. 例如 ListActivity.. 那我该怎么做呢?
  • 在这种情况下,您将不得不编写 YourListActivity,因为它是“Activity”的特殊形式。(如果您使用这么多 ListActivity。)
  • 但是当我们从一个活动切换到另一个活动时也会调用 onPause(),而不仅仅是在应用程序关闭或发送到后台时。
  • @peceps 你太老套了,只有当用户关闭应用程序时我才能使用哪个功能?
  • @Herter 你能解决这个问题吗?如果可以,请告诉我们怎么做?
【解决方案4】:

也许这会有所帮助,告诉我它是否对你有用。 仅当您从后台返回时,活动的值才会为 0(零) 当 onRestart() 被执行。

public class FatherClass extends Activity {

private static int activities = 0;

public void onCreate(Bundle savedInstanceState, String clase) {
    super.onCreate(savedInstanceState);
}

protected void onRestart()
{
    super.onRestart();
    if(activities == 0){
        Log.i("APP","BACK FROM BACKGROUND");
    }
}

protected void onStop(){
    super.onStop();
    activities = activities - 1;
}

protected void onStart(){
    super.onStart();
    activities = activities + 1;
}

}

您的所有类都必须从此类扩展才能使其工作。

说明onStart在activity“可见”时执行,onStop在activity“不可见”时执行。因此,当您的应用程序(它说应用程序不是活动)进入后台时,所有活动都是“不可见的”,因此它们执行 onStop 方法,所以这背后的想法是每次活动时 添加一个开始,并且每次隐藏活动时 SUBTRACT ONE,因此如果变量“活动”的值为零,则意味着在某个时间点开始的所有活动现在都不可见,所以当你的APP从后台返回并在“前台”的activity上执行onRestart方法,你可以检查是来自后台还是只是重新启动一个activity。

【讨论】:

  • 如果应用程序只能有 1 个主要活动,则无法正常工作。
  • @Dmitry 你有什么问题?这应该适用于最常见的问题,并不完美,但应该适用于大多数人。非常复杂的应用可能需要更复杂的方法。
  • 代码的问题是,如果我更改onStop 上的视图,那么当我返回应用程序时,我仍然可以看到视图的未更改内容。这很奇怪,但这正是发生的事情。如果我改用onPause / onResume,那么代码会认为应用程序在后台,而不是在后台。
  • 但是由于onPause / onResume 的问题,我可以假设onStop / onStart 在Android 更新后也会失败。或者它可能会在快速设备上失败,例如。
  • @Dmitry 抱歉花了这么长时间回复,抱歉我不明白你的情况,你的意思是只有一个主要活动?你的意思是你通过片段做所有事情?如果是这种情况,它应该可以工作,我不明白的是,当您说“更改视图”时,您是在谈论视图还是什么?如果您在 pastebin 中发布一些代码或我可以更好地帮助您的东西,那么数百万人使用的一些 Microsoft 应用程序已经使用了一种非常相似的方法,并且正在按预期工作。
【解决方案5】:

您可以使用 onAppForegroundStateChange() 方法,该方法在应用程序打开和关闭时调用。此方法仅在您的应用程序进入前台/后台时调用。 onAppForegroundStateChange() 方法比使用 onPause() 方法更好,因为每次进入其他活动时也会调用 onPause 方法。

你可以这样用这个方法

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        AppForegroundStateManager.getInstance().addListener(this);
    }

    @Override
    public void onAppForegroundStateChange(AppForegroundStateManager.AppForegroundState newState) {
        if (AppForegroundStateManager.AppForegroundState.IN_FOREGROUND == newState) {
            // App just entered the foreground. Do something here!
        } else {
            // App just entered the background. Do something here!
        }
    }
}

【讨论】:

  • 听起来不错,但我在 AppForegroundStateManager 类中也找不到此方法
  • 这是代码的来源,github.com/ytjojo/androidBaseLib/blob/master/androidBaseLib/src/…。你应该在 Application 中实现 OnAppForegroundStateChangeListener
  • 你应该在每个活动onPause()onResume()中调用onActiviyVisible(activity)onActiviyNotVisible(activity)
【解决方案6】:

覆盖 Home 活动的 onStop() 方法并在那里运行代码。

【讨论】:

    【解决方案7】:

    我认为您需要运行自己的线程,该线程将检查所有正在运行的活动是在后台运行还是被销毁。

    MyBasicActivity extends Activity
    {
        private static ArrayList<MyBasicActivity> activities=new ArrayList<MyBasicActivities);
        private boolean started;
    
        public void onCreate()
        {
           activities.add(this);
        } 
    
        public void onDestroy()
        {
           activities.remove(this);
        } 
    
        public void onStart()
        {
           this.started=true;
        }
    
        public void onPause()
        {
           this.started=false;
        }
    
        public boolean isStarted()
        {
           return started;
        }
    }
    
    MyThread implements Runnable
    {
        private ArrayList<MyBasicActivity> activities;
    
        public MyThread(ArrayList<MyBasicActivity> activities) 
        {
            this.activities=activities;
        }
    
        void run()
        {
              while(!stopped)
              {
                  boolean inBackground=true;
                  for(MyBasicActivity activity:activities)
                  {
                     if(activity.isStarted())
                     {
                          inBackground=false;
                          break;
                     }
                  }
                  if(inBackground)
                      //run your code here;
                  sleep(10000); //10 secs
              }
    
        }
    }
    

    【讨论】:

      【解决方案8】:

      如果您尝试提交/保存用户输入的数据,有比在他尝试关闭应用时执行此操作更好的方法。有很多方法可以关闭应用程序。用户甚至可以关闭电话。所以很难对所有这些都采取预防措施。

      我建议您在每次用户停止写入时提交数据,每隔几秒或当他按下按钮时(例如,如果您对网络服务的调用太慢)。

      这样更安全,也更容易实施。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-31
        • 1970-01-01
        • 2021-11-22
        • 2014-05-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多