AutoJs6 icon indicating copy to clipboard operation
AutoJs6 copied to clipboard

如何重启autojs6后执行某个脚本?

Open wengzhenquan opened this issue 1 month ago • 10 comments

我发现,当jvm内存占用过高时,autojs6会奔溃。 于是我打算写一个逻辑在代码里:监控jvm内存,当内存快满的时候,重启autojs6,达到释放内存的目的,然后重新执行脚本。

我发现,通过定时任务,广播设置:autojs6启动时,能够达到启动执行脚本的目的。 but,重启要点击autojs6左上角菜单展开的下方“重启”按钮才行。

我自己写的重启代码,成功重启autojs6,但无法触发定时任务“启动时”。

如果通过无障碍服务布局分析,一步步打开菜单去点击“重启”按钮,貌似也可以,但属于最差方案。

方案一:脚本通过命令执行重启和执行脚本,全程无感。 方案二:无感重启+autojs6定时任务“启动时”配合。

方案三:布局分析点击“重启”按钮+autojs6定时任务“启动时”配合。

现在只能做到方案三。

方案二也难以实现,不知道我的重启代码问题在哪儿。 方案一也搞不定

wengzhenquan avatar Nov 01 '25 17:11 wengzhenquan

未来 AutoJs6 会支持 autojs.restart / autojs.exit 方法用于重启或退出应用:

autojs.restart(); // 重启应用
autojs.restart('test-123'); // 重启应用并在启动时自动执行工作目录下的 test-123.js 脚本
autojs.restart(['a', 'b', 'c']); // 重启应用并在启动时自动依次执行工作目录下的 a.js, b.js, c.js 脚本. 但注意 b.js 并不会等待 a.js 执行完毕后才会执行, 除非脚本中使用 engines 模块做了监测
autojs.restart('@'); // 重启应用并在启动时自动执行当前脚本, 即执行了 `autojs.restart('@');` 代码的脚本自身

如果需要的话, 我可以提供上述代码的内部具体实现.

如果可以的话, 请提供你重启代码的关键代码, 我可以帮你分析问题出现的原因.

SuperMonster003 avatar Nov 03 '25 05:11 SuperMonster003

AutoJs6下一个版本有发布计划吗?

function restartAndRunScript(scriptPath) {
    try {
        let packageName = "org.autojs.autojs6";
        let activityManager = context.getSystemService(context.ACTIVITY_SERVICE);
        
        console.log("准备重启 Auto.js 并执行脚本:", scriptPath);
        
        // 1. 先强制停止应用
        activityManager.killBackgroundProcesses(packageName);
        console.log("已强制停止 Auto.js");
        sleep(2000);
        
        // 2. 通过 PackageManager 获取启动 Intent 并传递脚本路径
        let pm = context.getPackageManager();
        let intent = pm.getLaunchIntentForPackage(packageName);
        
        if (intent) {
            // 设置 Intent 标志
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
            
            // 传递脚本路径参数
            intent.putExtra("auto_run_script", scriptPath);
            intent.putExtra("timestamp", Date.now());
            
            // 启动应用
            context.startActivity(intent);
            console.log("已发送重启命令,脚本路径:", scriptPath);
            
            // 3. 退出当前脚本
            setTimeout(() => engines.myEngine().forceStop(), 500);
        } else {
            throw new Error("无法获取启动 Intent");
        }
        
    } catch (e) {
        console.error("系统服务方式失败:", e);
        // 回退到 Shell 命令方式
    }
}



// 使用示例
let targetScript = "/storage/emulated/0/Download/AJS6脚本/小社/【小社脚本】启动程序.js";
restartAndRunScript(targetScript);

这段代码只有重启功能,重启后无法执行脚本。 也无法触发AutoJS6自带的定时任务→广播→AutoJS6 启动时

wengzhenquan avatar Nov 03 '25 08:11 wengzhenquan

我想要内部实现,JS版的,可以吗?

wengzhenquan avatar Nov 03 '25 08:11 wengzhenquan

