栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

[JVMTI][JNI]Android 监听 java 对象创建释放

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

[JVMTI][JNI]Android 监听 java 对象创建释放

前言:

总共三步走:1. native 代码;2. 编译打包so;3. java 层 attach。
具体监听可以得到什么数据,数据怎么用不在该篇的叙述范围之内。

写 native 代码

随便找个地方创建一个 jni 文件夹

关键的代码如下:

# Application.mk
APP_ABI := arm64-v8a # armeabi-v7a x86 # 你需要生成什么版本的 so
# Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := monitor_agent # 唯一的模块名,默认生成为 lib.so
LOCAL_LDLIBS    := -llog # 依赖 native 的日志 lib
LOCAL_SRC_FILES := agentlib.cpp # 自己的资源文件
include $(BUILD_SHARED_LIBRARY)
直接去 JDK 里面 CV 一个  jvmti.h 出来。MAC 中的位置见下图。

void JNICALL
objectAlloc(jvmtiEnv
            *jvmti_env,
            JNIEnv *jni_env, jthread
            thread,
            jobject object, jclass
            object_klass,
            jlong size
) {
// 对象创建,这里可以记录一下
}


void JNICALL
objectFree(jvmtiEnv *jvmti_env,jlong tag) {
// 对象释放
}

extern "C"
JNIEXPORT void JNICALL
Java_com_tencent_qjvmti_Monitor_agent_1init(JNIEnv *env, jclass clazz
) {

  LOGE("Java_com_tencent_qjvmti_Monitor_agent_1init");

//开启jvm事件监听
  jvmtiEventCallbacks callbacks;
  memset(&callbacks, 0, sizeof(callbacks));
  callbacks.VMObjectAlloc = &objectAlloc;
  callbacks.ObjectFree = &objectFree;

//设置回调函数
  mJvmtiEnv->SetEventCallbacks(&callbacks, sizeof(callbacks));

//开启监听
  mJvmtiEnv-> SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC,nullptr);
  mJvmtiEnv-> SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, nullptr);
}
运行编译

找 ndk-build 在哪,直接运行(运行目录错的话,会提示你错误,比如目录错了)

/Users/lichen/Library/Android/sdk/ndk/22.1.7171670/ndk-build NDK_PROJECT_PATH=.

运行成功之后就会在 jni 的同级目录下面生成你的 so 了。

加载代理 移动 so

agent 的 so 与普通 so 不同,需要我们复制一份出来用。
直接打包进 apk lib 目录里面的 so 读不出来。所以我们直接放到 assets 中方便运行时读取。(或者指定 so 目录,push 进去,运行时读取一下)

读取 so,加载代理
// native 层的 init 方法
private native static void agent_init();

public static void init(Context application) {
    //最低支持Android 8.0
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
        return;
    }

    //获取so的地址后加载
    String agentPath = getAgentLibPath(application);
    
    // 先加载 so
    System.load(agentPath);

    //加载jvmti
    attachAgent(agentPath, application.getClassLoader());

    //开启jvmti事件监听
    agent_init();
}

// 读取复制之后的 agent.so 的 path
private static String getAgentLibPath(Context context) {
    try {
        return loadAssetsToCache(context, "libmonitor_agent.so");
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}


private static String loadAssetsToCache(Context context, String assertName) {
    String filePath = context.getFilesDir().getAbsolutePath();
    AssetManager assetManager = context.getAssets();
    try {
        InputStream inputStream = assetManager.open(assertName);
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        //保存到本地的文件夹下的文件
        String name = filePath + "/" + assertName;
        FileOutputStream fileOutputStream = new FileOutputStream(name);
        byte[] buffer = new byte[1024];
        int count = 0;
        while ((count = inputStream.read(buffer)) > 0) {
            fileOutputStream.write(buffer, 0, count);
        }
        fileOutputStream.flush();
        fileOutputStream.close();
        inputStream.close();
        return name;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}
一些小坑
  1. cmake 和 ndk,网上关于 so 的话题基本上都是 cmake 的,其实到最后认清楚 so 是可以独立出来,不是一定要在 gradle 里面配置的话,这里就可以跳出来了。二者都只不过是 so 的编译工具。
  2. 如何动态加载 so,直接打包进 apk 的 lib 里面是个大坑,读取不到,最后放到 assets 中再加载到 app 目录中即可。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/684561.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号