我试图写一个应用程序,做一些具体的时候,它被带回前台后一段时间。是否有一种方法可以检测应用程序是被发送到后台还是被带到前台?
当前回答
我们可以使用LiveData扩展这个解决方案:
class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {
private var lifecycleListener: LifecycleObserver? = null
override fun onActive() {
super.onActive()
lifecycleListener = AppLifecycleListener().also {
ProcessLifecycleOwner.get().lifecycle.addObserver(it)
}
}
override fun onInactive() {
super.onInactive()
lifecycleListener?.let {
this.lifecycleListener = null
ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
}
}
internal inner class AppLifecycleListener : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
value = State.FOREGROUND
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() {
value = State.BACKGROUND
}
}
enum class State {
FOREGROUND, BACKGROUND
}
}
现在我们可以订阅这个LiveData并捕获所需的事件。例如:
appForegroundStateLiveData.observeForever { state ->
when(state) {
AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
}
}
其他回答
The principal problem is that you have to get an specific behavior when you start an activity from background. If you override your onPause() and onResume() methods, you'll have a close answer, but not the solution. The problem is that onPause() and onResume() methods are called even if you don't minimize your application, they can be called when you start an activity and later you press the back button to return to your activity. To eliminate that problem and to know really when your application comes from background, you must to get the running process and compare with your process:
private boolean isApplicationBroughtToBackground() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(getPackageName())) {
return true;
}
}
return false;
}
现在你必须声明一个布尔变量:
public boolean wasPaused = false;
并询问你的活动何时进入后台:
@Override
public void onPause(){
super.onPause();
if(isApplicationBroughtToBackground())
wasPaused = true;
}
现在,当你的活动再次出现在屏幕上时,在onResume()方法中询问:
@Override
public void onResume(){
super.onResume();
if(wasPaused){
lockScreen(true);
}
wasPaused = false;
}
就是这样。现在,当您的活动进入后台,稍后用户将其带到前台时,锁定屏幕将出现。
如果你想为你的应用程序的任何活动重复这个行为,你必须创建一个活动(可以是BaseActivity),放这个方法,你所有的活动都必须从BaseActivity继承。
我希望这对你有帮助。
问候!
没有直接的生命周期方法来告诉你整个应用程序何时进入后台或前台。
我用简单的方法做了这件事。按照下面的说明检测应用程序后台/前台阶段。
只要有一点变通办法,这是可能的。在这里,ActivityLifecycleCallbacks可以救场。让我一步一步来。
First, create a class that extends the android.app.Application and implements the ActivityLifecycleCallbacks interface. In the Application.onCreate(), register the callback. public class App extends Application implements Application.ActivityLifecycleCallbacks { @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(this); } } Register the “App” class in the Manifest as below, <application android:name=".App". There will be at least one Activity in the started state when the app is in the foreground and there will be no Activity in the started state when the app is in the background. Declare 2 variables as below in the “App” class. private int activityReferences = 0; private boolean isActivityChangingConfigurations = false; activityReferences will keep the count of number of activities in the started state. isActivityChangingConfigurations is a flag to indicate if the current Activity is going through configuration change like an orientation switch. Using the following code you can detect if the App comes foreground. @Override public void onActivityStarted(Activity activity) { if (++activityReferences == 1 && !isActivityChangingConfigurations) { // App enters foreground } } This is how to detect if the App goes background. @Override public void onActivityStopped(Activity activity) { isActivityChangingConfigurations = activity.isChangingConfigurations(); if (--activityReferences == 0 && !isActivityChangingConfigurations) { // App enters background } }
工作原理:
这是按顺序调用Lifecycle方法的一个小技巧。让我来演练一个场景。
假设用户启动了应用程序,启动器活动A被启动。生命周期调用将是,
A.onCreate () A.onStart() (++activityReferences == 1)(应用程序进入前台) A.onResume ()
现在活动A启动活动B。
A.onPause () B.onCreate () B.onStart() (++activityReferences == 2) B.onResume () A.onStop()(——activityReferences == 1)
然后用户从活动B导航回来,
B.onPause () A.onStart() (++activityReferences == 2) A.onResume () B.onStop()(——activityReferences == 1) B.onDestroy ()
然后用户按Home键,
A.onPause () A.onStop()(——activityReferences == 0)(应用程序进入后台)
在这种情况下,如果用户从活动B按Home键而不是后退键,它仍然是一样的,activityReferences将是0。因此,我们可以检测作为应用程序进入后台。
那么,isActivityChangingConfigurations的角色是什么?在上面的场景中,假设活动B改变了方向。回调序列将是,
B.onPause () B.onStop()(——activityReferences == 0)(应用程序进入后台??) B.onDestroy () B.onCreate () B.onStart() (++activityReferences == 1)(应用程序进入前台??) B.onResume ()
这就是为什么我们有一个额外的isActivityChangingConfigurations检查,以避免活动正在经历配置更改的场景。
这些答案似乎不正确。当另一个活动开始和结束时,也会调用这些方法。你能做的是保留一个全局标志(是的,全局不好:),并在每次启动一个新活动时将其设置为true。在每个活动的onCreate中将其设置为false。然后,在onPause中检查这个标志。如果为假,你的应用就会进入后台,或者被杀死。
我在Github app- prospect -background-listen上创建了一个项目
为应用程序中的所有Activity创建一个BaseActivity。
public class BaseActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
public static boolean isAppInFg = false;
public static boolean isScrInFg = false;
public static boolean isChangeScrFg = false;
@Override
protected void onStart() {
if (!isAppInFg) {
isAppInFg = true;
isChangeScrFg = false;
onAppStart();
}
else {
isChangeScrFg = true;
}
isScrInFg = true;
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
if (!isScrInFg || !isChangeScrFg) {
isAppInFg = false;
onAppPause();
}
isScrInFg = false;
}
public void onAppStart() {
// Remove this toast
Toast.makeText(getApplicationContext(), "App in foreground", Toast.LENGTH_LONG).show();
// Your code
}
public void onAppPause() {
// Remove this toast
Toast.makeText(getApplicationContext(), "App in background", Toast.LENGTH_LONG).show();
// Your code
}
}
现在使用这个BaseActivity作为一个超类的所有你的活动像MainActivity扩展BaseActivity和onAppStart将被调用时,你启动你的应用程序和onappause()将被调用时,应用程序从后台从任何屏幕。
2018: Android通过生命周期组件原生支持这一点。
2018年3月更新:现在有了更好的解决方案。看到ProcessLifecycleOwner。您将需要使用新的体系结构组件1.1.0(目前的最新版本),但它是专门为此设计的。
在这个答案中提供了一个简单的示例,但我写了一个示例应用程序和一篇关于它的博客文章。
自从2014年我写了这篇文章以来,出现了不同的解决方案。有些可以工作,有些被认为可以工作,但有缺陷(包括我的!),我们作为一个社区(Android)学会了忍受后果,并为特殊情况编写了变通方案。
永远不要假设一个代码片段就是你想要的解决方案,这是不可能的;更好的做法是,试着理解它是做什么的,以及它为什么这么做。
MemoryBoss类实际上从未被我使用过,它只是一段碰巧工作的伪代码。
除非有充分的理由不使用新的体系结构组件(确实有一些,特别是如果您的目标是超级老的api),否则就继续使用它们。它们远非完美,但ComponentCallbacks2也不是。
UPDATE / NOTES (November 2015): People has been making two comments, first is that >= should be used instead of == because the documentation states that you shouldn't check for exact values. This is fine for most cases, but bear in mind that if you only care about doing something when the app went to the background, you will have to use == and also combine it with another solution (like Activity Lifecycle callbacks), or you may not get your desired effect. The example (and this happened to me) is that if you want to lock your app with a password screen when it goes to the background (like 1Password if you're familiar with it), you may accidentally lock your app if you run low on memory and are suddenly testing for >= TRIM_MEMORY, because Android will trigger a LOW MEMORY call and that's higher than yours. So be careful how/what you test.
此外,有些人问过如何检测你什么时候回来。
下面解释了我能想到的最简单的方法,但由于有些人不熟悉它,所以我在这里添加了一些伪代码。假设你有YourApplication类和MemoryBoss类,在你的类BaseActivity扩展了Activity(如果你没有的话,你需要创建一个)。
@Override
protected void onStart() {
super.onStart();
if (mApplication.wasInBackground()) {
// HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
mApplication.setWasInBackground(false);
}
}
我推荐onStart,因为对话框可以暂停一个活动,所以我敢打赌,如果你所做的只是显示一个全屏对话框,你不希望你的应用程序认为“它进入了后台”,但你的里程可能会有所不同。
就这些。if块中的代码只会执行一次,即使你去了另一个活动,新的活动(也扩展了BaseActivity)将报告wasInBackground为false,所以它不会执行代码,直到onmemorytrim被调用并且标志被再次设置为true。
更新/注释(2015年4月):在你对这段代码进行复制和粘贴之前,请注意,我已经发现了一些实例,其中它可能不是100%可靠的,必须与其他方法结合才能获得最佳结果。 值得注意的是,有两个已知的实例,其中onTrimMemory回调不保证被执行:
如果你的手机锁定屏幕,而你的应用程序是可见的(比如你的设备锁定nn分钟后),这个回调不会被调用(或不总是),因为锁定屏幕只是在顶部,但你的应用程序仍然“运行”,尽管覆盖。 如果您的设备内存相对较低(并且处于内存压力之下),操作系统似乎会忽略这个调用,直接进入更关键的级别。
现在,取决于你知道你的应用什么时候进入后台有多重要,你可能需要也可能不需要扩展这个解决方案,同时跟踪活动生命周期等等。
只要记住以上几点,并拥有一个优秀的QA团队;)
更新结束
可能晚了,但在冰淇淋三明治(API 14)及以上有一个可靠的方法。
当你的应用没有更多可见的UI时,一个回调被触发。可以在自定义类中实现的回调称为ComponentCallbacks2(是的,带有一个2)。此回调仅在API级别14(冰淇淋三明治)及以上可用。
你基本上得到一个方法的调用:
public abstract void onTrimMemory (int level)
级别是20或更多
public static final int TRIM_MEMORY_UI_HIDDEN
我一直在测试这个,它总是有效的,因为20级只是一个“建议”,你可能想要释放一些资源,因为你的应用程序不再可见。
引用官方文件:
onTrimMemory(int)的级别:进程已经显示了一个用户界面,并且不再这样做。这时候应该释放大量的UI分配,以便更好地管理内存。
当然,您应该实现它来实现它所说的(清除在特定时间内未使用的内存,清除一些未使用的集合,等等。可能性是无限的(查看官方文档了解其他可能的更关键的级别)。
但是,有趣的是,操作系统告诉你:嘿,你的应用进入了后台!
这正是你一开始就想知道的。
你怎么确定你什么时候回来?
好吧,这很容易,我相信你有一个“BaseActivity”,所以你可以使用你的onResume()标记事实,你回来了。因为只有当您实际接收到对上述onTrimMemory方法的调用时,您才会说您没有返回。
它的工作原理。你不会得到假阳性。如果一项活动恢复了,那么你就百分百地回来了。如果用户再次返回,您将得到另一个onTrimMemory()调用。
您需要订阅您的Activities(或者更好的是,定制类)。
保证你总是收到这个的最简单的方法是创建一个简单的类,像这样:
public class MemoryBoss implements ComponentCallbacks2 {
@Override
public void onConfigurationChanged(final Configuration newConfig) {
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// We're in the Background
}
// you might as well implement some memory cleanup here and be a nice Android dev.
}
}
为了使用这个,在你的应用程序实现中(你有一个,对吧?),做如下的事情:
MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mMemoryBoss = new MemoryBoss();
registerComponentCallbacks(mMemoryBoss);
}
}
如果你创建了一个接口,你可以在If中添加一个else,并实现在API 14以下的任何东西中使用的ComponentCallbacks(没有2)。该回调只有onLowMemory()方法,当你进入后台时不会被调用,但你应该使用它来修剪内存。
现在启动你的App,按home键。你的onTrimMemory(最终int级别)方法应该被调用(提示:添加日志记录)。
最后一步是从回调取消注册。也许最好的地方是你的应用程序的onTerminate()方法,但是,该方法不会在真正的设备上被调用:
/** *此方法用于模拟过程环境。它将 *永远不要在生产Android设备上调用,那里有进程 *通过简单地杀死它们来移除;没有用户代码(包括此回调) 执行*。 * /
因此,除非你真的遇到了不想再注册的情况,否则你可以安全地忽略它,因为你的进程在操作系统级已经死亡了。
如果你决定在某些时候取消注册(例如,如果你为你的应用程序提供了一个关闭机制来清理和死亡),你可以这样做:
unregisterComponentCallbacks(mMemoryBoss);
就是这样。
推荐文章
- 在android中从上下文获取活动
- 无法解析主机"<URL here>"没有与主机名关联的地址
- getActivity()在Fragment函数中返回null
- 按钮背景是透明的
- 在Mac OS X上哪里安装Android SDK ?
- 我如何获得图像缩放功能?
- 在Android应用程序中显示当前时间和日期
- BottomSheetDialogFragment的圆角
- 在应用程序启动时出现“无法获得BatchedBridge,请确保您的bundle被正确打包”的错误
- 我如何改变默认对话框按钮的文本颜色在安卓5
- 更改单选按钮的圆圈颜色
- 如何在android中复制一个文件?
- adb找不到我的设备/手机(MacOS X)
- 如何在新的材质主题中改变背面箭头的颜色?
- androidviewpager与底部点