function restartAndRunScript(scriptPath) {
    try {
        let packageName = "org.autojs.autojs6";
        let activityManager = context.getSystemService(context.ACTIVITY_SERVICE);
        
        console.log("准备重启 Auto.js 并执行脚本:", scriptPath);
        
        // 1. 先强制停止应用
        activityManager.killBackgroundProcesses(packageName);
        console.log("已强制停止 Auto.js");
        sleep(2000);
        
        // 2. 通过 PackageManager 获取启动 Intent 并传递脚本路径
        let pm = context.getPackageManager();
        let intent = pm.getLaunchIntentForPackage(packageName);
        
        if (intent) {
            // 设置 Intent 标志
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
            
            // 传递脚本路径参数
            intent.putExtra("auto_run_script", scriptPath);
            intent.putExtra("timestamp", Date.now());
            
            // 启动应用
            context.startActivity(intent);
            console.log("已发送重启命令,脚本路径:", scriptPath);
            
            // 3. 退出当前脚本
            setTimeout(() => engines.myEngine().forceStop(), 500);
        } else {
            throw new Error("无法获取启动 Intent");
        }
        
    } catch (e) {
        console.error("系统服务方式失败:", e);
        // 回退到 Shell 命令方式
    }
}



// 使用示例
let targetScript = "/storage/emulated/0/Download/AJS6脚本/小社/【小社脚本】启动程序.js";
restartAndRunScript(targetScript);

提供的上述代码似乎只是停止了后台进程, 没有杀掉 AutoJs6 前台进程, 因此不是真正意义上的 "退出" 应用. 另外 startActivity 如果希望清空包含当前任务的任务栈的话, 应该加上 CLEAR_TASK 标志位. 而 engines.myEngine().forceStop() 只是停止了脚本引擎, 并未让 AutoJs6 进程退出.

SuperMonster003 avatar Nov 04 '25 10:11 SuperMonster003

我想要内部实现,JS版的,可以吗?

可以的.

以下提供两个脚本, 一个包含 restart/exit 核心功能 (你可以作为模块导出相关函数), 另一个暂时将其命名为 'run-after-restart-if-needed.js', 它作为检测 restart 方法写入 storages 中需要自启的脚本名称列表, 你可以将它设置为一个定时任务 (AutoJs6 启动时).

const AlarmManager = android.app.AlarmManager;
const SystemClock = android.os.SystemClock;
const ActivityManager = android.app.ActivityManager;

const UNIQUE_REQUEST_CODE = 1001;
const STORAGE_NAME = 'autojs6_restart_helper';
const STORAGE_KEY_SCRIPTS_AFTER_RESTART = 'scripts_after_app_restart';

function getLaunchIntent() {
    let pkgName = context.getPackageName();
    let pm = context.getPackageManager();
    let launchIntent = pm.getLaunchIntentForPackage(pkgName);
    if (launchIntent == null) return null;

    launchIntent.setAction(pkgName + '.action.RESTART_UNIQUE');
    launchIntent.addFlags(
        Intent.FLAG_ACTIVITY_NEW_TASK
        | Intent.FLAG_ACTIVITY_CLEAR_TOP
        | Intent.FLAG_ACTIVITY_CLEAR_TASK,
    );
    return launchIntent;
}

function finishAllAppTasksSafely() {
    try {
        let am = context.getSystemService(Context.ACTIVITY_SERVICE);
        let appTasks = am.getAppTasks();
        if (appTasks != null) {
            let it = appTasks.iterator();
            while (it.hasNext()) {
                try {
                    let task = it.next();
                    task.finishAndRemoveTask();
                } catch (e) {
                    /* Ignored. */
                }
            }
        }
    } catch (e) {
        /* Ignored. */
    }
}

