백그라운드 작업 또는 서비스에서 현재 포 그라운드 응용 프로그램 확인


112

내장 된 응용 프로그램 (메시징, 연락처 등)이 언제 실행되고 있는지 알 수있는 백그라운드에서 실행되는 하나의 응용 프로그램을 갖고 싶습니다.

그래서 내 질문은 다음과 같습니다.

  1. 애플리케이션을 백그라운드에서 실행하는 방법.

  2. 내 백그라운드 애플리케이션이 현재 포 그라운드에서 실행중인 애플리케이션이 무엇인지 알 수있는 방법.

경험이 많은 분들의 답변 부탁드립니다.


나는 당신이하려는 일에 대해 적절한 설명을하지 않았다고 생각합니다. 무엇 을하려고 당신의 배경 응용 프로그램입니다? 사용자와 어떤 방식으로 상호 작용할 수 있어야합니까? 현재 포 그라운드 앱이 무엇인지 알아야하는 이유 는 무엇입니까? 기타
Charles Duffy


1
전경 응용 프로그램을 검출, 당신은 사용할 수 있습니다 github.com/ricvalerio/foregroundappchecker을
rvalerio

답변:


104

"2. 내 백그라운드 애플리케이션이 현재 포 그라운드에서 실행중인 애플리케이션이 무엇인지 알 수있는 방법"과 관련하여.

getRunningAppProcesses()내 경험에서 모든 종류의 시스템 쓰레기를 반환 하므로 메서드를 사용하지 마십시오 RunningAppProcessInfo.IMPORTANCE_FOREGROUND. 사용 getRunningTasks()하는 대신

이것은 현재 포 그라운드 애플리케이션을 식별하기 위해 서비스에서 사용하는 코드입니다. 정말 쉽습니다.

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

이제 포 그라운드 앱 / 활동의 세부 정보에 쉽게 액세스 할 수 있습니다.

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

이것은 활동 메니 페스트에 대한 추가 권한이 필요하며 완벽하게 작동합니다.

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

1
이것은 정답으로 표시되어야합니다. 필수 권한 : android.permission.GET_TASKS는이를 피할 수있는 다른 메소드의 유일한 단점입니다.
brandall

3
위 편집 : 참고 :이 방법은 작업 관리 사용자 인터페이스를 디버깅하고 표시하기위한 용도로만 사용됩니다. <-Java 문서에서 방금 발견했습니다. 따라서이 방법은 향후 예고없이 변경 될 수 있습니다.
brandall

47
참고 : getRunningTasks()- API 21 (롤리팝)는 지원되지 않습니다 developer.android.com/reference/android/app/...을
dtyler

@dtyler, 좋아요. 대신 다른 방법이 있습니까? Google은 타사 앱이 민감한 정보를 얻는 것을 원하지 않지만 기기의 플랫폼 키를 가지고 있습니다. 시스템 권한이있는 경우 동일한 작업을 수행하는 다른 방법이 있습니까?
Yeung

21에서 도입 된 "사용 통계 API"를 사용하는 방법이 있습니다
KunalK

38

어려운 방법으로 올바른 솔루션을 찾아야했습니다. 아래 코드는 cyanogenmod7 (태블릿 조정)의 일부이며 Android 2.3.3 / gingerbread에서 테스트되었습니다.

행동 양식:

  • getForegroundApp-포 그라운드 애플리케이션을 반환합니다.
  • getActivityForApp-찾은 앱의 활동을 반환합니다.
  • isStillActive-이전에 찾은 앱이 여전히 활성 앱인지 확인합니다.
  • isRunningService-getForegroundApp에 대한 도우미 함수

