【原创】如何在安卓系统下使用Delphi开发的APP运行ADB命令

其实这个功能极少有人会用吧,大部分用Delphi开发安卓程序都是做一些和UI打交道的前端程序,而我又偏偏拿Delphi在做一些偏硬件应用类的程序。

今天碰到的问题是这样的,我的rk3588的开发板上有2块100M的以太网卡,一块是原生百兆以太网 eth0,而另外一块是挂载为USB设备的百兆以太网 eth1

安卓系统只支持1块网卡的持久化设置,一般都是原生网卡,在我这里就是eth0,而另外一块USB网卡是需要输入ADB命令来设置的,而且还不支持持久化,也就是你把网线拔了,这块网卡的设置就被安卓系统自动清掉了,下次你再接通网线后必须再设置一次,非常头大。

于是乎,我想在Delphi开发的程序中来设置,这样我的程序就可以接管,方便太多了,但问题来了,Delphi如何运行外部的ADB命令呢?

干了差不多1天,尝试了很多种方法都以失败告终,Delphi无法直接调用ADB的命令,那怎么办呢,我想到了笨但好用的方法,就是先用java写一个函数,传入需要运行的命令字符串,由java代码来调用,最后再由Delphi来调用这个java的函数。

于是我就朝这个方向尝试了,先经过了一些折腾最终写出了一个java代码,并通过编译生成了jar文件,java代码如下:

import android.util.Log;
import java.io.*;
public class ShellHelper {

    public static int execRootCommand(String command) {
        try {
            Process process = Runtime.getRuntime().exec("su");
            DataOutputStream os = new DataOutputStream(process.getOutputStream());
            os.writeBytes(command + "\n");
            os.writeBytes("exit\n");
            os.flush();

            // 读取输出(略)
            process.waitFor();
            return 0;
        } catch (Exception e) {
            Log.e("ShellHelper", "execRootCommand error: ", e);
            return -1;
        }
    }

    public static int execCommand(String command) {
        try {
            Process process = Runtime.getRuntime().exec(new String[]{command});
            int exitCode = process.waitFor();
            return exitCode;
        } catch (Exception e) {
            Log.e("ShellHelper", "execCommand error: ", e);
            return -1;
        }
    }

    public static String execRootCommandWithResult(String command) {
        Process process = null;
        BufferedReader reader = null;
        DataOutputStream os = null;

        try {
            // 1. 获取 root shell
            process = Runtime.getRuntime().exec("su");

            // 2. 准备输入输出流
            os = new DataOutputStream(process.getOutputStream());
            reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream())
            );

            // 3. 执行命令
            os.writeBytes(command + "\n");
            os.writeBytes("exit\n");
            os.flush();

            // 4. 读取输出
            StringBuilder output = new StringBuilder();
            char[] buffer = new char[4096];
            int read;
            while ((read = reader.read(buffer)) > 0) {
                output.append(buffer, 0, read);
            }

            // 5. 等待执行完成
            process.waitFor();
            return output.toString();

        } catch (Exception e) {
            Log.e("ShellHelper", "execRootCommandWithResult error: ", e);
            return "ERROR: " + e.getMessage();
        } finally {
            // 6. 清理资源
            try {
                if (os != null) os.close();
                if (reader != null) reader.close();
                if (process != null) process.destroy();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

这个java类中一共有3个函数:

execRootCommand();  //运行需要root权限的命令调用这个函数

execCommand();  //运行普通用户权限的命令调用这个函数

execRootCommandWithResult();  //运行root权限的命令且会返回运行打印信息的

函数写完后编译生成 shellutils.jar

把 shellutils.jar 导入到Delphi工程的Lib库中:

可以直接打开 shellutils.jar 看到里面的函数声明及实现

然后使用老猫 Jar转Pas工具生成Delphi的接口pas文件:

//====================================================
//
//  转换来自JarOrClass2Pas(原JavaClassToDelphiUnit)
//  原始作者:ying32
//  QQ: 1444386932、396506155
//  Email:yuanfen3287@vip.qq.com
//
//  修改 By:Flying Wang & 爱吃猪头肉
//  请不要移除以上的任何信息。
//  请不要将本版本发到城通网盘,否则死全家。
//
//  Email:1765535979@qq.com
//  QQ Group:165232328
//
//  生成时间:2025/4/29 20:06:43
//  工具版本:1.0.2018.2.26
//
//====================================================
unit Androidapi.JNI.ShellHelper;

interface

uses
  Androidapi.JNIBridge, 
  Androidapi.JNI.JavaTypes;


type

// ===== Forward declarations =====

  JShellHelper = interface; //ShellHelper

// ===== Forward SuperClasses declarations =====


// ===== Interface declarations =====

  JShellHelperClass = interface(JObjectClass)
  ['{A1A50C39-E343-4390-8F51-3EF2038C766E}']
    { static Property Methods }

    { static Methods }
    {class} function init: JShellHelper; cdecl;
    {class} function execRootCommand(command: JString): Integer; cdecl;
    {class} function execCommand(command: JString): Integer; cdecl;
    {class} function execRootCommandWithResult(command: JString): JString; cdecl;

    { static Property }
  end;

  [JavaSignature('ShellHelper')]
  JShellHelper = interface(JObject)
  ['{CF8884FA-055C-4FA2-BB04-57289F79352C}']
    { Property Methods }

    { methods }

    { Property }
  end;

  TJShellHelper = class(TJavaGenericImport<JShellHelperClass, JShellHelper>) end;

implementation

procedure RegisterTypes;
begin
  TRegTypes.RegisterType('Androidapi.JNI.ShellHelper.JShellHelper', 
    TypeInfo(Androidapi.JNI.ShellHelper.JShellHelper));
end;


initialization
  RegisterTypes;

end.

最后再写一下调用代码:

procedure TvgsTrackerMainFrm.Button25Click(Sender: TObject);
var
  LCommand: JString;
  LExitCode: Integer;
  s: string;
begin
  try
    LCommand := StringToJString('/data/local/seteth1');
    LExitCode := TJShellHelper.JavaClass.execRootCommand(LCommand);
//    s:=JStringToString(TJShellHelper.JavaClass.execRootCommandWithResult(LCommand));
//    ShowMessage(s);
    if LExitCode = 0 then
      ShowMessage('脚本执行成功')
    else
      ShowMessage('执行失败,错误码: ' + IntToStr(LExitCode));
  except
    on E: Exception do
      ShowMessage('调用异常: ' + E.Message);
  end;
end;

不过这里还漏了ADB的网卡配置脚本,一并贴出来

#!/system/bin/sh
ip addr flush dev eth1
ip addr add 192.168.1.129/24 broadcast 192.168.1.255 dev eth1
ip route add 192.168.1.0/24 dev eth1
ip route add default via 192.168.1.1 dev eth1
ip rule add from 192.168.1.0/24 lookup main
ip rule add to 192.168.1.0/24 lookup main
ip link set eth1 up

OK,就这么简单的搞定了,现在使用起来就非常的爽了,可以同时跨IP网段的拉取2个视频流,也可以从eth0网段推流到eth1网段,真是又趟过一个坑了呀。

欢迎Delphi的老人们一起来交流,加入 Delphi玩转AI (974857430) 群一起来变年轻吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值