function tryCleanupNative() {
    tryInvoke(org.autojs.autojs.ui.floating.FloatyWindowManger, 'hideCircularMenuAndSaveState');
    tryInvoke(org.autojs.autojs.ui.enhancedfloaty.FloatyService, 'stopService');
    tryInvoke(org.autojs.autojs.core.image.capture.ScreenCapturerForegroundService, 'stopService');
    tryInvoke(org.autojs.autojs.AutoJs.getInstance().getScriptEngineService(), 'stopAll')
    tryInvoke(org.autojs.autojs.AutoJs.getInstance(), 'clear')
    tryInvoke(org.autojs.autojs.App.getApp(), 'clear')
    tryInvoke(com.huaban.analysis.jieba.WordDictionaryDatabase.getInstance(context), 'close');
    tryInvoke(com.huaban.analysis.jieba.CharsDictionaryDatabase.getInstance(context), 'close');
    tryInvoke(com.huaban.analysis.jieba.PhrasesDictionaryDatabase.getInstance(context), 'close');
    tryInvoke(org.autojs.autojs.runtime.api.WrappedShizuku, 'onDestroy');
}

function tryInvoke(object, methodName, argList) {
    try {
        if (typeof object[methodName] === 'function') {
            object[methodName].apply(object, argList || []);
        }
    } catch (e) {
        /* Ignored. */
    }
}

function restart() {
    let launchIntent = getLaunchIntent();
    if (!launchIntent) {
        throw new Error('Failed to create launch intent for AutoJs6');
    }

    let args = Array.from(arguments);
    let storage = storages.create(STORAGE_NAME);
    let scriptsToRunAfterRestart = ( /* @IIFE */ () => {
        if (![0, 1].includes(args.length)) {
            throw new Error(`Arguments length (${args.length}) is invalid for restart`);
        }
        let scriptsToRun;
        if (args.length > 0) {
            if (Array.isArray(args[0])) {
                scriptsToRun = args[0];
            } else {
                let s = args[0];
                if (typeof s !== 'string') {
                    throw new Error(`Script ${s} is not a valid name for restart`);
                }
                scriptsToRun = [s];
            }
        }
        let scriptToRunFromSto = storage.get(STORAGE_KEY_SCRIPTS_AFTER_RESTART, null);
        if (Array.isArray(scriptToRunFromSto)) {
            scriptsToRun = scriptToRunFromSto.concat(scriptsToRun);
        }
        return scriptsToRun || [];
    })();

    let list = scriptsToRunAfterRestart
        .map(function (s) {
            return (s || '').trim();
        })
        .filter(function (s) {
            return s.length > 0;
        });
    if (list.length > 0) {
        storage.put(STORAGE_KEY_SCRIPTS_AFTER_RESTART, list.join('\n'));
    }

    let delayMs = 300;
    let triggerAtMillis = SystemClock.elapsedRealtime() + delayMs;
    try {
        let pendingIntent = PendingIntent.getActivity(
            context,
            UNIQUE_REQUEST_CODE,
            launchIntent,
            PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
        );
        let am = context.getSystemService(Context.ALARM_SERVICE);
        am.cancel(pendingIntent);
        am.setExactAndAllowWhileIdle(
            AlarmManager.ELAPSED_REALTIME,
            triggerAtMillis,
            pendingIntent,
        );
    } catch (e) {
        /* Ignored. */
    }

    /* 延时以便 storages 模块完成写入. */
    setTimeout(exit, 500);
}

function exit() {
    tryCleanupNative();
    finishAllAppTasksSafely();
    java.lang.System.exit(0);
}

restart('test-123');

上述示例代码以 'test-123' 为例, 将其作为重启后自动执行的脚本, 如需多个, 可传入数组. 需要注意, 自动执行脚本需要监测, 将下面的脚本加入定时任务, 可以实现监测功能:

const STORAGE_NAME = 'autojs6_restart_helper';
const STORAGE_KEY_SCRIPTS_AFTER_RESTART = 'scripts_after_app_restart';

function runAfterRestartIfNeeded() {
    let storage = storages.create(STORAGE_NAME);
    let value = storage.get(STORAGE_KEY_SCRIPTS_AFTER_RESTART, null);
    if (value == null) return;

    let scripts = String(value)
        .split('\n')
        .map((s) => (s || '').trim())
        .filter((s) => s.length > 0);

    let workingDir = files.cwd();
    let failures = {};

    scripts.forEach(function (script) {
        try {
            let file = toScriptFile(script, workingDir);
            if (file.exists()) {
                engines.execScriptFile(file.getAbsolutePath());
            } else {
                throw new Error('File not found: ' + file.getAbsolutePath());
            }
        } catch (e) {
            failures[toScriptFile(script, workingDir).getAbsolutePath()] = e;
        }
    });

    let keys = Object.keys(failures);
    if (keys.length > 0) {
        dialogs.alert('脚本执行失败', `共 ${keys.length} 项自启动脚本执行失败:\n\n${keys.join('\n')}`, () => {
            keys.forEach((k) => {
                console.error(k);
                console.error('-'.repeat(20));
                console.error(failures[k]);
            });
        });
    }
    storage.remove(STORAGE_KEY_SCRIPTS_AFTER_RESTART);
}

