Определение текущего приложения переднего плана из фоновой задачи или службы
Я хочу иметь одно приложение, которое работает в фоновом режиме, которое знает, когда запущено какое-либо из встроенных приложений (обмен сообщениями, контакты и т.д.).
Итак, мои вопросы:
-
Как запустить приложение в фоновом режиме.
-
Как мое фоновое приложение может узнать, что приложение, выполняемое в настоящее время на переднем плане.
Приветствуются ответы от людей с опытом.
Ответы
Ответ 1
Что касается "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" />
Ответ 2
Мне нужно было найти правильное решение. приведенный ниже код является частью cyanogenmod7 (хитрости планшета) и тестируется на Android 2.3.3/gingerbread.
методы:
- getForegroundApp - возвращает приложение переднего плана.
- getActivityForApp - возвращает активность найденного приложения.
- isStillActive - определяет, является ли ранее обнаруженное приложение активным приложением.
- isRunningService - вспомогательная функция для getForegroundApp
это, надеюсь, ответит на этот вопрос во всех аспектах (:
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;
}
Ответ 3
Попробуйте использовать следующий код:
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);
}
}
Имя процесса - это имя пакета приложения, запущенного на переднем плане. Сравните это с именем пакета вашего приложения. Если это то же самое, ваше приложение работает на переднем плане.
Надеюсь, это ответит на ваш вопрос.
Ответ 4
От lollipop вперед это изменилось. Пожалуйста, найдите ниже код, прежде чем этот пользователь должен перейти Настройки → Безопасность → (Прокрутить вниз до последнего) Приложения с доступом к использованию → Предоставить разрешения для нашего приложения
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);
}
Ответ 5
Принимая во внимание, что getRunningTasks()
устарел и getRunningAppProcesses()
не является надежным, я пришел к решению объединить 2 подхода, упомянутых в 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();
}
}
Ответ 6
класс ActivityManager - это подходящий инструмент для просмотра процессов работает.
Для запуска в фоновом режиме вы обычно хотите использовать Сервис.
Ответ 7
Чтобы определить приложение переднего плана, вы можете использовать для обнаружения приложения переднего плана, вы можете использовать https://github.com/ricvalerio/foregroundappchecker. Он использует разные методы в зависимости от версии устройства Android.
Что касается сервиса, репо также предоставляет код, который вам нужен. По сути, позвольте студии android создать сервис для вас, а затем onCreate добавить фрагмент, который использует appChecker. Однако вам нужно запросить разрешение.
Ответ 8
Для случаев, когда нам нужно проверить из нашего собственного сервиса /background-thread, находится ли наше приложение на переднем плане или нет. Вот как я это реализовал, и он прекрасно работает для меня:
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):
Не забудьте зарегистрироваться для обратных вызовов в вашем методе класса onCreate
класса onCreate
.
@Override
public void onCreate() {
...
registerActivityLifecycleCallbacks(this);
}
Ответ 9
Это сработало для меня. Но он дает только имя главного меню. То есть, если пользователь открыл Настройки → Bluetooth → Имя устройства, RunningAppProcessInfo называет его как просто Настройки. Невозможно развернуть furthur
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());
}
}
Ответ 10
Сделайте что-то вроде этого:
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;
}
Ответ 11
В леденец и вверх:
Добавить в список:
<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;
}
Ответ 12
Вот как я проверяю, находится ли мое приложение на переднем плане. Примечание. Я использую AsyncTask, как это предлагает официальная документация 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;
}
}
и выполните AsyncTask следующим образом.
new CheckIfForeground().execute();
Ответ 13
Я объединил два решения в одном методе, и он работает для меня для API 24 и для API 21. Другие я не тестировал.
Код в Котлине:
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" />