Check if app is running in foreground or background (with sync adapter)

五迷三道 提交于 2020-02-15 10:01:51

问题


I know this's a repeated question but i've looked all over the place and couldn't find a solution that works for me so...

I've an app that fetches movie data from TMDB API, it fetches it by page using a sync adapter, basically it runs great except when the sync adapter's periodic sync is ran while the app is open and the user is not at the first page, so my options were to force update the movies list and send the user to the top of the list which is totally bad experience so not considered as an option, Or i could check if the app is running, either in foreground or in the app stack that it's not visible to the user yet still running, so the best i could get to by searching was this piece of code:

   public static boolean isAppRunning(final Context context, final String packageName) {

    final ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    final List<ActivityManager.RunningAppProcessInfo> procInfos = activityManager.getRunningAppProcesses();

    if (procInfos != null) {
        for (final ActivityManager.RunningAppProcessInfo processInfo : procInfos) {
            if (processInfo.processName.equals(packageName))
                return true;
        }
    }

    return false;
}

but for whatever reason it doesn't work correctly and sees the app as running even if i killed it by swiping, at first i thought it was because i have a sync adapter which could be considered as the app running so i used another process for it in the manifest using the

android:process=":service"

but it didn't work

Also thought about using variables in onStart and onStop of the activity but it's not guaranteed that onStop will be called when app is closed or killed so trying to avoid this method

this's my manifest.xml

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="inc.mourad.ahmed.watchme">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />

<permission
    android:name="inc.ahmed.mourad.ACCESS_WATCHME_DATABASE"
    android:label="access my movies content provider"
    android:protectionLevel="dangerous" />
<permission
    android:name="inc.ahmed.mourad.ACCESS_WATCHME_SYNC_SERVICE"
    android:label="access my sync service"
    android:protectionLevel="dangerous" />
<permission
    android:name="inc.ahmed.mourad.ACCESS_WATCHME_SYNC_AUTHENTICATOR_SERVICE"
    android:label="access my sync authenticator dummy service"
    android:protectionLevel="dangerous" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/Theme.AppCompat">

    <activity android:name=".SplashScreenActivity"
        android:theme="@style/Theme.AppCompat.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat">
        <intent-filter>
            <data
                android:host="www.ahmedmourad.com"
                android:pathPrefix="/watchme/"
                android:scheme="http" />

            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
        </intent-filter>
    </activity>

    <activity
        android:name=".SettingsActivity"
        android:label="@string/title_activity_settings"
        android:parentActivityName=".MainActivity">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="inc.mourad.ahmed.watchme.MainActivity" />
    </activity>
    <activity android:name=".MovieDetailsActivity" />

    <provider
        android:name=".data.MovieProvider"
        android:authorities="@string/content_authority"
        android:enabled="true"
        android:exported="false"
        android:permission="inc.ahmed.mourad.ACCESS_WATCHME_DATABASE"
        android:syncable="true" />

    <!-- SyncAdapter's dummy authentication service -->
    <service android:name=".sync.WatchMeAuthenticatorService"
        android:permission="inc.ahmed.mourad.ACCESS_WATCHME_SYNC_AUTHENTICATOR_SERVICE"
        android:process=":service">
        <intent-filter>
            <action android:name="android.accounts.AccountAuthenticator" />
        </intent-filter>

        <meta-data
            android:name="android.accounts.AccountAuthenticator"
            android:resource="@xml/authenticator" />
    </service>

    <!-- The SyncAdapter service -->
    <service
        android:name=".sync.WatchMeSyncService"
        android:exported="true"
        android:permission="inc.ahmed.mourad.ACCESS_WATCHME_SYNC_SERVICE"
        android:process=":service">
        <intent-filter>
            <action android:name="android.content.SyncAdapter" />
        </intent-filter>

        <meta-data
            android:name="android.content.SyncAdapter"
            android:resource="@xml/syncadapter" />
    </service>

</application>

so, any ideas? Thanks in advance


回答1:


We tweaked your idea and came up with this (in Kotlin, see below for Java):

fun appInForeground(context: Context): Boolean {
    val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    val runningAppProcesses = activityManager.runningAppProcesses ?: return false
    return runningAppProcesses.any { it.processName == context.packageName && it.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND }
}

This is the equivalent in Java:

private boolean appInForeground(@NonNull Context context) {
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
    if (runningAppProcesses == null) {
        return false;
    }

    for (ActivityManager.RunningAppProcessInfo runningAppProcess : runningAppProcesses) {
        if (runningAppProcess.processName.equals(context.getPackageName()) &&
                runningAppProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
            return true;
        }
    }
    return false;
}

Here's the relevant documentation.




回答2:


Original Answer : https://stackoverflow.com/a/48767617/10004454 The recommended way to do it in accordance with Android documentation is

class MyApplication : Application(), LifecycleObserver {

override fun onCreate() {
    super.onCreate()
    ProcessLifecycleOwner.get().lifecycle.addObserver(this);
}

fun isActivityVisible(): String {
    return ProcessLifecycleOwner.get().lifecycle.currentState.name
}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppBackgrounded() {
    //App in background

    Log.e(TAG, "************* backgrounded")
    Log.e(TAG, "************* ${isActivityVisible()}")
}

@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onAppForegrounded() {

    Log.e(TAG, "************* foregrounded")
    Log.e(TAG, "************* ${isActivityVisible()}")
    // App in foreground
}}

In your gradle (app) add : implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"

Then to check the state at runtime call MyApplication().isActivityVisible()



来源:https://stackoverflow.com/questions/43378841/check-if-app-is-running-in-foreground-or-background-with-sync-adapter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!