function toScriptFile(inputPath, workingDir) {
    let path = inputPath;
    if (!/\.js$/i.test(path)) {
        path = path + '.js';
    }

    let f = new File(path);
    if (f.exists() || path.startsWith('$remote/')) {
        return f;
    }

    let wk = workingDir;
    if (wk.endsWith('/')) {
        wk = wk.substring(0, wk.length - 1);
    }
    if (f.getAbsolutePath().startsWith(wk)) {
        return f;
    }
    return new File(new File(workingDir), path);
}

runAfterRestartIfNeeded();

如有其他问题, 欢迎继续讨论.

SuperMonster003 avatar Nov 04 '25 10:11 SuperMonster003

第一段代码,报错。 但能重启,重启后没反应,效果跟我前面那段代码一样,重启后不会执行指定脚本,也不会触发定时任务→广播→AutoJS6启动时。

第二段代码还没测试,这个要开什么样的定时任务?每分钟启动一次吗? 是不是必须配合第二段监控,第一段代码才能生效?

wengzhenquan avatar Nov 04 '25 12:11 wengzhenquan

将第二段代码保存到任意文件中, 例如 run-after-restart-if-needed.js, 文件管理器找到这个文件, 选择 "定时任务", "广播触发任务", "AutoJs6 启动时".

这样, 它将每次随 AutoJs6 启动而启动, 作为 restart 方法传入参数的监测文件. 基本原理是 restart 方法将参数写入 storages 模块创建的 SharedPreferences, 然后 run-after-restart-if-needed.js 文件读取其存入的数据执行对应的脚本.

SuperMonster003 avatar Nov 04 '25 12:11 SuperMonster003

我还是等待更新吧。

wengzhenquan avatar Nov 05 '25 16:11 wengzhenquan

检查了代码,似乎没有截图等内存泄漏的操作,跑几天感觉程序就自动退出了。期望自身重启api尽快上线。

fr0zenrain avatar Nov 06 '25 01:11 fr0zenrain

检查了代码,似乎没有截图等内存泄漏的操作,跑几天感觉程序就自动退出了。期望自身重启api尽快上线。

// JVM内存信息
function getMemoryInfo() {
    let runtime = java.lang.Runtime.getRuntime();
    let maxMemory = runtime.maxMemory(); // 最大内存
    let totalMemory = runtime.totalMemory(); //总内存
    let freeMemory = runtime.freeMemory(); // 空闲内存
    let usedMemory = totalMemory - freeMemory; //已使用

    return {
        maxMemory: maxMemory,
        totalMemory: totalMemory,
        freeMemory: freeMemory,
        usedMemory: usedMemory,
        maxMemoryMB: (maxMemory / (1024 * 1024)).toFixed(0),
        totalMemoryMB: (totalMemory / (1024 * 1024)).toFixed(2),
        freeMemoryMB: (freeMemory / (1024 * 1024)).toFixed(2),
        usedMemoryMB: ((usedMemory) / (1024 * 1024)).toFixed(2),
        usagePercentage: ((usedMemory / maxMemory) * 100).toFixed(2)
    };
}

// 打印jvm内存信息
function printJVMMemory() {
    let info = getMemoryInfo();
    console.error("JVM:" +
        info.usedMemoryMB + "/" +
        info.maxMemoryMB +
        " MB (" + info.usagePercentage + "%)");
}

监控jvm,使用过程中jvm内存总体递增,当100%时,AutoJS6就会oom,或奔溃。 重启才能释放内存

wengzhenquan avatar Nov 06 '25 01:11 wengzhenquan