이것은 모든 extend (:

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
내가 물어봐도 괜찮다면 서비스가 실행 중인지, 아직 활성 상태인지 확인하고 포 그라운드 애플리케이션을 가져 오기 위해 활동을 가져 오는 방법이 필요한 이유는 무엇입니까?

2
미안하지만 작동하지 않습니다. 이유없이 일부 앱이 필터링되는 경우가 있는데, 서비스를 실행할 때인 것 같습니다. 그래서 isRunningService는 그들을 쫓아냅니다.
seb

15
안타깝게도 getRunningTasks ()는 Android L (API 20) 이후로 더 이상 사용되지 않습니다. L부터는이 방법을 타사 응용 프로그램에서 더 이상 사용할 수 없습니다. 문서 중심의 최신 정보가 도입됨에 따라 사람 정보가 호출자에게 유출 될 수 있습니다. 이전 버전과의 호환성을 위해 최소한 호출자 자신의 작업과 민감하지 않은 것으로 알려진 집과 같은 다른 작업과 같은 데이터의 작은 하위 집합을 반환합니다.
Sam Lu

이 접근 방식이 단순히하는 것보다 더 나은 방법을 아는 사람이 mActivityManager.getRunningTasks(1).get(0).topActivity있습니까?
Sam

35

다음 코드를 시도하십시오.

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

프로세스 이름은 포 그라운드에서 실행중인 앱의 패키지 이름입니다. 애플리케이션의 패키지 이름과 비교하십시오. 동일하면 애플리케이션이 포 그라운드에서 실행 중입니다.

귀하의 질문에 대한 답변이 되었기를 바랍니다.


7
Android 버전 L부터는 다른 솔루션에서 사용되는 방법이 더 이상 사용되지 않으므로 작동하는 유일한 솔루션입니다.
androidGuy 2014

4
그러나 앱이 포 그라운드에 있고 화면이
잠긴

정답은 쳤다되어야한다
A_rmas

1
수락 된 답변과 같은 추가 권한이 필요합니까?
wonsuc

1
이것은 정확하지 않습니다. 프로세스의 이름이 해당 앱 패키지 이름과 항상 같지는 않습니다.
Sam

25

롤리팝 이후로 이것은 바뀌 었습니다. 해당 사용자가 설정-> 보안-> (마지막으로 스크롤) 사용 권한이있는 앱-> 앱에 권한 부여하기 전에 아래 코드를 찾으십시오.

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
이 사용 통계를 프로그래밍 방식으로 설정할 수 없습니까?
rubmz

@rubmz 이것에 대한 해결책이 있습니까?
VVB

1
안드로이드 5.1 내 doogee에있는 옵션은 "우리의 응용 프로그램에 권한을 부여하지 않습니다"이
djdance

이것은 바로 대부분의 시간입니다 ... 100 % 정확한 결과를 생성하지 않습니다
CamHart

1
이렇게하면 마지막 앱이 포 그라운드에 표시되지 않지만 마지막으로 "사용 된"앱이 표시됩니다. 활동을 떠나 다른 활동으로 이동 한 후 위에서 런처 메뉴를 당겨 놓으면 " 당신이 다른 곳으로 갈 때까지 잘못된 "활동. 화면을 스크롤하든 안하든 상관없고 다른 활동을 시작할 때까지 잘못된 활동을보고합니다.
Azurlake 2019

9

자체 서비스 / 백그라운드 스레드에서 앱이 포 그라운드에 있는지 여부를 확인해야하는 경우. 이것이 내가 구현 한 방법이며 나를 위해 잘 작동합니다.

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
}

이제 앱이 포 그라운드에 있는지 여부를 다른 클래스에서 확인하려면 다음을 호출하면됩니다.

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

업데이트 ( SHS가 지적한대로 ) :

Application 클래스의 onCreate메서드 에서 콜백을 등록하는 것을 잊지 마십시오 .

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

2
그것이 지난 2 시간 동안 제가 찾고 있던 것입니다. 감사합니다! :)
waseefakhtar

1
"registerActivityLifecycleCallbacks (this);"를 호출해야합니다. Application 클래스 (TestApplication)의 "onCreate ()"메서드를 재정의합니다. 그러면 애플리케이션 클래스에서 콜백이 시작됩니다.
SHS

@SarthakMittal, 장치가 대기 모드로 들어가거나 잠그면 onActivityStopped ()가 호출됩니다. 코드에 따라 애플리케이션이 백그라운드에 있음을 나타냅니다. 그러나 실제로는 전경에 있습니다.
Ayaz Alifov

@AyazAlifov 전화가 대기 상태이거나 전화가 잠겨 있으면 전경에있는 것으로 간주하지 않지만 기본 설정에 따라 다릅니다.
Sarthak Mittal

8

