Menentukan aplikasi latar depan saat ini dari tugas atau layanan latar belakang

112

Saya ingin memiliki satu aplikasi yang berjalan di latar belakang, yang mengetahui kapan salah satu aplikasi bawaan (olahpesan, kontak, dll) sedang berjalan.

Jadi pertanyaan saya adalah:

  1. Bagaimana saya harus menjalankan aplikasi saya di latar belakang.

  2. Bagaimana aplikasi latar belakang saya dapat mengetahui aplikasi apa yang sedang berjalan di latar depan.

Tanggapan dari orang-orang yang berpengalaman akan sangat kami hargai.

dhaiwat
sumber
Saya tidak berpikir Anda telah memberikan penjelasan yang memadai tentang apa yang Anda coba lakukan. Apa yang coba dilakukan aplikasi latar belakang Anda? Dengan cara apa ia dapat berinteraksi dengan pengguna? Mengapa Anda perlu mengetahui apa aplikasi latar depan saat ini? Dll
Charles Duffy
1
untuk mendeteksi aplikasi latar depan, Anda dapat menggunakan github.com/ricvalerio/foregroundappchecker
rvalerio

Jawaban:

104

Berkenaan dengan "2. Bagaimana aplikasi latar belakang saya dapat mengetahui aplikasi apa yang sedang berjalan di latar depan."

JANGAN menggunakan getRunningAppProcesses()metode ini karena ini mengembalikan semua jenis sampah sistem dari pengalaman saya dan Anda akan mendapatkan banyak hasil yang memilikinya RunningAppProcessInfo.IMPORTANCE_FOREGROUND. Gunakan getRunningTasks()sebagai gantinya

Ini adalah kode yang saya gunakan dalam layanan saya untuk mengidentifikasi aplikasi latar depan saat ini, sangat mudah:

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);

Itu saja, maka Anda dapat dengan mudah mengakses detail aplikasi / aktivitas latar depan:

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

Ini membutuhkan izin tambahan dalam menifest aktivitas dan bekerja dengan sempurna.

<uses-permission android:name="android.permission.GET_TASKS" />
Oliver Pearmain
sumber
1
Ini harus ditandai sebagai jawaban yang benar. Izin yang diperlukan: android.permission.GET_TASKS adalah satu-satunya kelemahan yang sangat kecil untuk metode lain yang dapat menghindarinya.
brandall
3
EDIT KE ATAS: Catatan: metode ini hanya ditujukan untuk debugging dan menampilkan antarmuka pengguna manajemen tugas. <- Baru saja melihatnya di dokumen Java. Jadi metode ini mungkin berubah tanpa pemberitahuan di masa mendatang.
brandall
47
Catatan: getRunningTasks()tidak berlaku lagi di API 21 (Lollipop) - developer.android.com/reference/android/app/…
dtyler
@dtyler, oke. Apakah ada metode lain sebagai gantinya? Meskipun Google tidak ingin aplikasi pihak ketiga mendapatkan informasi sensitif, saya memiliki kunci platform perangkat. Apakah ada metode lain yang melakukan hal yang sama jika saya memiliki hak istimewa sistem?
Yeung
Ada cara menggunakan "API statistik penggunaan" yang diperkenalkan pada 21
KunalK
38

saya harus mencari tahu solusi yang tepat dengan cara yang sulit. kode di bawah ini adalah bagian dari cyanogenmod7 (tablet tweak) dan diuji pada android 2.3.3 / gingerbread.

metode:

  • getForegroundApp - mengembalikan aplikasi latar depan.
  • getActivityForApp - mengembalikan aktivitas aplikasi yang ditemukan.
  • isStillActive - menentukan apakah aplikasi yang ditemukan sebelumnya masih merupakan aplikasi yang aktif.
  • isRunningService - fungsi pembantu untuk getForegroundApp

semoga ini menjawab masalah ini secara menyeluruh (:

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;
}
Sven Dawitz
sumber
1
jika Anda tidak keberatan saya bertanya, mengapa Anda memerlukan metode untuk melihat apakah itu menjalankan layanan, masih aktif, dan mendapatkan aktivitas hanya untuk mendapatkan aplikasi latar depan?
2
maaf, tapi tidak berhasil. Beberapa aplikasi difilter tanpa alasan, saya pikir itu ketika mereka menjalankan beberapa layanan. Jadi isRunningService menendang mereka keluar.
seb
15
Sayangnya, getRunningTasks () sudah tidak digunakan lagi sejak Android L (API 20). Sejak L, metode ini tidak lagi tersedia untuk aplikasi pihak ketiga: pengenalan terbaru yang berpusat pada dokumen berarti dapat membocorkan informasi orang ke pemanggil. Untuk kompatibilitas mundur, itu masih akan mengembalikan sebagian kecil datanya: setidaknya tugas pemanggil sendiri, dan mungkin beberapa tugas lain seperti rumah yang diketahui tidak sensitif.
Sam Lu
Adakah yang tahu bagaimana pendekatan ini lebih baik daripada sekadar melakukan mActivityManager.getRunningTasks(1).get(0).topActivity?
Sam
35

