autojs之内存泄露

简介: 内存泄漏的概念

内存泄漏的概念


百度百科:

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。


media projection泄露

和群里的@新城 看了截图内存泄漏,发现漏的不是aj,是系统进程  ,

一开media projection就开始涨内存  ,

没找到规避方法 除非用root截屏  ,

安卓的bug,雷电也休不了呀  ,

这个是合成surface的,本来就有这个进程  

autojs版本

9.0.5

autojs官方文档自带示例

// autojs官网: https://pro.autojs.org/docs/#/zh-cn/debug
$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();
for (let i = 0; i < 10; i++) {
  // 这个图片本应手动调用recycle回收
  let leak = captureScreen().clone();
  // 我们故意注释掉回收的代码
  // leak.recycle();
}


autojs开发者定的规矩


图片相关代码
除了截图不用主动回收图片,
其他图片处理, 一律必须主动回收
内存泄露只检测Image对象,不检测Mat  

img.recycle();


截图不会内存泄露

$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();
for (let i = 0; i < 10; i++) {
  img = captureScreen();
  sleep(10);
  img = captureScreen();
  sleep(10);
  img = captureScreen();
  sleep(10);
  img = captureScreen();
}


找色不会内存泄露

$debug.setMemoryLeakDetectionEnabled(true);
if (!requestScreenCapture()) {
  toast("请求截图失败");
  exit();
}
var img = captureScreen();
toastLog("开始找色");
//指定在位置(100, 220)宽高为400*400的区域找色。
//#75438a是编辑器默认主题的棕红色字体(数字)颜色,位置大约在第5行的"2000",坐标大约为(283, 465)
var point = findColorInRegion(img, "#75438a", 90, 220, 900, 1000);
if (point) {
  toastLog("x = " + point.x + ", y = " + point.y);
} else {
  toastLog("没有找到");
}


找图, 会内存泄露

$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();
var bigImg = captureScreen();
var smallImg = images.clip(bigImg, 10, 10, 200, 200);
var point = findImage(bigImg, smallImg);
toastLog(point);
// 不主动回收, 就会内存泄露
// bigImg.recycle();
// smallImg.recycle();


灰度化图片, 会内存泄露

$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();
var img = captureScreen();
let grayImg = images.grayscale(img); // 灰度化图片
// 不主动回收, 就会内存泄露
// grayImg.recycle();


嵌套函数处理图片, 会内存泄露

$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();
let img = captureScreen();
let thresholdImg = images.threshold(images.grayscale(img), 100, 200);
// 有两张图片, 你只回收了一张
thresholdImg.recycle();


正确的做法: 不要嵌套函数处理图片

$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();
let img = captureScreen();
let grayscaleImg = images.grayscale(img);
let thresholdImg = images.threshold(grayscaleImg, 100, 200);
grayscaleImg.recycle();
thresholdImg.recycle();


其他泄露情况

mat

// mat是opencv常用的数据类型, 需要主动释放
// 请求截图
if (!requestScreenCapture()) {
  toast("请求截图失败");
  exit();
}
let img = captureScreen();
let mat = img.getMat();
// 释放mat资源
mat.release();


ArrayList

let arrayList = new java.util.ArrayList();
// 用完以后, 及时释放资源
arrayList.clear();
arrayList.trimToSize();
arrayList = null;


events.broadcast

"ui";
切换界面1();
function 切换界面1() {
  ui.layout(
    <vertical>
      <text textSize="66sp">界面1</text>
      <button id="btn1">按钮1</button>
    </vertical>
  );
  ui.btn1.click(function () {
    log("切换界面2");
    切换界面2();
  });
  log("界面1显示出来了");
  events.broadcast.emit("界面名称", "界面1");
  events.broadcast.on("界面名称", function (name) {
    log("界面名称: " + name);
  });
}
function 切换界面2() {
  ui.layout(
    <vertical>
      <text textSize="66sp">界面2</text>
      <button id="btn2">按钮2</button>
    </vertical>
  );
  ui.btn2.click(function () {
    log("切换界面1");
    切换界面1();
  });
  log("界面2显示出来了");
  events.broadcast.emit("界面名称", "界面2");
  events.broadcast.on("界面名称", function (name) {
    log("界面名称: " + name);
  });
}


