autojs文件互传(模拟器和手机)(一)

740 阅读3分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第5篇文章,点击查看活动详情

牙叔教程 简单易懂

发送文件效果演示

手机发送文件给模拟器

模拟器发送文件给手机

思路

要实现互传, 那么两端必须通信, 通信采用什么方式呢?

经测试,

用autojs的nodejs在真机上开起服务, 模拟器是可以访问到的;

用autojs的nodejs在模拟器上开起服务, 真机访问不到;

因此, 我们就不能在模拟器用nodejs开启服务了, 因为真机访问不到;

所以, 我们只在手机开启服务, 模拟器只发送请求;

手机端开启socket服务,

模拟器也用socket与手机通信;

有一个问题要注意, 模拟器上, autojs不能运行命令

npm i --no-bin-links koa

会报错: initialize houdini failed

因此, 我们写完代码后, 要打包以后使用;

打包以后安装到模拟上, 可以正常运行

以上是文件互传的通信设计思路, 下面来看看代码怎么写:

nodejs的socket例子

服务端

const net = require("net");
const port = 8000;
const hostname = "127.0.0.1";
// const hostname = null;

// 定义两个变量, 一个用来计数,一个用来保存客户端
let clients = {};
let clientName = 0;

// 创建服务器
const server = new net.createServer();

server.on("connection", (client) => {
    client.name = ++clientName; // 给每一个client起个名
    clients[client.name] = client; // 将client保存在clients

    client.on("data", function (msg) {
        //接收client发来的信息
        console.log(`客户端${client.name}发来一个信息:${msg}`);
    });

    client.on("error", function (e) {
        //监听客户端异常
        console.log("client error" + e);
        client.end();
    });

    client.on("close", function () {
        delete clients[client.name];
        console.log(`客户端${client.name}下线了`);
    });
});

server.listen(port, hostname, function () {
    console.log(`服务器运行在:http://${hostname}:${port}`);
});

客户端

const net = require("net");

const socket = new net.Socket();

const port = 8000;

const hostname = "127.0.0.1";

socket.setEncoding = "UTF-8";

socket.connect(port, hostname, function () {
    socket.write("hello 大家好~~");
});

socket.on("data", function (msg) {
    console.log(msg);
});

socket.on("error", function (error) {
    console.log("error" + error);
});

socket.on("close", function () {
    console.log("服务器端下线了");
});

然后在终端运行

node socket-server.js

再开一个终端, 运行

node socket-client.js

控制台显示内容

\

基本的socket建立起来了, 下面要考虑实际情况

连接的实际情况

假如有一个手机, 电脑上再开一个模拟器, 两者使用同一个无线网络;

现在要实现文件互传, 用户先打开哪个? 后打开哪个?

我们是不知道的, 并且, 还要判断是不是模拟器,

怎么判断是不是模拟器

importClass(android.net.Uri);
importClass(android.os.Build);
function isSimulator() {
    let url = "tel:" + "123456";
    let intent = new Intent();
    intent.setData(Uri.parse(url));
    intent.setAction(Intent.ACTION_DIAL);
    // 是否可以处理跳转到拨号的 Intent
    let canCallPhone = intent.resolveActivity(context.getPackageManager()) != null;
    return (
        Build.FINGERPRINT.startsWith("generic") ||
        java.lang.String(Build.FINGERPRINT.toLowerCase()).contains("vbox") ||
        java.lang.String(Build.FINGERPRINT.toLowerCase()).contains("test-keys") ||
        java.lang.String(Build.MODEL).contains("google_sdk") ||
        java.lang.String(Build.MODEL).contains("Emulator") ||
        java.lang.String(Build.MODEL).contains("MuMu") ||
        java.lang.String(Build.MODEL).contains("virtual") ||
        java.lang.String(Build.SERIAL).equalsIgnoreCase("android") ||
        java.lang.String(Build.MANUFACTURER).contains("Genymotion") ||
        (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) ||
        "google_sdk".equals(Build.PRODUCT) ||
        context.getSystemService(context.TELEPHONY_SERVICE).getNetworkOperatorName().toLowerCase().equals("android") ||
        !canCallPhone
    );
}

let simulator = isSimulator();
if (simulator) isSimulator = "当前是模拟器环境";
else isSimulator = "当前是真机环境";
log(isSimulator);

\

\

UI显示连接的客户端

假设先点开手机

正常启动socket服务端, UI上应该显示一个等待别人连接的画面, 谁连接了就显示出来

我们先画一个头像

\

然后画圆圈, 从小到大

\

把圆慢慢变成透明

\

让圆环动起来

\

再绘制一个客户端头像

\

UI基本就这样, 后面有需要再添加其他的按钮或者列表之类的

模拟器怎么发现手机端