Coba kode berikut:

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);
    }
}

Nama proses adalah nama paket aplikasi yang berjalan di latar depan. Bandingkan dengan nama paket aplikasi Anda. Jika sama, maka aplikasi Anda berjalan di latar depan.

Saya harap ini menjawab pertanyaan Anda.

Friedrich Bhaer
sumber
7
Mulai Android versi L dan seterusnya, ini adalah satu-satunya solusi yang berfungsi karena metode yang digunakan di solusi lain sudah tidak digunakan lagi.
androidGuy
4
Namun, ini tidak berfungsi jika aplikasi berada di latar depan dan layar terkunci
Krit
Jawaban yang benar harus dicentang
A_rmas
1
Apakah perlu izin tambahan seperti jawaban yang diterima?
wonsuc
1
Ini tidak sepenuhnya benar. Nama proses tidak selalu sama dengan nama paket aplikasi yang sesuai.
Sam
25

Dari lollipop dan seterusnya ini berubah. Silakan temukan kode di bawah ini, sebelum pengguna itu harus pergi ke Pengaturan -> Keamanan -> (Gulir ke bawah hingga terakhir) Aplikasi dengan akses penggunaan -> Berikan izin ke aplikasi kami

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);
}
Suman
sumber
1
tidak bisakah statistik penggunaan ini disetel secara terprogram?
rubmz
@rubmz Apakah Anda punya solusi untuk ini?
VVB
1
di doogee saya dengan android 5.1 tidak ada opsi "Berikan izin ke aplikasi kami"
djdance
Ini tidak menghasilkan 100% hasil yang akurat ... itu benar sebagian besar waktu
CamHart
1
Ini tidak akan menampilkan Aplikasi terakhir di latar depan, tetapi mungkin yang terakhir "digunakan" dengan suatu cara - jika Anda meninggalkan aktivitas dan pergi ke aktivitas lain, tarik menu peluncur dari atas, dan lepaskan, Anda akan berada di " salah "sampai Anda beralih ke aktivitas lain. Tidak masalah jika Anda menggulir layar atau tidak, ini melaporkan aktivitas yang salah sampai Anda meluncurkan yang lain.
Azurlake
9

Untuk kasus ketika kita perlu memeriksa dari service / background-thread kita sendiri apakah aplikasi kita berada di latar depan atau tidak. Ini adalah cara saya menerapkannya, dan berfungsi dengan baik untuk saya:

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
}

Sekarang untuk memeriksa dari kelas lain, apakah aplikasi berada di latar depan atau tidak, cukup panggil:

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

Pembaruan (seperti yang ditunjukkan oleh SHS ):

Jangan lupa untuk mendaftar callback dalam onCreatemetode kelas Aplikasi Anda .

@Override
public void onCreate() {
    ...
    registerActivityLifecycleCallbacks(this);
}
Sarthak Mittal
sumber
2
Itulah yang saya cari sejak dua jam terakhir. Terima kasih! :)
waseefakhtar
1
Kita perlu memanggil "registerActivityLifecycleCallbacks (this);" di dalam metode Override "onCreate ()" dari kelas Aplikasi (TestApplication). Ini akan memulai callback di kelas aplikasi kita.
SHS
@SarthakMittal, Jika perangkat masuk ke mode siaga atau kita menguncinya, maka onActivityStopped () akan dipanggil. Berdasarkan kode Anda, ini akan menunjukkan bahwa aplikasi berada di latar belakang. Tapi sebenarnya itu ada di latar depan.
Ayaz Alifov
@AyazAlifov Jika ponsel dalam keadaan standby atau ponsel terkunci, saya tidak akan menganggapnya di latar depan, tetapi sekali lagi itu tergantung pada preferensi.
Sarthak Mittal
8

Untuk menentukan aplikasi latar depan, Anda dapat menggunakan untuk mendeteksi aplikasi latar depan, Anda dapat menggunakan https://github.com/ricvalerio/foregroundappchecker . Ini menggunakan metode yang berbeda tergantung pada versi android perangkat.