포 그라운드 애플리케이션을 결정하기 위해 포 그라운드 앱을 감지하는 데 사용할 수 있으며 https://github.com/ricvalerio/foregroundappchecker 를 사용할 수 있습니다 . 기기의 Android 버전에 따라 다른 방법을 사용합니다.

서비스의 경우 저장소는 필요한 코드도 제공합니다. 기본적으로 android studio가 서비스를 생성하도록 한 다음 onCreate가 appChecker를 사용하는 스 니펫을 추가합니다. 그러나 권한을 요청해야합니다.


완전한!! 안드로이드 7에서 테스트
Pratik Tank dec

정말 고마워. 이것이 앱 알림과 관련하여 직면 한 문제를 해결 한 유일한 답변입니다. 알림이 표시되면 다른 모든 답변은 알림을 보낸 앱의 패키지 이름을 반환합니다. LollipopDetector가이 문제를 해결했습니다. 한 가지 힌트 : API 29에서 MOVE_TO_FOREGROUND는 UsageEvents.Event에서 더 이상 사용되지 않으므로 Android Q부터는 ACTIVITY_RESUMED를 사용해야합니다. 그러면 매력처럼 작동합니다!
Jorn Rigter

8

고려 getRunningTasks()되지하고 getRunningAppProcesses()신뢰할 수 없습니다, I에 유래에 언급 된 두 방법을 결합하는 결정을했다 :

   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
당신이 매니페스트되지 않는 권한에 추가 언급 할 필요가 <사용-권한은 안드로이드 : 이름 = "android.permission.GET_TASKS을"/> 그렇지 않으면 응용 프로그램이 충돌합니다
RJFares

1
android.permission.GET_TASKS-현재 Play 스토어에서 금지되어 있습니다
Duna


1

이것은 나를 위해 일했습니다. 그러나 주 메뉴 이름 만 제공합니다. 즉, 사용자가 설정-> Bluetooth-> 장치 이름 화면을 연 경우 RunningAppProcessInfo는이를 설정으로 호출합니다. 더 드릴 다운 할 수 없음

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
내 앱이 포 그라운드에서 실행되는 경우에만 작동한다고 생각합니다. 다른 앱이 포 그라운드에있을 때는 아무것도보고하지 않습니다.
Alice Van Der Land

0

다음과 같이하십시오.

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은 아마도 ActivityManager.getRunningTasks ()를 사용하는 앱을 거부 할 것입니다. 문서는 개발 목적으로 만
적극

3
@ForceMagic-Google은 단순히 신뢰할 수없는 API 를 사용한다고해서 앱을 거부하지 않습니다 . 또한 Google은 Android 애플리케이션을위한 유일한 배포 채널이 아닙니다. 즉,이 API의 정보는 신뢰할 수 없다는 점을 명심할 가치가 있습니다.
Chris Stratton

@ChrisStratton 흥미롭지 만, 핵심 로직에 사용하는 것은 권장하지 않지만 앱을 거부하지는 않습니다. 댓글 링크에서 Sky Kelsey에게 경고 할 수 있습니다.
ForceMagic 2014 년

3
"getRunningTasks"는 api 21 이상에서 작동하므로 LOLLIPOP에서 작동하도록하는 다른 방법을 찾는 것이 좋습니다. (내 자신은 이것을 알아 내지 못했지만 :()
amIT

0

롤리팝 이상 :

메인 페스트에 추가 :

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

그리고 다음과 같이하십시오.

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

4
그것은 마시 멜로로 사용되지 않습니다
jinkal

0

이것이 내 앱이 포 그라운드에 있는지 확인하는 방법입니다. 참고 공식 Android 문서에서 제안한대로 AsyncTask 를 사용하고 있습니다 .`

`

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

다음과 같이 AsyncTask를 실행합니다. new CheckIfForeground().execute();


이것에 대한 링크가 있습니까?
user1743524 jul.

0

두 가지 솔루션을 하나의 방법으로 결합했으며 API 24와 API 21에서 작동합니다. 다른 솔루션은 테스트하지 않았습니다.

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

및 매니페스트

<!-- Check whether app in background or foreground -->
<uses-permission android:name="android.permission.GET_TASKS" />
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.