Bir arka plan görevinden veya hizmetinden mevcut ön plan uygulamasını belirleme


112

Arka planda çalışan ve yerleşik uygulamalardan herhangi birinin (mesajlaşma, kişiler, vb.) Ne zaman çalıştığını bilen bir uygulamaya sahip olmak istiyorum.

Yani sorularım:

  1. Uygulamamı arka planda nasıl çalıştırmalıyım.

  2. Arka plan uygulamam, o anda ön planda çalışan uygulamanın ne olduğunu nasıl anlayabilir?

Tecrübeli kişilerden gelen yanıtlar çok takdir edilecektir.


Ne yapmaya çalıştığına dair yeterli bir açıklama yaptığını sanmıyorum. Ne yapmaya çalışıyor arka plan uygulamadır? Kullanıcıyla hangi şekillerde etkileşime girebilmelidir? Mevcut ön plan uygulamasının ne olduğunu neden bilmeniz gerekiyor? Vb.
Charles Duffy


1
ön plan uygulamasını tespit etmek için github.com/ricvalerio/foregroundappchecker'ı
rvalerio

Yanıtlar:


104

"2. Arka plan uygulamam, o anda ön planda çalışan uygulamanın ne olduğunu nasıl bilebilir?"

getRunningAppProcesses()Yöntemi KULLANMAYIN çünkü bu benim deneyimlerime göre her türlü sistem çöpünü döndürür ve birden fazla sonuç alırsınız RunningAppProcessInfo.IMPORTANCE_FOREGROUND. getRunningTasks()Bunun yerine kullanın

Bu, mevcut ön plan uygulamasını tanımlamak için hizmetimde kullandığım kod, gerçekten çok kolay:

ActivityManager am = (ActivityManager) AppService.this.getSystemService(ACTIVITY_SERVICE);
// The first in the list of RunningTasks is always the foreground task.
RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);

İşte bu, daha sonra ön plan uygulamasının / etkinliğinin ayrıntılarına kolayca erişebilirsiniz:

String foregroundTaskPackageName = foregroundTaskInfo .topActivity.getPackageName();
PackageManager pm = AppService.this.getPackageManager();
PackageInfo foregroundAppPackageInfo = pm.getPackageInfo(foregroundTaskPackageName, 0);
String foregroundTaskAppName = foregroundAppPackageInfo.applicationInfo.loadLabel(pm).toString();

Bu, etkinlik menüsünde ek bir izin gerektirir ve mükemmel çalışır.

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

1
Bu doğru cevap olarak işaretlenmelidir. Gerekli izin: android.permission.GET_TASKS, onu önleyebilecek diğer yöntemlerin tek çok küçük dezavantajıdır.
brandall

3
YUKARIDA DÜZENLEME: Not: Bu yöntem yalnızca görev yönetimi kullanıcı arabirimlerinde hata ayıklama ve sunma amaçlıdır. <- Bunu Java belgesinde fark ettim. Dolayısıyla bu yöntem gelecekte önceden haber verilmeksizin değişebilir.
brandall

47
Not: getRunningTasks()API 21'de (Lollipop) kullanımdan kaldırıldı - developer.android.com/reference/android/app/…
dtyler

@dtyler, tamam o zaman. Bunun yerine başka bir yöntem var mı? Google, üçüncü taraf uygulamaların hassas bilgileri almasını istemese de, cihazın platform anahtarına sahibim. Sistem ayrıcalığına sahipsem aynı şeyi yapan başka bir yöntem var mı?
Yeung

21'de tanıtılan "Kullanım istatistikleri api" nin bir kullanımı var
KunalK

38

Doğru çözümü zor yoldan bulmalıydım. Aşağıdaki kod cyanogenmod7'nin (tablet ince ayarları) bir parçasıdır ve android 2.3.3 / gingerbread üzerinde test edilmiştir.

yöntemleri:

  • getForegroundApp - ön plan uygulamasını döndürür.
  • getActivityForApp - bulunan uygulamanın etkinliğini döndürür.
  • isStillActive - daha önce bulunan bir uygulamanın hala etkin uygulama olup olmadığını belirler.
  • isRunningService - getForegroundApp için bir yardımcı işlev