Sedangkan untuk layanan, repo juga menyediakan kode yang Anda butuhkan. Pada dasarnya, biarkan android studio membuat layanan untuk Anda, lalu onCreate menambahkan cuplikan yang menggunakan appChecker. Namun, Anda perlu meminta izin.

rvalerio
sumber
sempurna!! diuji pada android 7
Pratik Tank
Terima kasih banyak. Ini adalah satu-satunya jawaban yang memecahkan masalah yang kami hadapi dengan pemberitahuan aplikasi. Saat notifikasi muncul, semua jawaban lain akan memberikan kembali nama paket aplikasi yang mengirim notifikasi. LollipopDetector Anda memecahkan masalah ini. Satu petunjuk: dari API 29, MOVE_TO_FOREGROUND tidak digunakan lagi di UsageEvents.Event, jadi mulai Android Q dan seterusnya, Anda harus menggunakan ACTIVITY_RESUMED. Maka itu akan bekerja seperti pesona!
Jorn Rigter
8

Mempertimbangkan bahwa getRunningTasks()sudah usang dan getRunningAppProcesses()tidak dapat diandalkan, saya mengambil keputusan untuk menggabungkan 2 pendekatan yang disebutkan di StackOverflow:

   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();
        }
    }
Ayaz Alifov
sumber
4
Anda perlu menyebutkan untuk menambahkan ke manifes izin yang tidak berlaku lagi <
using
1
android.permission.GET_TASKS - saat ini dilarang di Play Store
Duna
6

Kelas ActivityManager adalah alat yang tepat untuk melihat proses mana yang sedang berjalan.

Untuk berjalan di latar belakang, Anda biasanya ingin menggunakan Service .

Charles Duffy
sumber
1

Ini berhasil untuk saya. Tapi itu hanya memberikan nama menu utama. Itu jika pengguna telah membuka Pengaturan -> Bluetooth -> layar Nama Perangkat, RunningAppProcessInfo menyebutnya hanya Pengaturan. Tidak dapat menelusuri lebih jauh

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());
                    }               
                }
NakulSargur
sumber
4
Saya pikir ini hanya berfungsi jika aplikasi Anda sendiri berjalan di latar depan. Itu tidak melaporkan apa pun saat beberapa aplikasi lain berada di latar depan.
Alice Van Der Land
0

Lakukan sesuatu seperti ini:

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;
}
ChristianS
sumber
Google mungkin akan menolak aplikasi yang menggunakan ActivityManager.getRunningTasks (). Dokumentasi menyatakan bahwa itu adalah tujuan musuh saja: developer.android.com/reference/android/app/… Lihat komentar awal: stackoverflow.com/questions/4414171/…
ForceMagic
3
@ForceMagic - Google tidak menolak aplikasi hanya karena menggunakan API yang tidak dapat diandalkan ; Lebih jauh lagi, Google bukanlah satu-satunya saluran distribusi untuk aplikasi Android. Karena itu, perlu diingat bahwa informasi dari API ini tidak dapat diandalkan.
Chris Stratton
@ChrisStratton Menarik, tetapi Anda mungkin benar, meskipun tidak disarankan untuk menggunakannya untuk logika inti, mereka tidak akan menolak aplikasi. Anda mungkin ingin memperingatkan Sky Kelsey dari tautan komentar.
ForceMagic
3
"getRunningTasks" berfungsi untuk api 21 dan seterusnya, jadi mungkin ide yang bagus untuk mencari cara lain agar berfungsi untuk LOLLIPOP (saya sendiri belum menemukan
jawabannya
0

Di lollipop dan lebih tinggi:

Tambahkan ke mainfest:

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

Dan lakukan sesuatu seperti ini:

if( mTaskId < 0 )
{
    List<AppTask> tasks = mActivityManager.getAppTasks(); 
    if( tasks.size() > 0 )
        mTaskId = tasks.get( 0 ).getTaskInfo().id;
}
rubmz
sumber
4
Itu tidak digunakan lagi menjadi marshmallow
jinkal
0

Beginilah cara saya memeriksa apakah aplikasi saya ada di latar depan. Catatan Saya menggunakan AsyncTask seperti yang disarankan oleh dokumentasi resmi Android.`

`

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

dan jalankan AsyncTask seperti ini. new CheckIfForeground().execute();

Developine
sumber
apakah kamu punya link untuk ini?
pengguna1743524
0

Saya menggabungkan dua solusi dalam satu metode dan itu bekerja untuk saya untuk API 24 dan untuk API 21. Lainnya saya tidak menguji.

Kode di Kotlin:

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()
}

dan di Manifest

<!-- Check whether app in background or foreground -->
<uses-permission android:name="android.permission.GET_TASKS" />
Denshov
sumber