C#通过adb传输安卓设备数据

xiaoxiao2021-02-28  69

转自:http://blog.csdn.net/omguare/article/details/49804323

最近因为项目需要,研究了一下C#调用adb传输和推送数据到安卓设备上。 查了资料发现安卓设备与电脑连接,传输数据有两种方式: 1.通过adb 2.socket。 市面上安卓设备管理工具如:豌豆荚、XX手机助手大多采用socket方式,监听某个端口,通过socket传输数据。socket优点是速度快,不会被语言和编码限制,缺点是开发量大,难懂(至少对于大多数开发者是这样的)。

下面介绍一下adb使用的一些经验和技巧,文章最后附有我写的一个程序。


1.ADB简介及命令

ADB:Android Debug Bridge,安卓调试桥接工具。(连接设备时要保证pc上安装有该设备的驱动)传输数据文件常用的命令有:

adb shell 进入shell界面pull拷贝文件到电脑: pull sdcard/a.jpg d:\a.jpgpush拷贝文件到设备:push d:\a.jpg sdcard/a.jpgmkdir创建文件夹:mkdir xxx mkdir –p xxx/xxx(递归创建文件夹)ls 列出当前文件夹下所有文件和文件夹 *

cd转到指定文件夹下

进入shell 转到目录,列出目录中的文件和文件夹

pull、push命令不必进入shell

创建文件夹

*由于cmd字符集的问题,汉字命名的文件在ls时出现乱码,原因是cmd采用gbk,Android系统采用的UTF-8所致,可以更改字符集和展示字体,见此。Ps:在程序中推荐大家使用英文和数字来命名文件和文件夹,免得出现不必要的辛苦,因为通过修改命名方式和修改Android底层adb的源码再次编译相比,真是不要太简单!

2.C#程序

首先说明一下我的开发环境:VS2010 .Net Framework 4.0 文件结构