umarım bu sorunu her yönden yanıtlar (:

private RunningAppProcessInfo getForegroundApp() {
    RunningAppProcessInfo result=null, info=null;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <RunningAppProcessInfo> l = mActivityManager.getRunningAppProcesses();
    Iterator <RunningAppProcessInfo> i = l.iterator();
    while(i.hasNext()){
        info = i.next();
        if(info.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                && !isRunningService(info.processName)){
            result=info;
            break;
        }
    }
    return result;
}

private ComponentName getActivityForApp(RunningAppProcessInfo target){
    ComponentName result=null;
    ActivityManager.RunningTaskInfo info;

    if(target==null)
        return null;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <ActivityManager.RunningTaskInfo> l = mActivityManager.getRunningTasks(9999);
    Iterator <ActivityManager.RunningTaskInfo> i = l.iterator();

    while(i.hasNext()){
        info=i.next();
        if(info.baseActivity.getPackageName().equals(target.processName)){
            result=info.topActivity;
            break;
        }
    }

    return result;
}

private boolean isStillActive(RunningAppProcessInfo process, ComponentName activity)
{
    // activity can be null in cases, where one app starts another. for example, astro
    // starting rock player when a move file was clicked. we dont have an activity then,
    // but the package exits as soon as back is hit. so we can ignore the activity
    // in this case
    if(process==null)
        return false;

    RunningAppProcessInfo currentFg=getForegroundApp();
    ComponentName currentActivity=getActivityForApp(currentFg);

    if(currentFg!=null && currentFg.processName.equals(process.processName) &&
            (activity==null || currentActivity.compareTo(activity)==0))
        return true;

    Slog.i(TAG, "isStillActive returns false - CallerProcess: " + process.processName + " CurrentProcess: "
            + (currentFg==null ? "null" : currentFg.processName) + " CallerActivity:" + (activity==null ? "null" : activity.toString())
            + " CurrentActivity: " + (currentActivity==null ? "null" : currentActivity.toString()));
    return false;
}

private boolean isRunningService(String processname){
    if(processname==null || processname.isEmpty())
        return false;

    RunningServiceInfo service;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <RunningServiceInfo> l = mActivityManager.getRunningServices(9999);
    Iterator <RunningServiceInfo> i = l.iterator();
    while(i.hasNext()){
        service = i.next();
        if(service.process.equals(processname))
            return true;
    }

    return false;
}

1
Sormamın sakıncası yoksa, bir hizmetin çalışıp çalışmadığını, hala aktif olup olmadığını görmek ve sadece ön plan uygulamasını almak için aktivite almak için neden yöntemlere ihtiyacınız var?

2
üzgünüm ama çalışmıyor. Bazı uygulamalar sebepsiz filtreleniyor, sanırım bazı servisler çalıştırdıklarında. Öyleyse, RunService onları dışarı atıyor.
seb

15
Maalesef getRunningTasks (), Android L'den (API 20) beri kullanımdan kaldırılmıştır. L itibariyle, bu yöntem artık üçüncü taraf uygulamalar için mevcut değildir: belge merkezli son aramaların tanıtımı, kişi bilgilerini arayan kişiye sızdırabileceği anlamına gelir. Geriye dönük uyumluluk için, yine de verilerinin küçük bir alt kümesini döndürecektir: en azından arayanın kendi görevleri ve muhtemelen hassas olmadığı bilinen ev gibi diğer bazı görevler.
Sam Lu

Bu yaklaşımın basitçe yapmaktan daha iyi olduğunu bilen var mı mActivityManager.getRunningTasks(1).get(0).topActivity?
Sam

35

Aşağıdaki kodu deneyin:

ActivityManager activityManager = (ActivityManager) newContext.getSystemService( Context.ACTIVITY_SERVICE );
List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
for(RunningAppProcessInfo appProcess : appProcesses){
    if(appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
        Log.i("Foreground App", appProcess.processName);
    }
}

İşlem adı, ön planda çalışan uygulamanın paket adıdır. Bunu uygulamanızın paket adıyla karşılaştırın. Eğer aynıysa uygulamanız ön planda çalışıyor demektir.

Umarım bu, sorunuzu yanıtlamıştır.


7
Android L sürümünden itibaren, diğer çözümlerde kullanılan yöntemler kullanımdan kaldırıldığı için çalışan tek çözüm budur.
androidGuy

4
ancak, uygulamanın ön planda olması ve ekranın kilitlenmesi durumunda bu işe yaramaz
Krit

Doğru cevap işaretlenmelidir
A_rmas

1
Kabul edilen cevap gibi ek izne ihtiyaç var mı?
wonsuc

1
Bu tam olarak doğru değil. Bir işlemin adı her zaman karşılık gelen uygulama paketi adıyla aynı değildir.
Sam

25

Lolipoptan itibaren bu değişti. Lütfen aşağıdaki kodu bulun, bu kullanıcının Ayarlar -> Güvenlik -> (En sona kaydır) Kullanım erişimi olan uygulamalar -> Uygulamamıza izinler verin

private void printForegroundTask() {
    String currentApp = "NULL";
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        UsageStatsManager usm = (UsageStatsManager) this.getSystemService(Context.USAGE_STATS_SERVICE);
        long time = System.currentTimeMillis();
        List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
        if (appList != null && appList.size() > 0) {
            SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
            for (UsageStats usageStats : appList) {
                mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
            }
            if (mySortedMap != null && !mySortedMap.isEmpty()) {
                currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
            }
        }
    } else {
        ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
        currentApp = tasks.get(0).processName;
    }

    Log.e(TAG, "Current App in foreground is: " + currentApp);
}

