Существует ли нативный способ получения ссылки на текущую Activity из сервиса?
У меня есть служба, работающая в фоновом режиме, и я хотел бы обновлять текущую Activity при наступлении какого-либо события (в службе). Есть ли простой способ сделать это (подобный тому, что я предложил выше)?
Вот хороший способ сделать это с помощью менеджера активности. В основном вы получаете runningTasks из менеджера активности. Он всегда будет возвращать сначала текущую активную задачу. Отсюда можно получить topActivity.
Существует простой способ получения списка запущенных задач из сервиса ActivityManager. Вы можете запросить максимальное количество задач, выполняющихся на телефоне, и по умолчанию первой возвращается текущая активная задача.
После этого можно получить объект ComponentName, запросив topActivity из списка.
Вот пример.
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
Log.d("topActivity", "CURRENT Activity ::" + taskInfo.get(0).topActivity.getClassName());
ComponentName componentInfo = taskInfo.get(0).topActivity;
componentInfo.getPackageName();
Вам потребуется следующее разрешение в манифесте:
<uses-permission android:name="android.permission.GET_TASKS"/>
Компания Google пригрозила удалять приложения из Play Store, если они используют сервисы доступности не по назначению. Однако по имеющимся сведениям, эта мера пересматривается.
AccessibilityService
AccessibilityService
.onAccessibilityEvent
проверьте наличие TYPE_WINDOW_STATE_CHANGED
тип события, чтобы определить, когда текущее окно меняется.PackageManager.getActivityInfo()
.GET_TASKS
.AccessibilityService
, он не может нажать кнопку OK, если приложение разместило на экране оверлей. К таким приложениям относятся Velis Auto Brightness и Lux. Это может сбить пользователя с толку, поскольку он может не знать, почему не может нажать кнопку и как это сделать.AccessibilityService
не будет знать о текущей активности до первого изменения активности.public class WindowChangeDetectingService extends AccessibilityService {
@Override
protected void onServiceConnected() {
super.onServiceConnected();
//Configure these here for compatibility with API 13 and below.
AccessibilityServiceInfo config = new AccessibilityServiceInfo();
config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
if (Build.VERSION.SDK_INT >= 16)
//Just in case this helps
config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
setServiceInfo(config);
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
if (event.getPackageName() != null && event.getClassName() != null) {
ComponentName componentName = new ComponentName(
event.getPackageName().toString(),
event.getClassName().toString()
);
ActivityInfo activityInfo = tryGetActivity(componentName);
boolean isActivity = activityInfo != null;
if (isActivity)
Log.i("CurrentActivity", componentName.flattenToShortString());
}
}
}
private ActivityInfo tryGetActivity(ComponentName componentName) {
try {
return getPackageManager().getActivityInfo(componentName, 0);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
@Override
public void onInterrupt() {}
}
Включите это в свой манифест:
<application>
<service
android:label="@string/accessibility_service_name"
android:name=".WindowChangeDetectingService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibilityservice"/>
</service>
</application>
Поместите это в файл res/xml/accessibilityservice.xml
:
<?xml version="1.0" encoding="utf-8"?>
<!-- These options MUST be specified here in order for the events to be received on first
start in Android 4.1.1 -->
<accessibility-service
xmlns:tools="http://schemas.android.com/tools"
android:accessibilityEventTypes="typeWindowStateChanged"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagIncludeNotImportantViews"
android:description="@string/accessibility_service_description"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:ignore="UnusedAttribute"/>
Каждому пользователю приложения необходимо явно включить AccessibilityService
, чтобы она могла использоваться. Как это сделать, см. в этом ответе на StackOverflow.
Обратите внимание, что пользователь не сможет нажать кнопку OK при попытке включить службу доступности, если приложение разместило на экране оверлей, например Velis Auto Brightness или Lux.
Существует ли нативный способ получения ссылки на текущую Activity из сервиса?
Вы можете не владеть "текущей активностью".
У меня есть служба, работающая в фоновом режиме, и я хотел бы обновлять текущую Activity при наступлении какого-либо события (в службе). Есть ли простой способ сделать это (подобный тому, который я предложил выше)?
Intent
в активность - вот пример проекта, демонстрирующий этот паттернPendingIntent
(например, через createPendingResult()
), который вызывает сервисbindService()
, а сервис вызывает метод события на этом объекте обратного вызова/слушателя.Intent
, с низкоприоритетным BroadcastReceiver
в качестве резервного (чтобы поднять Notification
, если активность не находится на экране) -- вот запись в блоге с более подробным описанием этого паттернаЭто может быть сделано путем:
общественное приложение класс расширяет приложения {
частная деятельность activeActivity;
@Переопределить общественного недействительными метод onCreate() { супер.метод onCreate(); setupActivityListener(); }
частный недействительными setupActivityListener() { registerActivityLifecycleCallbacks(новый ActivityLifecycleCallbacks() { @Переопределить общественного недействительными onActivityCreated(деятельность, пакет savedInstanceState) { } @Переопределить общественного недействительными onActivityStarted(деятельности) { } @Переопределить общественного недействительными onActivityResumed(деятельности) { activeActivity = активность; } @Переопределить общественного недействительными onActivityPaused(деятельности) { activeActivity = нуль; } @Переопределить общественного недействительными onActivityStopped(деятельности) { } @Переопределить общественного недействительными onActivitySaveInstanceState(деятельность, пачка outState) { } @Переопределить общественного недействительными onActivityDestroyed(деятельности) { } }); }
getActiveActivity общественной активности(){ возвращение activeActivity; }
}
()
и бросил его на ваше имя класса приложения (Приложение в данном случае). Чем вы можете вызвать приложение.getActiveActivity()- это даст вам текущей видимой активности (или null, если никакой активности видно). Вы можете получить имя действия путем вызова
activeActivity.getClass().getSimpleName()`Я не мог найти решение, что наша команда будет довольна тем, что мы закатали наши собственные. Мы используем ActivityLifecycleCallbacks
, чтобы отслеживать текущую активность, а затем сделать его доступным через сервис:
public interface ContextProvider {
Context getActivityContext();
}
public class MyApplication extends Application implements ContextProvider {
private Activity currentActivity;
@Override
public Context getActivityContext() {
return currentActivity;
}
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
MyApplication.this.currentActivity = activity;
}
@Override
public void onActivityStarted(Activity activity) {
MyApplication.this.currentActivity = activity;
}
@Override
public void onActivityResumed(Activity activity) {
MyApplication.this.currentActivity = activity;
}
@Override
public void onActivityPaused(Activity activity) {
MyApplication.this.currentActivity = null;
}
@Override
public void onActivityStopped(Activity activity) {
// don't clear current activity because activity may get stopped after
// the new activity is resumed
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
// don't clear current activity because activity may get destroyed after
// the new activity is resumed
}
});
}
}
Затем настроить свой контейнер, чтобы возвратить экземпляр Мое_приложение " за " ContextProvider
, например
public class ApplicationModule extends AbstractModule {
@Provides
ContextProvider provideMainActivity() {
return MyApplication.getCurrent();
}
}
(Обратите внимание, что реализации getCurrent()` исключается из кода выше. Это's просто статической переменной, что's набор из приложения конструктор)
ActivityManager
Если вы только хотите знать применение содержит текущей деятельности, вы можете сделать это, используя ActivityManager
. Техника, которую вы можете использовать, зависит от версии Android:
ActivityManager.getRunningTasks
(Пример)ActivityManager.getRunningAppProcesses
(Пример)ActivityManager.RunningAppProcessInfo.processState
public class CurrentApplicationPackageRetriever {
private final Context context;
public CurrentApplicationPackageRetriever(Context context) {
this.context = context;
}
public String get() {
if (Build.VERSION.SDK_INT < 21)
return getPreLollipop();
else
return getLollipop();
}
private String getPreLollipop() {
@SuppressWarnings("deprecation")
List<ActivityManager.RunningTaskInfo> tasks =
activityManager().getRunningTasks(1);
ActivityManager.RunningTaskInfo currentTask = tasks.get(0);
ComponentName currentActivity = currentTask.topActivity;
return currentActivity.getPackageName();
}
private String getLollipop() {
final int PROCESS_STATE_TOP = 2;
try {
Field processStateField = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");
List<ActivityManager.RunningAppProcessInfo> processes =
activityManager().getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo process : processes) {
if (
// Filters out most non-activity processes
process.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
&&
// Filters out processes that are just being
// _used_ by the process with the activity
process.importanceReasonCode == 0
) {
int state = processStateField.getInt(process);
if (state == PROCESS_STATE_TOP) {
String[] processNameParts = process.processName.split(":");
String packageName = processNameParts[0];
/*
If multiple candidate processes can get here,
it's most likely that apps are being switched.
The first one provided by the OS seems to be
the one being switched to, so we stop here.
*/
return packageName;
}
}
}
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
return null;
}
private ActivityManager activityManager() {
return (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
}
}
Добавить только
разрешение AndroidManifest.xml
:
<!--suppress DeprecatedClassUsageInspection -->
<uses-permission android:name="android.permission.GET_TASKS" />
Я'м через это для моего испытания. Это'ы по API > 19, и только для деятельности вашего приложения, хотя.
@TargetApi(Build.VERSION_CODES.KITKAT)
public static Activity getRunningActivity() {
try {
Class activityThreadClass = Class.forName("android.app.ActivityThread");
Object activityThread = activityThreadClass.getMethod("currentActivityThread")
.invoke(null);
Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
activitiesField.setAccessible(true);
ArrayMap activities = (ArrayMap) activitiesField.get(activityThread);
for (Object activityRecord : activities.values()) {
Class activityRecordClass = activityRecord.getClass();
Field pausedField = activityRecordClass.getDeclaredField("paused");
pausedField.setAccessible(true);
if (!pausedField.getBoolean(activityRecord)) {
Field activityField = activityRecordClass.getDeclaredField("activity");
activityField.setAccessible(true);
return (Activity) activityField.get(activityRecord);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
throw new RuntimeException("Didn't find the running activity");
}
Используйте этот код для API 21 или выше. Это работает и дает лучший результат по сравнению с другими ответами, он прекрасно распознает процесс переднего плана.
if (Build.VERSION.SDK_INT >= 21) {
String currentApp = null;
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();
}
}
Вот мой ответ, который работает просто отлично...
Вы должны быть в состоянии получить текущую деятельность таким образом... Если структура вашего приложения с нескольких мероприятий с большим количеством обломков и вы хотите отслеживать то, что является вашей текущей деятельностью, это займет много работы. Мой сценарий был у меня одно занятие с нескольких фрагментов. Так что я могу отслеживать текущую деятельность через объект application, который может хранить все текущее состояние глобальных переменных.
Вот это способ. Когда вы начинаете вашу деятельность, вы храните, что деятельность по Приложения.setCurrentActivity(так()); Это приложение будет хранить его. На ваш класс обслуживания, вы можете просто нравится Намерение currentIntent = приложение.getCurrentActivity(); getApplication().startActivity(currentIntent);
Я не'т знаю, если это'ы глупый ответ, но решил эту проблему путем сохранения флага в общих настройках каждый раз, когда я вошел в метод onCreate() какой-либо деятельности, то я использовал значение из поддерживает предпочтения, чтобы выяснить, что это'ы на переднем плане активности.
Совсем недавно узнал об этом. С помощью API как:
ActivityManager.getCurrentActivity(контекст)
Надеюсь, что это какая-нибудь польза.