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之保活
autojs打包的app经常在后台被杀,请做到以下几点来保活: autojs版本号
3148 0
autojs之保活
|
API Android开发
截图授权和投影媒体权限
牙叔教程 简单易懂
1174 0
|
存储 安全 Shell
Git clone 克隆私有项目
Git clone 克隆私有项目
Git clone 克隆私有项目
|
编解码 数据安全/隐私保护
手机录制脚本自动执行, 免root屏幕录制脚本,自动脚本精灵app【autojs】
自动创建保存目录确保路径存在 动态生成带时间戳的文件名避免重复
|
缓存 前端开发 应用服务中间件
Nginx Windows详细安装部署教程
Nginx Windows详细安装部署教程
4772 0
Nginx Windows详细安装部署教程
|
存储 关系型数据库 MySQL
PACS系统 中 dicom 文件在mysql 8.0 数据库中的 存储和读取(pydicom 库使用)
PACS系统 中 dicom 文件在mysql 8.0 数据库中的 存储和读取(pydicom 库使用)
611 2
|
索引 Python
PDF文件页面提取操作小指南
PDF文件页面提取操作小指南
618 4
|
监控 JavaScript 前端开发
autojs,监控屏幕以及获取所有应用
autojs,监控屏幕以及获取所有应用
|
人工智能 数据可视化 BI
超好用的低代码平台:10款可视化工具深度评测
以下是内容摘要,以240个字符以内完成: 了解多款低代码开发平台,如Zoho Creator(提供直观界面、预建模板、工作流定制和多平台支持)、OutSystems(专注高性能、企业安全及多渠道发布)、Mendix(与Microsoft 365集成、丰富的组件和自定义连接器)、Wix Code(无代码Web开发)、Bubble(完全无代码、高度定制化页面)、Knack(快速建库、多角色访问控制)和Caspio(强大的数据库功能和定制报表)。这些平台降低开发门槛,适用于不同业务场景。
1052 10
|
机器学习/深度学习 人工智能 BI
Python计算余弦相似度
Python计算余弦相似度