1
bu kullanım istatistikleri programlı olarak ayarlanamaz mı?
rubmz

@rubmz Bu konuda herhangi bir çözümünüz var mı?
VVB

1
Android 5.1 ile doogee'mde "Uygulamamıza izin verme" seçeneği yok
djdance

Bu% 100 doğru sonuçlar üretmez ... çoğu zaman
doğrudur

1
Bu, ön plandaki son Uygulamayı göstermez, ancak belki de bir şekilde son "kullanılmış" - bir etkinlikten ayrılır ve başka birine giderseniz, başlatıcı menüsünü yukarıdan çekip serbest bırakırsanız, " bir başkasına gidene kadar yanlış "aktivite. Ekranı kaydırmanız veya kaydırmamanız önemli değil, siz başka bir etkinlik başlatana kadar yanlış etkinliği bildiriyor.
Azurlake

9

Uygulamamızın ön planda olup olmadığını kendi hizmetimizden / arka plan iş parçacığından kontrol etmemiz gereken durumlar için. Bunu nasıl uyguladım ve benim için iyi çalışıyor:

public class TestApplication extends Application implements Application.ActivityLifecycleCallbacks {

    public static WeakReference<Activity> foregroundActivityRef = null;

    @Override
    public void onActivityStarted(Activity activity) {
        foregroundActivityRef = new WeakReference<>(activity);
    }

    @Override
    public void onActivityStopped(Activity activity) {
        if (foregroundActivityRef != null && foregroundActivityRef.get() == activity) {
            foregroundActivityRef = null;
        }
    }

    // IMPLEMENT OTHER CALLBACK METHODS
}

Şimdi, uygulamanın ön planda olup olmadığını diğer sınıflardan kontrol etmek için şunu arayın:

if(TestApplication.foregroundActivityRef!=null){
    // APP IS IN FOREGROUND!
    // We can also get the activity that is currently visible!
}

Güncelleme ( SHS tarafından belirtildiği gibi ):

Uygulama sınıfınızın onCreateyönteminde geri aramalar için kaydolmayı unutmayın .

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

2
Son iki saattir aradığım şey bu. Teşekkür ederim! :)
waseefakhtar

1
"RegisterActivityLifecycleCallbacks (this)"; Uygulama sınıfının (TestApplication) "onCreate ()" Override yöntemi. Bu, uygulama sınıfımızdaki geri aramaları başlatacaktır.
SHS

