🌒MoonLab

> 在MoonLab中搜索

  1. 1. 前言
  2. 2. 正文
    1. 2.1. 方法1
    2. 2.2. 方法2 (???)
    3. 2.3. 方法3
      1. 2.3.1. Shizuku 做法

Android 监听第三方Activity的一举一动

Category: Programming

🏷️  Android   中文

前言

距离上一次更新也已经是六个月前了。感觉2019年过得真不咋样,什么也没干成吧,没有什么进步。

不知不觉又是新的一年,2019是去年,2018就成了前年了。

这个冬天我们这都没怎么下过雪,积雪的话我知记得有一次,融化得还很快。

国内也出现了传染病,迄今为止有五千多人中枪了吧。现在也不让出去,亲戚也都没有拜完,现在天天在家窝。(不过好处就是不用上学啦)

今年的央视春晚也没有看,bilibili还搞了拜年祭,拜年祭真的是一年比一年棒了,可惜我也没兴趣看。

最近在补魔法少女小圆,看得很慢。1月新番也没啥期待的,只有一部喜欢的:某科学电池炮T,毕竟是我的入坑番。还有一部是异度入侵,我不太喜欢目前的剧情,唯一喜欢的就只有OP了。

哦其实还有Acfun独播的达尔文游戏,也还可以(因为我喜欢女主的性格)

总结:一月新番喜欢的只有一部番和一首歌,没了。

感觉越来越没意思了,也可能是我心态的变化,现在我都一直在看以前的番,新番我已经没啥可期待的了。

这篇文章磨磨唧唧花费了三天,我已经把魔法少女小圆TV版看完了,太特喵好看了,还有剧场版和外传又够我看一阵子了。

好多人都说“致郁”,其实我觉得还可以啊并不怎么致郁,相反还有些治愈(迫真脸)。


[TOC]

正文

回归正题,我在开发的过程中有一个小需求:需要监听每一个Activity的启动,并且需要获取Activity的ClassName。

我这种菜鸡只能先求助百度:Android 监听第三方Activity

方法1

首先百度到的第一个方法是搞一个Service,然后在一个循环里一直获取栈顶的Activity。

emmmmmm….

List<ActivityManager.RunningTaskInfo> runningTasks = am.getRunningTasks(1);
ActivityManager.RunningTaskInfo runningTaskInfo = runningTasks.get(0);

这个办法肯定是不行哒,Google早已对getRunningTasks方法严加管制,需要android.permission.GET_TOP_ACTIVITY_INFO权限,但是这个权限必须要系统app才能申请。

但或许能用shell指令 ps 来获取栈顶的 Activity,虽然我没试过。

方法2 (???)

第二个方法是使用 UsageStatsManager,也就是应用使用情况的一个功能。

但是只能得到应用的包名,而不是Activity的类名。

方法3

在我百度的时候我注意到了一个接口:IActivityController和一个方法:setActivityController

啊,决定了,就是你啦!

我开始了解有关IActivityController的详细内容。

我还发现了一个比AndroidXref稍微更友好的Android源码在线查看网站:https://www.androidos.net.cn/

本来setActivityController这个方法是在ActivityManagerNative中的,但是在Android API 26的时候似乎被遗弃了,取而代之的是IActivityManager中的setActivityController

在之前,启动Activity的是ActivityManagerNative,getDefault()内的startActivity方法,而ActivityManagerService继承自ActivityManagerNative,ActivityManagerNative又继承自Binder并实现iActivityManager,AMS是IActivityManager的具体实现。

IInterface, Stub, Proxy 三个类是分离的,分别是 IActivityManager,ActivityManagerNative (extends Binder) 和 ActivityManagerProxy。

而现在(API 26+),AMS直接继承了 IActivityManager的内部类 Stub:

这时候 Android 源码已经有了 IActivityManager.aidl,ActivityManagerNative 这个类被弃用了

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
        ...
}

那么自然而然,setIActivityController 这个方法是在 IActivityManager.aidl 内定义的,

而 IActivityController 是一个在 Binder 中的回调接口,可以直接下载 IActivityController.aidl 放在 android.app 这个包下。要与 Android 系统源码的位置一致。

但是该怎么调用 系统API 呢?除非我们的应用得在系统进程。

这时候我在酷安看到了一个叫做 Shizuku 的玩意:官方文档

它可以帮助我们快速使用 系统API,用户只需用 adb 或者 root 激活 Shizuku,看看官方文档的解释:

Shizuku 做法

Shizuku app 会引导用户使用 root 或是 adb 方式运行一个进程(Shizuku 服务进程)。

  1. 应用进程启动时 Shizuku 服务进程发送 binder 至应用进程
  2. 应用通过该 binder 与 Shizuku 服务进程交互,Shizuku 服务进程通过 binder 与 system server 交互

Shizuku 的优点在于:

  1. 极小额外时间及性能消耗
  2. 与直接调用 API 体验几乎一致(应用开发者只许添加少量代码)

导入 Shizuku 的开源库,先在 android.app 包内创建一个同名文件:!ActivityManager.java

假装他是 IActivityManager.aidl 自动生成的一个类(我们无需在下载 IActivityManager.aidl 放在项目里)。

package android.app;

import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;

/* 我们自己创建的 IActivityManager */
public interface IActivityManager extends IInterface {

    void setActivityController(IActivityController watcher, boolean imAMonkey);

    abstract class Stub extends Binder implements IActivityManager {

        public static IActivityManager asInterface(IBinder obj) {
            throw new UnsupportedOperationException();
        }
    }

}

结构和 aidl 自动生成的类没什么两样,就是只把关键的方法写出来。

注意哦,这个 Stub 是一个抽象类!asInterface 方法并没有具体的实现。

之后我们再:

private static final IActivityManager ACTIVITY_MANAGER = IActivityManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("activity")));

    public static void setActivityController(IActivityController activityController) {
        if (!ShizukuService.pingBinder()) {
            return;
        }
        ACTIVITY_MANAGER.setActivityController(activityController, true);
    }

这样我们就能直接调用 setActivityController 方法了,然后在重写 IActivityController.Stub 内的回调方法就行了。

2018-2023 MoonLab