模拟器一定是看客户端, 因为我用的雷电模拟器局域网ip是 172 开头,

手机是192开头, 不在同一个网段;

模拟器不知道手机的前三位, 因此, 我们前三位就固定下来是 192.168.5.

或者用一个输入框, 让用户可编辑, 作为教程来说, 简单一些, 就固定即即可;

那么模拟器怎么才能知道手机最后一位呢?

我们可以遍历最后一位 2~254

\

子网掩码默认为255.255.255.0掩码,意思就是前面三个位置已经固定,IP地址最多有255个。如果子网掩码是255.255.0.0 那IP地址就是255X255=65025.一般255个已经足够了。

C类地址

C类地址的表示范围为:192.0.0.0~223.255.255.255,默认网络掩码为:255.255.255.0;C类地址分配给小型网络,如一般的局域网和校园网,它可连接的主机数量是最少的,采用把所属的用户分为若干的网段进行管理。C类网络用前三组数字表示网络的地址,最后一组数字作为网络上的主机地址。

一般同一个局域网,前三位都是一样的, 我这里前三位是 192.168.5

那么我们要发现同一局域网可用的ip时, 我们可以发送请求遍历2~254,

可以用ping, 也可以用http, 我测试ping的时候, ping不通

用java代码或者autojs自带的命令行都ping不通 , 网上说可能是台式机禁用了ICMP回显功能之类的吧

pingCmd = "ping -c 1 -w 0.5 "; //其中 -c 1为发送的次数,-w 表示发送后等待响应的时间
locAddress = "192.168.5.";
// current_ip = locAddress + "21";
current_ip = "192.168.5.20";
p = pingCmd + current_ip;
run = java.lang.Runtime.getRuntime(); //获取当前运行环境,来执行ping,相当于windows的cmd
log(p);
proc = run.exec(p);
result = proc.waitFor();
if (result == 0) {
    log("连接成功" + current_ip);
} else {
    log("连接失败" + current_ip);
}

那么我就不用ping了, 用http, 也不用auotjs的http, 用nodejs会快一点, 毕竟是异步的;

ping不需要端口, http需要端口, 端口我们也用固定的, 随便选个 5001-65535

就选 53182 , 随便打了几个数字

手机服务端监听

我发现写教程, 不能考虑那么多场景, 不然要花好多时间才能写完

用的还是上面那个服务端代码

大家要注意, 我用的雷电模拟器和手机不在同一个网段,

手机ping不了模拟器, 所以只能手机当服务端, 模拟器只能当客户端

const server = new net.createServer();
server.on("connection", (client) => {
    ...
});
server.listen(port, hostname, function () {
    console.log(`服务器运行在:http://${hostname}:${port}`);
});

模拟器遍历端口

"nodejs";
var net = require("net");
var Socket = net.Socket;
var ip = "192.168.5";
var port = 53182;
var scan = function (host, cb) {
    var socket = new Socket();
    var status = null;
    socket.setTimeout(1500);
    socket.on("connect", function () {
        socket.end();
        cb && cb(null, host);
    });
    socket.on("timeout", function () {
        socket.destroy();
        cb && cb(new Error("timeout"), host);
    });
    socket.on("error", function (err) {
        cb && cb(err, host);
    });
    socket.on("close", function (err) {});
    socket.connect(port, host);
};
for (var i = 2; i < 254; i++) {
    scan(ip + "." + i, function (err, host) {
        if (err) {
            console.log("Not found", host);
            return;
        }
        console.log("Found: ", host);
    });
}

遍历的日志

09-18 12:40:39.890 Thread-34/D: Found:  192.168.5.30 
09-18 12:40:41.246 Thread-34/D: Not found 192.168.5.19 
09-18 12:40:41.249 Thread-34/D: Not found 192.168.5.20 
09-18 12:40:41.251 Thread-34/D: Not found 192.168.5.21 
...
09-18 12:40:41.269 Thread-34/D: Not found 192.168.5.28 
09-18 12:40:41.270 Thread-34/D: Not found 192.168.5.29 
09-18 12:40:41.278 Thread-34/D: process exit: 0 
09-18 12:40:41.279 Script-7 Main [/storage/emulated/0/脚本/findipclient.js]/V: 

我的手机ip是192.168.5.30, 模拟器是可以发现我的手机ip的, 同时nodejs是异步的, 速度很快, 最多2秒, 255个端口就遍历完了

篇幅太长, 下一节再说传输文件

环境

设备: 小米11pro
Android版本: 12
雷电模拟器:9.0.17
Android版本: 9
Autojs版本: 9.2.13

名人名言

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

声明

部分内容来自网络 本教程仅用于学习, 禁止用于其他用途

微信公众号 牙叔教程