日志如下

09-06 08:49:37.177 main/V: 开始运行[remote://main.js] 
09-06 08:49:37.250 main/D: 界面1显示出来了 
09-06 08:49:37.261 main/D: 界面名称: 界面1 
09-06 08:49:38.826 main/D: 切换界面2 
09-06 08:49:38.831 main/D: 界面2显示出来了 
09-06 08:49:38.837 main/D: 界面名称: 界面2 
09-06 08:49:38.837 main/D: 界面名称: 界面2 
09-06 08:49:39.977 main/D: 切换界面1 
09-06 08:49:39.991 main/D: 界面1显示出来了 
09-06 08:49:39.994 main/D: 界面名称: 界面1 
09-06 08:49:39.995 main/D: 界面名称: 界面1 
09-06 08:49:39.996 main/D: 界面名称: 界面1 
09-06 08:49:41.565 main/D: 切换界面2 
09-06 08:49:41.579 main/D: 界面2显示出来了 
09-06 08:49:41.583 main/D: 界面名称: 界面2 
09-06 08:49:41.584 main/D: 界面名称: 界面2 
09-06 08:49:41.585 main/D: 界面名称: 界面2 
09-06 08:49:41.586 main/D: 界面名称: 界面2 
09-06 08:49:42.872 main/D: 切换界面1 
09-06 08:49:42.890 main/D: 界面1显示出来了 
09-06 08:49:42.897 main/D: 界面名称: 界面1 
09-06 08:49:42.901 main/D: 界面名称: 界面1 
09-06 08:49:42.902 main/D: 界面名称: 界面1 
09-06 08:49:42.902 main/D: 界面名称: 界面1 
09-06 08:49:42.903 main/D: 界面名称: 界面1 


总结
函数每执行一次, 就注册一次广播events.broadcast, 所以不应当在函数中注册广播, 除非你及时释放广播监听

释放指定的广播监听

"ui";
let listener1;
let listener2;
切换界面1();
function 切换界面1() {
  if (listener2) {
    events.broadcast.removeListener("界面名称", listener2);
  }
  ui.layout(
    <vertical>
      <text textSize="66sp">界面1</text>
      <button id="btn1">按钮1</button>
    </vertical>
  );
  ui.btn1.click(function () {
    log("切换界面2");
    切换界面2();
  });
  log("界面1显示出来了");
  events.broadcast.on("界面名称", function (name) {
    log("界面名称: " + name);
  });
  events.broadcast.emit("界面名称", "界面1");
  listener1 = events.broadcast.listeners("界面名称")[0];
}
function 切换界面2() {
  if (listener1) {
    events.broadcast.removeListener("界面名称", listener1);
  }
  ui.layout(
    <vertical>
      <text textSize="66sp">界面2</text>
      <button id="btn2">按钮2</button>
    </vertical>
  );
  ui.btn2.click(function () {
    log("切换界面1");
    切换界面1();
  });
  log("界面2显示出来了");
  events.broadcast.on("界面名称", function (name) {
    log("界面名称: " + name);
  });
  events.broadcast.emit("界面名称", "界面2");
  listener2 = events.broadcast.listeners("界面名称")[0];
}


animator不移除监听会内存泄漏

设置animator监听

let animator = ValueAnimator.ofInt(255, 100);
animator.setDuration(animatorDuration); //动画时间
// setRepeatMode(int value)用于设置循环模式,取值为ValueAnimation.RESTART时,表示正序重新开始,当取值为ValueAnimation.REVERSE表示倒序重新开始。
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addListener(
  new AnimatorListenerAdapter({
    onAnimationStart: function (animation) {
      log("animation start");
    },
    onAnimationEnd: function (animation) {
      log("animation end");
    },
    onAnimationRepeat: function (animation) {
      log("animation repeat");
      // themeColor = rndColor();
    },
    onAnimationCancel: function (animation) {
      log("animation cancel");
      // themeColor = rndColor();
    },
  })
);
animator.addUpdateListener(
  new ValueAnimator.AnimatorUpdateListener({
    onAnimationUpdate: function (valueAnimator) {
      let value = valueAnimator.getAnimatedValue();
      setGradientBorder(value, themeColor);
    },
  })
);


移除animator监听

events.on("exit", function () {
  log("exit start");
  animator.removeAllUpdateListeners();
  animator.removeAllListeners();
  animator.cancel();
  animator = null;
  w.close();
  log("exit end");
});


参考

JavaScript中的垃圾回收和内存泄漏

名人名言

思路是最重要的, 其他的百度, bing, stackoverflow, 安卓文档, autojs文档, 最后才是群里问问

--- 牙叔教程


声明

部分内容来自网络

本教程仅用于学习, 禁止用于其他用途

相关文章
|
测试技术 Android开发
autojs横屏截图的正确姿势
牙叔教程 简单易懂
3381 0
|
Android开发
Autox.js 脚本开发环境搭建,从案例到打包apk(详细流程)
Autox.js 脚本开发环境搭建,从案例到打包apk(详细流程)
3734 0
|
Android开发
autojs修改悬浮窗按钮点击事件
牙叔教程 简单易懂
1644 0
|
Android开发
autojs下拉刷新
牙叔教程 简单易懂
1171 0
|
人工智能 前端开发 Java
autojs非常见函数1
牙叔教程 简单易懂
2455 0
|
人工智能 Linux Shell
用命令修改宝塔面板修改默认端口
用命令修改宝塔面板修改默认端口
|
4月前
|
编解码 数据安全/隐私保护
手机录制脚本自动执行, 免root屏幕录制脚本,自动脚本精灵app【autojs】
自动创建保存目录确保路径存在 动态生成带时间戳的文件名避免重复
|
9月前
|
弹性计算 Linux 数据安全/隐私保护
幻兽帕鲁palworld自建游戏联机服务器,使用阿里云价格真优惠!
阿里云提供幻兽帕鲁(Palworld)游戏服务器搭建服务,支持4核16G(8人在线)和8核32G(20人在线)两种配置,带宽10M,价格低至70元/月起。通过阿里云计算巢服务,可实现一键购买与自动部署,简单快捷。玩家只需在本地安装STEAM客户端并登录游戏,输入服务器IP及端口8211即可联机游玩。教程详细涵盖服务器选择、配置设置及游戏接入全流程,助你轻松开启帕鲁冒险之旅!
|
存储 移动开发 前端开发
【Uniapp 专栏】Uniapp 架构设计与原理探究
【5月更文挑战第12天】Uniapp是一款用于跨平台移动应用开发的框架,以其高效性和灵活性脱颖而出。它基于HTML、CSS和Vue.js构建视图层,JavaScript处理逻辑层,管理数据层,实现统一编码并支持原生插件扩展。通过抽象平台特性,开发者能专注于业务逻辑,提高开发效率。尽管存在兼容性和复杂性挑战,但深入理解其架构设计与原理将助力开发者创建高质量的跨平台应用。随着技术进步,Uniapp将继续在移动开发领域扮演重要角色。
650 1
【Uniapp 专栏】Uniapp 架构设计与原理探究
|
安全 开发工具 Android开发
3种方式自动化控制APP
自动化控制APP不管是在工作还是生活方面,都可以帮助我们高效地完成任务,节省时间和精力。本文主要介绍自动化控制APP的3种常用方式。
3种方式自动化控制APP