@SarthakMittal, Bir cihaz bekleme moduna girerse veya onu kilitlediğimizde, onActivityStopped () çağrılacaktır. Kodunuza bağlı olarak, bu, uygulamanın arka planda olduğunu gösterecektir. Ama aslında ön planda.
Ayaz Alifov

@AyazAlifov Telefon beklemedeyse veya telefon kilitliyse, ön planda olduğunu düşünmeyeceğim, ancak yine tercihe bağlı.
Sarthak Mittal

8

Ön plan uygulamasını belirlemek için ön plan uygulamasını tespit etmek için kullanabilir, https://github.com/ricvalerio/foregroundappchecker kullanabilirsiniz . Cihazın android sürümüne bağlı olarak farklı yöntemler kullanır.

Hizmete gelince, repo da bunun için ihtiyacınız olan kodu sağlar. Esasen, android stüdyosunun hizmeti sizin için oluşturmasına izin verin ve ardından onCreate appChecker'ı kullanan parçacığı ekleyin. Ancak izin istemeniz gerekecek.


mükemmel!! android 7'de test edildi
Pratik Tank

Çok teşekkürler. Uygulamaların bildirimleriyle karşılaştığımız sorunu çözen tek cevap bu. Bir bildirim açıldığında, diğer tüm yanıtlar bildirimi gönderen uygulamanın paket adını geri verecektir. LollipopDetector'ınız bu sorunu çözdü. Bir ipucu: API 29'dan MOVE_TO_FOREGROUND UsageEvents.Event'te kullanımdan kaldırıldı, bu nedenle Android Q'dan itibaren ACTIVITY_RESUMED kullanılmalıdır. O zaman bir cazibe gibi çalışacak!
Jorn Rigter

8

Kullanımdan getRunningTasks()kaldırıldığını ve getRunningAppProcesses()güvenilir olmadığını göz önünde bulundurarak , StackOverflow'da belirtilen 2 yaklaşımı birleştirmeye karar verdim:

   private boolean isAppInForeground(Context context)
    {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
        {
            ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
            ActivityManager.RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);
            String foregroundTaskPackageName = foregroundTaskInfo.topActivity.getPackageName();

            return foregroundTaskPackageName.toLowerCase().equals(context.getPackageName().toLowerCase());
        }
        else
        {
            ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
            ActivityManager.getMyMemoryState(appProcessInfo);
            if (appProcessInfo.importance == IMPORTANCE_FOREGROUND || appProcessInfo.importance == IMPORTANCE_VISIBLE)
            {
                return true;
            }

            KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
            // App is foreground, but screen is locked, so show notification
            return km.inKeyguardRestrictedInputMode();
        }
    }

4
Manifest'e kullanımdan kaldırılan izni <uses-
allow

1
android.permission.GET_TASKS - şu anda Play Store'da yasak
Duna


1

Bu benim için çalıştı. Ancak yalnızca ana menü adını verir. Yani, kullanıcı Ayarlar -> Bluetooth -> Cihaz Adı ​​ekranını açtıysa, RunningAppProcessInfo onu sadece Ayarlar olarak çağırır. Furthur'u delemiyor

ActivityManager activityManager = (ActivityManager) context.getSystemService( Context.ACTIVITY_SERVICE );
                PackageManager pm = context.getPackageManager();
                List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
                for(RunningAppProcessInfo appProcess : appProcesses) {              
                    if(appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                        CharSequence c = pm.getApplicationLabel(pm.getApplicationInfo(appProcess.processName, PackageManager.GET_META_DATA));
                        Log.i("Foreground App", "package: " + appProcess.processName + " App: " + c.toString());
                    }               
                }

4
Bunun yalnızca kendi uygulamanız ön planda çalışıyorsa işe yaradığını düşünüyorum. Başka bir uygulama ön plandayken hiçbir şey bildirmez.
Alice Van Der Land

0

Bunun gibi bir şey yapın:

int showLimit = 20;