在程序中最好集成adb.exe,使用起来也很方便使用log4net记录数据传输时的返回信息ProcessHelper类用来监控cmd进程中输入输出流等 贴一下ProcessHelper类的代码: using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Text; using System.Threading; namespace AndroidDataTransform { public class ProcessHelper { private static Process GetProcess() { var mProcess = new Process(); mProcess.StartInfo.CreateNoWindow = true; mProcess.StartInfo.UseShellExecute = false; mProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; mProcess.StartInfo.RedirectStandardInput = true; mProcess.StartInfo.RedirectStandardError = true; mProcess.StartInfo.RedirectStandardOutput = true; mProcess.StartInfo.StandardOutputEncoding = Encoding.UTF8; return mProcess; } private static string ReadStandardOutputLine(Process p) { var tmp = new StringBuilder(); //当下一次读取时,Peek可能为-1,但此时缓冲区其实是有数据的。正常的Read一次之后,Peek就又有效了。 if (p.StandardOutput.Peek() == -1) tmp.Append((char)p.StandardOutput.Read()); while (p.StandardOutput.Peek() > -1) { tmp.Append((char)p.StandardOutput.Read()); } return tmp.ToString(); } /// <summary> /// 读取数据的时候等待时间,等待时间过短时,可能导致读取不出正确的数据。 /// </summary> public static int WaitTime = 50; /// <summary> /// 连续运行模式,支持打开某程序后,持续向其输入命令,直到结束。 /// </summary> /// <param name="exePath"></param> /// <param name="args"></param> /// <param name="moreArgs"></param> /// <returns></returns> public static RunResult RunAsContinueMode(string exePath, string args, string[] moreArgs) { var result = new RunResult(); try { using (var p = GetProcess()) { p.StartInfo.FileName = exePath; p.StartInfo.Arguments = args; p.Start(); //先输出一个换行,以便将程序的第一行输出显示出来。 //如adb.exe,假如不调用此函数的话,第一行等待的shell@android:/ $必须等待下一个命令输入才会显示。 p.StandardInput.WriteLine(); result.OutputString = ReadStandardOutputLine(p); result.MoreOutputString = new Dictionary<int, string>(); for (int i = 0; i < moreArgs.Length; i++) { p.StandardInput.WriteLine(moreArgs[i] + '\r'); //必须等待一定时间,让程序运行一会儿,马上读取会读出空的值。 Thread.Sleep(WaitTime); result.MoreOutputString.Add(i, ReadStandardOutputLine(p)); } // Do not wait for the child process to exit before // reading to the end of its redirected stream. // p.WaitForExit(); // Read the output stream first and then wait. p.WaitForExit(); result.ExitCode = p.ExitCode; result.Success = true; } } catch (Win32Exception ex) { result.Success = false; //System Error Codes (Windows) //http://msdn.microsoft.com/en-us/library/ms681382(v=vs.85).aspx result.OutputString = string.Format("{0},{1}", ex.NativeErrorCode, SystemErrorCodes.ToString(ex.NativeErrorCode)); } catch (Exception ex) { result.Success = false; result.OutputString = ex.ToString(); } return result; } public static RunResult Run(string exePath, string args) { var result = new RunResult(); try { using (var p = GetProcess()) { p.StartInfo.FileName = exePath; p.StartInfo.Arguments = args; p.Start(); //获取正常信息 if (p.StandardOutput.Peek() > -1) result.OutputString = p.StandardOutput.ReadToEnd(); //获取错误信息 if (p.StandardError.Peek() > -1) result.OutputString = p.StandardError.ReadToEnd(); // Do not wait for the child process to exit before // reading to the end of its redirected stream. // p.WaitForExit(); // Read the output stream first and then wait. p.WaitForExit(); result.ExitCode = p.ExitCode; result.Success = true; } } catch (Win32Exception ex) { result.Success = false; //System Error Codes (Windows) //http://msdn.microsoft.com/en-us/library/ms681382(v=vs.85).aspx result.OutputString = string.Format("{0},{1}", ex.NativeErrorCode, SystemErrorCodes.ToString(ex.NativeErrorCode)); } catch (Exception ex) { result.Success = false; result.OutputString = ex.ToString(); } return result; } public class RunResult { /// <summary> /// 当执行不成功时,OutputString会输出错误信息。 /// </summary> public bool Success; public int ExitCode; public string OutputString; /// <summary> /// 调用RunAsContinueMode时,使用额外参数的顺序作为索引。 /// 如:调用ProcessHelper.RunAsContinueMode(AdbExePath, "shell", new[] { "su", "ls /data/data", "exit", "exit" }); /// 果:MoreOutputString[0] = su执行后的结果字符串;MoreOutputString[1] = ls ...执行后的结果字符串;MoreOutputString[2] = exit执行后的结果字符串 /// </summary> public Dictionary<int, string> MoreOutputString; public new string ToString() { var str = new StringBuilder(); str.AppendFormat("Success:{0}\nExitCode:{1}\nOutputString:{2}\nMoreOutputString:\n", Success, ExitCode, OutputString); if (MoreOutputString != null) foreach (var v in MoreOutputString) str.AppendFormat("{0}:{1}\n", v.Key, v.Value.Replace("\r", "\\Ⓡ").Replace("\n", "\\Ⓝ")); return str.ToString(); } } } }

AdbHelper类中包括了连接设备、列出指定文件夹、文件的传输

将文件拷贝至设备 若result信息中包含No such file or directory 证明设备上没有该路径。为了保留pc上文件的路径,需要在设备上创建相应的文件夹。例如:通过输出字符串:" failed to copy 'F:\padData\image\1.jpg' to 'sdcard/21at/output/image/1.jpg': No such file or directory"我们要获得目标文件夹是sdcard/21at/output/image/,具体实现见下: /// <summary> /// 将文件拷贝到设备上(不适用于文件夹) /// </summary> /// <param name="deviceNo"></param> /// <param name="pcPath"></param> /// <param name="devPath"></param> /// <returns></returns> public static bool CopyToDevice(string deviceNo, string pcPath, string devPath) { //adb push [-p] <local> <remote> //- copy file/dir to device var result = ProcessHelper.Run(AdbExePath, string.Format("-s {0} push {1} {2}", deviceNo, pcPath, devPath)); m_log.Info("推送PAD时结果:" + result.ToString()); if (result.ExitCode != 0 || (result.OutputString.Contains("failed") && result.OutputString.Contains("No such file or directory")))//若出现设备文件夹不存在的情况,则创建该文件夹 { //构建拷贝后设备路径 int index1 = result.OutputString.IndexOf("failed to copy "); //输出字符串:" failed to copy 'F:\padData\image\1.jpg' to 'sdcard/21at/output/image/1.jpg': No such file or directory" string temp = result.OutputString.Substring(index1); int index2 = temp.IndexOf(devPath); int index3 = temp.IndexOf("': No such file or directory"); string devPath2 = temp.Substring(index2, index3 - index2);//设备图片路径 devPath2 = devPath2.Substring(0, devPath2.LastIndexOf('/')); var moreArgs = new[] { "su", "mkdir -p "+ devPath2, "exit", "exit" }; //shell 方式创建文件夹 result = ProcessHelper.RunAsContinueMode(AdbExePath, string.Format("-s {0} shell", deviceNo), moreArgs); m_log.Info("创建文件夹:" + devPath2); m_log.Info("创建文件夹结果:" + result.ToString()); //再次推送该文件 result = ProcessHelper.Run(AdbExePath, string.Format("-s {0} push {1} {2}", deviceNo, pcPath, devPath)); m_log.Info("再次推送PAD结果:" + result.ToString()); } if (!result.Success || result.ExitCode != 0 || (result.OutputString != null && result.OutputString.Contains("failed"))) { return false; throw new Exception("push 执行返回的结果异常:" + result.OutputString); } return true; } 将文件拷贝至PC /// <summary> /// 拷贝文件到PC上 /// </summary> /// <param name="deviceNo"></param> /// <param name="devPath"></param> /// <param name="pcPath"></param> /// <returns></returns> public static bool CopyFromDevice(string deviceNo, string devPath, string pcPath) { //使用Pull命令将数据库拷贝到Pc上 //adb pull [-p] [-a] <remote> [<local>] var result = ProcessHelper.Run(AdbExePath, string.Format("-s {0} pull {1} {2}", deviceNo, devPath, pcPath)); m_log.Info("推送PC时结果:" + result.ToString()); if (!result.Success || result.ExitCode != 0 || (result.OutputString != null && result.OutputString.Contains("failed"))) { return false; throw new Exception("pull 执行返回的结果异常:" + result.OutputString); } return true; } 列出指定目录 /// <summary> /// 获取指定的目录 /// </summary> /// <param name="deviceNo"></param> /// <param name="path"></param> /// <returns></returns> public static List<string> ListDataFolder(string deviceNo, string path) { var moreArgs = new[] { "su", "ls " + path, "exit", "exit" }; var result = ProcessHelper.RunAsContinueMode(AdbExePath, string.Format("-s {0} shell", deviceNo), moreArgs); m_log.Info("获取路径结果:" + result.ToString()); var itemsString = result.MoreOutputString[1]; var items = itemsString.Split(new[] { "$", "#", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); var itemsList = new List<String>(); foreach (var item in items) { var tmp = item.Trim(); //移除第一行,输入的命令 if (tmp.Contains(moreArgs[1])) continue; //若有Permission denied证明没有该路径,直接退出 if (tmp.Contains(path + ": Permission denied")) break; //移除空白行 if (string.IsNullOrEmpty(tmp)) continue; //移除最后两行的root@android if (tmp.ToLower().Contains("root@") || tmp.ToLower().Contains("shell@")) continue; if (tmp.Equals("su") || tmp.Contains("su: not found"))//移除su continue; itemsList.Add(path + "/" + tmp); } itemsList.Sort(); return itemsList; }

文件app.config中记录了指定的下载的文件夹,以及当前程序标示是上传还是下载

<appSettings> <!--文件路径:实际路径是 sdcard/Tencent或extSdCard/Tencent--> <add key="PadDataPath" value="/Tencent"/> <!--程序是上传(1)还是下载(0)--> <add key="type" value="0"/> </appSettings>

在C#的程序中可通过 System.Configuration.ConfigurationManager.AppSettings["PadDataPath"]方式来获取设置的value值。程序运行图

我写的程序在此。

转载请注明原文地址: https://www.6miu.com/read-1950243.html

最新回复(0)