是否有一种原生的android方法来获取对服务中当前正在运行的Activity的引用?
我有一个在后台运行的服务,我想在事件发生时(在服务中)更新我当前的Activity。有没有一种简单的方法(就像我上面建议的那样)?
答案 0 :(得分:137)
这是使用活动管理器完成此操作的好方法。 您基本上从活动管理器获取runningTasks。它将始终首先返回当前活动的任务。从那里你可以获得topActivity。
有一种从ActivityManager服务获取正在运行的任务列表的简单方法。 您可以请求在手机上运行的最大任务数,默认情况下,首先返回当前活动的任务。
完成后,您可以通过从列表中请求topActivity来获取ComponentName对象。
这是一个例子。
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"/>
答案 1 :(得分:100)
Google has threatened如果他们使用辅助功能服务用于不可访问性目的,则从Play商店中删除应用。但是,this is reportedly being reconsidered。
AccessibilityService
AccessibilityService
onAccessibilityEvent
回调中,检查TYPE_WINDOW_STATE_CHANGED
event type以确定当前窗口何时发生变化。PackageManager.getActivityInfo()
检查窗口是否为活动。GET_TASKS
权限。AccessibilityService
时,如果应用在屏幕上放置了叠加层,则无法按“确定”按钮。一些应用程序是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
才能使用它。有关如何执行此操作,请参阅this StackOverflow answer。
请注意,如果应用在屏幕上放置了叠加层,例如Velis Auto Brightness或Lux,则在尝试启用辅助功能服务时,用户将无法按OK按钮。
答案 2 :(得分:81)
是否有一种原生的android方法来获取对服务中当前正在运行的Activity的引用?
您可能不拥有“当前正在运行的活动”。
我有一个在后台运行的服务,我想在事件发生时(在服务中)更新我当前的Activity。有没有一种简单的方法(就像我上面建议的那样)?
Intent
- 展示此模式的here is a sample project PendingIntent
(例如,通过createPendingResult()
)bindService()
向服务注册回调或侦听器对象,并让服务调用该回调/侦听器对象上的事件方法Intent
,并将低优先级BroadcastReceiver
作为备份(如果活动不在屏幕上,则提升Notification
) - {{ 3}}关于此模式的更多内容答案 3 :(得分:18)
可以通过以下方式完成:
实现您自己的应用程序类,注册ActivityLifecycleCallbacks - 这样您就可以看到我们的应用程序正在发生什么。在每次恢复时,回调会在屏幕上分配当前可见活动,暂停时会删除分配。它使用API 14中添加的方法registerActivityLifecycleCallbacks()
。
public class App extends Application {
private Activity activeActivity;
@Override
public void onCreate() {
super.onCreate();
setupActivityListener();
}
private void setupActivityListener() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
activeActivity = activity;
}
@Override
public void onActivityPaused(Activity activity) {
activeActivity = null;
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
public Activity getActiveActivity(){
return activeActivity;
}
}
在您的服务电话getApplication()
中,将其投放到您的应用类名称(在这种情况下为应用)。您可以调用app.getActiveActivity()
- 这将为您提供当前可见的活动(如果没有活动可见,则为null)。您可以致电activeActivity.getClass().getSimpleName()
答案 4 :(得分:13)
我找不到我们团队满意的解决方案,所以我们推出了自己的解决方案。我们使用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
}
});
}
}
然后配置您的DI容器以返回MyApplication
ContextProvider
的实例,例如
public class ApplicationModule extends AbstractModule {
@Provides
ContextProvider provideMainActivity() {
return MyApplication.getCurrent();
}
}
(请注意,上面的代码中省略了getCurrent()
的实现。它只是一个从应用程序构造函数设置的静态变量)
答案 5 :(得分:12)
ActivityManager
如果您只想知道包含当前活动的应用程序,可以使用ActivityManager
执行此操作。您可以使用的技术取决于Android的版本:
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);
}
}
将GET_TASKS
权限添加到AndroidManifest.xml
:
<!--suppress DeprecatedClassUsageInspection -->
<uses-permission android:name="android.permission.GET_TASKS" />
答案 6 :(得分:9)
我正在使用它进行测试。它的API&gt;但是,只有 用于您应用的活动。
@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");
}
答案 7 :(得分:1)
将此代码用于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();
}
}
答案 8 :(得分:1)
我喜欢 Application.ActivityLifecycleCallbacks
的想法。但是获得顶级活动可能有点棘手
要处理所有这些情况,您需要跟踪每个活动生命周期。这正是我使用以下解决方案所做的。
所有这些都合并为一次 getTopForegroundActivity()
调用,该调用返回顶部前台活动,或者 null
(如果堆栈中没有活动或没有活动在前台)。
public class MyApp extends Application {
private ActivityTracker activityTracker;
@Override
public void onCreate() {
super.onCreate();
activityTracker = new ActivityTracker();
registerActivityLifecycleCallbacks(activityTracker);
...
Activity activity = activityTracker.getTopForegroundActivity();
if(activity != null) {
// Do something
}
}
}
public class ActivityTracker implements Application.ActivityLifecycleCallbacks {
private final Map<Activity, ActivityData> activities = new HashMap<>();
public Activity getTopForegroundActivity() {
if (activities.isEmpty()) {
return null;
}
ArrayList<ActivityData> list = new ArrayList<>(activities.values());
Collections.sort(list, (o1, o2) -> {
int compare = Long.compare(o2.started, o1.started);
return compare != 0 ? compare : Long.compare(o2.resumed, o1.resumed);
});
ActivityData topActivity = list.get(0);
return topActivity.started != -1 ? topActivity.activity : null;
}
@Override
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
activities.put(activity, new ActivityData(activity));
}
@Override
public void onActivityStarted(@NonNull Activity activity) {
ActivityData activityData = activities.get(activity);
if (activityData != null) {
activityData.started = System.currentTimeMillis();
}
}
@Override
public void onActivityResumed(@NonNull Activity activity) {
ActivityData activityData = activities.get(activity);
if (activityData != null) {
activityData.resumed = System.currentTimeMillis();
}
}
@Override
public void onActivityPaused(@NonNull Activity activity) {
ActivityData activityData = activities.get(activity);
if (activityData != null) {
activityData.resumed = -1;
}
}
@Override
public void onActivityStopped(@NonNull Activity activity) {
ActivityData activityData = activities.get(activity);
if (activityData != null) {
activityData.started = -1;
}
}
@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
}
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
activities.remove(activity);
}
private static class ActivityData {
public final Activity activity;
public long started;
public long resumed;
private ActivityData(Activity activity) {
this.activity = activity;
}
}
}
答案 9 :(得分:0)
这是我的答案,工作得很好......
你应该能够以这种方式获得当前的活动...... 如果您使用包含许多片段的一些活动构建您的应用程序,并且您想要跟踪当前活动的内容,则需要做很多工作。我的senario是我有一个活动有多个碎片。所以我可以通过Application Object跟踪当前活动,它可以存储全局变量的所有当前状态。
这是一种方式。当您启动Activity时,您将存储该Activity Application.setCurrentActivity(getIntent()); 此应用程序将存储它。 在您的服务类上,您可以简单地这样做 Intent currentIntent = Application.getCurrentActivity(); 。getApplication()startActivity(currentIntent);
答案 10 :(得分:0)
我不知道这是不是一个愚蠢的答案,但每次我输入任何活动的onCreate()时都会在共享首选项中存储一个标志来解决这个问题,然后我使用shered偏好中的值来找出它是什么前景活动。
答案 11 :(得分:0)
这是我的建议,对我有用。在您的应用程序类中,实现一个Application.ActivityLifeCycleCallbacks
侦听器并在您的应用程序类中设置一个变量。然后根据需要查询变量。
class YourApplication: Application.ActivityLifeCycleCallbacks {
var currentActivity: Activity? = null
fun onCreate() {
registerActivityLifecycleCallbacks(this)
}
...
override fun onActivityResumed(activity: Activity) {
currentActivity = activity
}
}
答案 12 :(得分:-2)
刚刚发现了这个。用apis作为:
targetSdkVersion 26
ActivityManager.getCurrentActivity(上下文)
希望这有用。