/* Get all Tasks available (with limit set). */
ActivityManager mgr = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> allTasks = mgr.getRunningTasks(showLimit);
/* Loop through all tasks returned. */
for (ActivityManager.RunningTaskInfo aTask : allTasks) 
{                  
    Log.i("MyApp", "Task: " + aTask.baseActivity.getClassName()); 
    if (aTask.baseActivity.getClassName().equals("com.android.email.activity.MessageList")) 
        running=true;
}

Google muhtemelen ActivityManager.getRunningTasks () kullanan bir uygulamayı reddedecektir. Belgeler, bunun yalnızca geliştirici amaçlı olduğunu belirtmektedir: developer.android.com/reference/android/app/… İlk yoruma bakın: stackoverflow.com/questions/4414171/…
ForceMagic

3
@ForceMagic - Google, uygulamaları yalnızca güvenilmez API'ler kullandıkları için reddetmez ; ayrıca Google, Android uygulamaları için tek dağıtım kanalı değildir. Bununla birlikte, bu API'den gelen bilgilerin güvenilir olmadığı unutulmamalıdır.
Chris Stratton

@ChrisStratton İlginç, ancak muhtemelen haklısınız, temel mantık için kullanmaktan vazgeçilse de, uygulamayı reddetmeyecekler. Sky Kelsey'i yorum bağlantısından uyarmak isteyebilirsiniz.
ForceMagic

3
"getRunningTasks" API 21 ve sonrasında işe yarıyor, bu yüzden LOLLIPOP için çalışmasını sağlayacak başka bir yol bulmak iyi bir fikir olabilir (yine de bunu kendim
çözmedim

0

Lolipopta ve üzeri:

Ana festivale ekle:

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

Ve bunun gibi bir şey yapın:

if( mTaskId < 0 )
{
    List<AppTask> tasks = mActivityManager.getAppTasks(); 
    if( tasks.size() > 0 )
        mTaskId = tasks.get( 0 ).getTaskInfo().id;
}

4
Hatmi olarak kullanımdan kaldırıldı
jinkal

0

Uygulamamın ön planda olup olmadığını bu şekilde kontrol ediyorum. Not Resmi Android belgelerinde önerildiği gibi AsyncTask kullanıyorum.`

'

    private class CheckIfForeground extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... voids) {

        ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
        for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
            if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                Log.i("Foreground App", appProcess.processName);

                if (mContext.getPackageName().equalsIgnoreCase(appProcess.processName)) {
                    Log.i(Constants.TAG, "foreground true:" + appProcess.processName);
                    foreground = true;
                    // close_app();
                }
            }
        }
        Log.d(Constants.TAG, "foreground value:" + foreground);
        if (foreground) {
            foreground = false;
            close_app();
            Log.i(Constants.TAG, "Close App and start Activity:");

        } else {
            //if not foreground
            close_app();
            foreground = false;
            Log.i(Constants.TAG, "Close App");

        }

        return null;
    }
}

ve AsyncTask'ı bu şekilde çalıştırın. new CheckIfForeground().execute();


bunun için bir bağlantın var mı
user1743524

0

İki çözümü tek bir yöntemde birleştirdim ve benim için API 24 ve API 21 için çalışıyor. Diğerlerini test etmedim.

Kotlin'deki kod:

private fun isAppInForeground(context: Context): Boolean {
    val appProcessInfo = ActivityManager.RunningAppProcessInfo()
    ActivityManager.getMyMemoryState(appProcessInfo)
    if (appProcessInfo.importance == IMPORTANCE_FOREGROUND ||
            appProcessInfo.importance == IMPORTANCE_VISIBLE) {
        return true
    } else if (appProcessInfo.importance == IMPORTANCE_TOP_SLEEPING ||
            appProcessInfo.importance == IMPORTANCE_BACKGROUND) {
        return false
    }

    val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    val foregroundTaskInfo = am.getRunningTasks(1)[0]
    val foregroundTaskPackageName = foregroundTaskInfo.topActivity.packageName
    return foregroundTaskPackageName.toLowerCase() == context.packageName.toLowerCase()
}

ve Manifest'te

<!-- Check whether app in background or foreground -->
<uses-permission android:name="android.permission.GET_TASKS" />
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.