最近项目中遇到一个问题,需要做一个全局的弹窗,为此需要拿到当前栈顶的 Activity。在这里做一个总结:
- ActivityManager.getRunningTasks()
这个函数是官方的 public API,但已经废弃,原因是:“可能造成隐私泄露”。但因为兼容性原因,它仍然会返回一小部分不敏感的数据,比如当前进程的 Activities 或者 Launcher。
换句话说,在 Android L 以上,返回结果非常不可靠,而且只能拿到ComponentName 而不是 Activity 实例,不建议使用。
public static ComponentName getTopActivity(Context c) {
ActivityManager am = (ActivityManager) c
.getSystemService(Context.ACTIVITY_SERVICE);
if (am != null) {
List<ActivityManager.RunningTaskInfo> infos
= am.getRunningTasks(1);
if (infos != null && infos.size() != 0) {
return infos.get(0).topActivity;
}
}
return null;
}
- BaseActivity 记录生命周期回调
这个方法对于没有继承 BaseActivity 的 Activity 是无效的……由此可见在项目开始时创建基类成员并写进开发规范多么重要……
public abstract class BaseActivity extends AppCompatActivity {
private static WeakReference<BaseActivity> sTopActivity;
public static BaseActivity getTopActivity() {
return sTopActivity == null ? null : sTopActivity.get();
}
@Override
protected void onStart() {
super.onStart();
sTopActivity = new WeakReference<>(this);
}
@Override
protected void onStop() {
super.onStop();
if (getTopActivity() == this) {
sTopActivity = null;
}
}
}
- Application.registerActivityLifecycleCallback
这个方法可以在 Application 的 onCreate 函数中注册
private static WeakReference<Activity> sTopActivity;
public static Activity getTopActivity() {
return sTopActivity == null ? null : sTopActivity.get();
}
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
sTopActivity = new WeakReference<>(activity);
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
if (activity == getTopActivity()) {
sTopActivity = null;
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
- ActivityThread.ActivityClientRecord
这个方法是实际我使用的,因为项目是 Xposed 模块,可能是本机代码优化的原因,Application.onCreate() 函数 hook 不到,就算 hook 到了注册成功也收不到回调,有点莫名其妙。如果看过 ActivityThread 的源码,会发现里面有一个内部类 ActivityClientRecord,这个类是用来保存本进程所有 Activity 实例及生命状态的。缺点是:由于使用了反射和内部类,兼容性无法保证,如果 ROM 厂商改个名字可能就跪了。而且 Android 9 以上限制了反射的使用,需要进行一下适配。
private boolean mIgnore = false;
private Field mActivityField;
private Field mActivitiesField;
private Field mStoppedField;
@SuppressWarnings("unchecked")
private Activity getTopActivity() {
if (mIgnore) {
return null;
}
try {
if (mActivitiesField == null) {
mActivitiesField = ReflectUtils.getDeclaredField(
ActivityThread.class, "mActivities"
);
Class<?> cls = Class.forName("android.app.ActivityThread$ActivityClientRecord");
mActivityField = ReflectUtils.getDeclaredField(cls, "activity");
mStoppedField = ReflectUtils.getDeclaredField(cls, "stopped");
}
ActivityThread thread = ActivityThread.currentActivityThread();
final Map<Object, Object> mActivities = (Map<Object, Object>)
mActivitiesField.get(thread);
for (Object r : mActivities.values()) {
Activity activity = (Activity) mActivityField.get(r);
if (activity.isFinishing() || activity.isDestroyed()
|| ((boolean) mStoppedField.get(r))) {
continue;
}
return activity;
}
} catch (Exception e) {
mIgnore = true;
Log.e(TAG, "getTopActivity: ", e);
}
return null;
}
下面是 ReflectUtils
public static Field getDeclaredField(Class<?> cls, String name) {
Field field;
try {
field = cls.getDeclaredField(name);
field.setAccessible(true);
} catch (NoSuchFieldException e) {
field = null;
Log.e(TAG, "getDeclaredField: " + name, e);
}
return field;
}
对于 Android9 以上,可以参考
http://weishu.me/2019/03/16/another-free-reflection-above-android-p/