内存泄漏的概念
百度百科:
内存泄漏(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"); });
参考
名人名言
思路是最重要的, 其他的百度, bing, stackoverflow, 安卓文档, autojs文档, 最后才是群里问问
--- 牙叔教程
声明
部分内容来自网络
本教程仅用于学习, 禁止用于其他用途