adb助手 - 为adb提供简单的上下文提示

偶尔用一下adb,因不常用, 经常遇到命令记不清的情况, 网上找了几个adb助手,都偏向于界面化,做的很好,但对学习adb本身帮助不大.所以搞个工具方便我这样一年用不了几次,或初学adb的人.

预期功能:

  1. 输入adb命令时能显示上下文提示
  2. 上下文内容可扩充
  3. 一次能执行多个命令
  4. 能同时对多个设备操作.
  5. 能显示手机屏幕,并可在电脑上进行简单的操作

其他考虑:

  1. 尽可能少的依赖环境,最好只一个可执行文件.
  2. 尽可能不依赖第三方库和数据库等

效果图:

adb Helper

关键点

  • 一.显示上下文提示框

思路: 当输入"空格" 后,取光标最近的一个单词为关键字, 以此关键字筛选相关内容,并在当前光标下方弹出窗口显示. 此步骤需解决的主要问题:

a). 取关键字(因不想使用第三方控件, 所以使用TextBox作为输入框, 试过RichTextBox,刷新太慢,放弃了)

    //注: box 为TextBox控件

    int curLineIdx = box.GetLineIndexFromCharacterIndex(box.CaretIndex); //取当前行标
    int lineBegin = box.GetCharacterIndexFromLineIndex(curLineIdx); 
    string lineText = box.GetLineText(curLineIdx); //取当前行的全部文本
    if (box.CaretIndex < lineBegin) return;

    int linePos = box.CaretIndex - lineBegin; //取光标位置
    if (lineText.Length < linePos) return;

    lineText = lineText.Substring(0, linePos); //截取从本行开始到当前光标的文本

    string[] spor = { " ", "\t" };
    string[] buf = lineText.Split(spor, StringSplitOptions.None);
    
    string curToken = buf.Last(); //正在输入的字符(即: 当前行,距离光标最近的空格之后 的输入内容)

b). 取弹出窗口的位置

   POINT pt;
   NativeMethods.GetCaretPos(out pt); //调用 windows API , 取当前光标位置
   IntPtr wnd = NativeMethods.GetHandle(this);
   NativeMethods.ClientToScreen(wnd, ref pt);
   pt.Y += this._inputboxLineHeight; // TextBox的行高
   this._pop.Left = pt.X; //_pop为 window,即弹出的提示窗口
   this._pop.Top = pt.Y;
   this._pop.Show();

c). 取TextBox的行高. (因字体原因,例如电脑上没有默认的字体,可能导致行高不同, 所以不能设为固定值)

//注: tb为TextBox控件
Typeface tc = new Typeface(tb.FontFamily, tb.FontStyle, tb.FontWeight, tb.FontStretch);

FormattedText ft = new FormattedText("A", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, tc, tb.FontSize, tb.Background); //随便输入一个字符,取它的实际高度

return (int)Math.Round(ft.Height, 0); //输入框的行高
  • 二.上下文管理

思路: 上下文保存在文本文件中, 运行时用一个数组存储, 输入关键字后筛选出相关的内容即可,

a).上下文结构. (相当于一对多的二维表)

  //上下文结构
  public class AdbContextValue
    {
        public string KeyWord { get; set; } //关键字

        public IEnumerable<ContextValue> Context { get; set; } //对应的列表
    }  

b). 上下文缓存

 //上下文缓存
  public class ContextCache
    {
        volatile static ContextCache _instance = null;
        static readonly object locker = new object();

        public static ContextCache Instance
        {
            get
            {
                lock (locker)
                {
                    if (_instance == null) _instance = new ContextCache();
                    return _instance;
                }
            }
        }

        private ContextCache()
        {
            this._adbContextCache = LoadCacheFromCsv(); //读取本地的缓存文件
        }

        List<AdbContextValue> _adbContextCache;

        //用关键字索引
        public IEnumerable<ContextValue> this[string Key]
        {
            get
            {
                foreach (var item in this._adbContextCache)
                {
                    if (item.KeyWord == Key)
                        return item.Context;
                }
                return null;
            }
        }
    }

 

  • 三.执行adb

思路: 用cmd执行adb命令, 取输出即可,(需处理中文,特殊符号等问题). 如果直接用Process 执行adb,参数传递有问题,返回值也不好取.

a). 用cmd执行adb.

       public string ExecuteArgs(string args, AdbDevice device = null, int timeout = 10000)
        {
            if (string.IsNullOrEmpty(this._adbPath)) throw new Exception("请先在app.json中设置adb路径");
            if (string.IsNullOrEmpty(args)) throw new ArgumentNullException("无效adb参数");

            using (Process proc = new Process())
            {
                proc.StartInfo.FileName = "cmd.exe";
                proc.StartInfo.UseShellExecute = false;
                proc.StartInfo.RedirectStandardInput = true;
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.RedirectStandardError = true;
                proc.StartInfo.CreateNoWindow = true;
                proc.EnableRaisingEvents = true;
                proc.Start();
                string cmd = string.Empty;
                if (device == null)
                    cmd = $"\"{ this._adbPath}\" {Encoding.Default.GetString(Encoding.UTF8.GetBytes(args.Replace("|", "||")))}";
                else
                    cmd = $"\"{ this._adbPath}\" -s {device.DeviceID} {Encoding.Default.GetString(Encoding.UTF8.GetBytes(args.Replace("|", "||")))}";

                proc.StandardInput.WriteLine("echo off");
                proc.StandardInput.WriteLine(cmd);
                proc.StandardInput.WriteLine("exit"); 
                StringBuilder sb = new StringBuilder();
                proc.ErrorDataReceived += (ss, ee) =>
                {
                    if (!string.IsNullOrEmpty(ee.Data) && ee.Data.IndexOf("Microsoft") < 0 && ee.Data.IndexOf(this._head) < 0 && ee.Data != "exit")
                        sb.AppendLine(ee.Data);
                };
                proc.OutputDataReceived += (ss, ee) =>
                {
                    if (!string.IsNullOrEmpty(ee.Data) && ee.Data.IndexOf("Microsoft") < 0 && ee.Data.IndexOf(this._head) < 0 && ee.Data != "exit")
                        sb.AppendLine(ee.Data);
                };

                proc.BeginErrorReadLine();
                proc.BeginOutputReadLine();
                proc.WaitForExit(timeout);

                if (sb.Length > 2 && sb[sb.Length - 1] == '\n' && sb[sb.Length - 2] == '\r')
                    sb.Remove(sb.Length - 2, 2);

                return sb.ToString();
            }
        }
  • 四.  显示手机屏幕

思路: 用连续截屏的方式模拟手机屏幕. 同时结合屏幕坐标与shell input 模拟操作.

在取输出流的时候走了点弯路,有的设备正常,有的设备截屏无效,查了半天找不出原因, 后来截取各手机的黑屏并保存为文件,用WinHex对比才发现有微小的区别(按道理,相同分辨率的纯黑图片应该完全相同),可能是不同安卓版本导致的.

   public byte[] ScreenShotByte(AdbDevice device = null)
        {
            if (string.IsNullOrEmpty(this._adbPath)) throw new Exception("请先在app.json中设置adb路径");
            byte[] rev;
            using (Process proc = new Process())
            {
                proc.StartInfo.FileName = this._adbPath;
                proc.StartInfo.UseShellExecute = false;
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.CreateNoWindow = true;
                string cmd = "shell screencap -p"; //不输出文件,减少IO时间,也避免频繁写文件影响硬盘寿命
                if (device != null)
                    cmd = $"-s {device.DeviceID} {cmd}";

                proc.StartInfo.Arguments = cmd;
                proc.Start();

                using (MemoryStream memstream = new MemoryStream())
                {
                    byte[] buffer = new byte[1];
                    while (proc.StandardOutput.BaseStream.Read(buffer, 0, 1) > 0) //因StandardOutput.ReadToEnd返回的是带编码的数据,故直接取Stream
                    {
                        memstream.Write(buffer, 0, 1);
                    }
                    rev = memstream.ToArray();
                }
                proc.WaitForExit(5000);
            }

            if (rev != null && rev.Length>1024)
            {
                if (device != null && device.VerNum <= 6)
                    return FixAdbBytesB(rev); //不同设备输出格式不同, 测试机器不多,最低只有安卓4设备
                else
                    return FixAdbBytesA(rev); //修正控制台输出的0xOA 0xOD问题
            }
            else
                return null;
        }

修正换行符问题

 //旧手机的输出\r\r\n和\r\n都有, 可能是版本原因
        private byte[] FixAdbBytesB(byte[] data)
        {
            int lenght = data.Length;
            int endPos = lenght - 1;
            int endPos2 = lenght - 2;
            List<byte> list = new List<byte>();
            for (int i = 0; i < lenght; i++)
            {
                if (i < endPos && data[i] == 0x0D && data[i + 1] == 0x0A)
                {
                    list.Add(0x0A);
                    i++;
                    continue;
                }
                else if (i < endPos2 && data[i] == 0x0D && data[i + 1] == 0x0D && data[i + 2] == 0x0A)
                {
                    list.Add(0x0A);
                    i += 2;
                    continue;
                }
                list.Add(data[i]);
            }
            return list.ToArray();
        }

本想加一个自动分析设备可用命令的功能, 例如自动查询设备可用指令,并筛选掉没权限的, 查询设备上的app并自动扩充到该设备的上下文缓存等, 想了想觉得工作量太大,放弃了. 等哪天来劲头了再搞

自动脚本功能搞了一半也去掉了,因为觉得实际意义不大, 直接用bat文件处理更简单方便.

详见代码:

    AdbHelper(CSDN) 

    百度网盘 提取码: 8888

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页