前几天接到一个需求,我们的客户需要对手机网络接入点进行可用性测试,简单点说就是需要实现Android上的APN配置的添加,APN切换网络模式4G/3G/2G切换,我要调研下写个demo。

  因为是要实现自动化测试,而且得合并到现有的拨测系统(C#项目)成为其中的一个模块,就需要用C#来驱动Android测试。交互方式上首先想到的是撸个代码放Android上,定时从服务端获取任务命令然后执行,嗯,OWIN实现个webapi进行数据交互分分钟的事情,貌似可行。 不过又想到,我们测试万一网络切换坏了,就不能联网了那就完了。这样的话,就不能进行任何手机天线端的网络操作了。接着就想到USB交互 然后找到了这个命令:adb forward tcp:PCPort tcp:Androidport 作用是将当前环境的某个端口与Android的某个端口绑定。这样Android 内部请求Androidport端口号就和请求PC上的PCPort端口一样,反之亦然,手机需要打开USB调试。准备写的时候我又想到,我们做的是无人值守的主动测试,Android一会儿跑过来问问有没有执行命令,一会儿跑过来问问 感觉有点不大好,麻烦别人还得别人惦记着不是我的性格。。。 balabala一番思想斗争后决定用socket交互,Android端做服务端,要做啥 过来说下~~

 

  Android的Server端通讯简要讯码:

  SCServer :接收连接过来的客户端,并且保存到ClientManager中

public class SCServer implements Runnable {static Boolean Startd = false;static Integer Port;static ServerSocket serverSocket = null;ClientManager clientManager = new ClientManager();public SCServer(int port) {Port = port;}@Overridepublic void run() {if (!Startd) {try {serverSocket = new ServerSocket(Port);Startd = true;System.out.println("Startd :" + Port);} catch (IOException e) {e.printStackTrace();}try {while (Startd) {Socket socket = serverSocket.accept();clientManager.AddClient(socket);}} catch (IOException e) {// TODO Auto-generated catch block
                e.printStackTrace();}}}public void RegistCallBack(String comm, CallBack callBack) {CommManager.Add(comm, callBack);}public void UnRegistCallBack(String comm) {CommManager.Remove(comm);}public void Send(Integer clientID, String comm, Map<String, String> msgDatas) {clientManager.SendMsg(clientID, comm, msgDatas);}}

View Code

  ClientManager:保存所有客户端,分配唯一编号,线程运行客户端监听消息,根据编号找到客户端Client 发送消息。

public class ClientManager {static Integer ClientID=0;static Map<Integer, Client> Clients = new HashMap<>();public void AddClient(Socket socket) {Integer clientID= ClientID++;Client clinet = new Client(socket,clientID);new Thread(clinet).start();Clients.put(clientID, clinet);}public void SendMsg(Integer clientID, String comm,Map<String, String> msgDatas) {if (Clients.containsKey(clientID)) {Client client = Clients.get(clientID);client.SendMsg(comm, msgDatas);}}
}

View Code

  Client:数据收发,命令解析。消息的载体是json格式FastJson处理。数据类容转换为Map<String,String>对应的为C#的Dictionary<string, string>

public class Client implements Runnable {private Socket socket;private DataOutputStream dos = null;private BufferedReader brIs = null;private boolean bConnected = false;public Integer ClientID = -1;public Client(Socket socket, int id) {this.socket = socket;this.ClientID = id;}@Overridepublic void run() {try {brIs = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));dos = new DataOutputStream(socket.getOutputStream());System.out.println(this.ClientID + " Start");bConnected = true;while (bConnected) {String str = brIs.readLine();if(str!=null){System.out.println("-------->" + str);JSONObject jb = JSON.parseObject(str);String msgComm = jb.getString("MsgComm");CallBack cb = CommManager.Get(msgComm);if (cb != null) {String msgCBComm = jb.getString("MsgCBComm");Map<String, String> msgDatas = (Map<String, String>) JSON.parse(jb.getString("MsgDatas"));cb.execute(ClientID, msgCBComm, msgDatas);} else {System.out.println("--->MsgComm:[" + msgComm+ "] Can't Find!");}}}} catch (IOException e) {e.printStackTrace();}}public void SendMsg(String comm, String callBackComm,Map<String, String> msgDatas) {Message msg = new Message();msg.MsgCBComm = callBackComm;msg.MsgComm = comm;msg.MsgDatas = msgDatas;String StrJson = JSON.toJSONString(msg);System.out.println("<--------"+StrJson);try {this.dos.writeUTF(StrJson);this.dos.flush();} catch (IOException e) {e.printStackTrace();}}public void SendMsg(String comm, Map<String, String> msgDatas) {SendMsg(comm,"",msgDatas);}
}

View Code

  CommManager:消息命令管理,保存命令关键字与回调的处理方法。

public class CommManager {static Map<String, CallBack> Comms = new HashMap<String, CallBack>();public static void Add(String comm, CallBack callBack) {Comms.put(comm, callBack);}public static CallBack Get(String comm) {if (Comms.containsKey(comm)) {CallBack callBack = Comms.get(comm);return callBack;} else {return null;}}public static void Remove(String comm) {Comms.remove(comm);}
}

View Code

  CallBack:回调接口,返回客户端ID,消息返回命令,接收的消息

public interface CallBack {public void execute(Integer clientID, String callBackComm,Map<String, String> msgDatas);
}

View Code

  Message:交互的消息

public class Message {public String MsgComm;  //传过来的命令public String MsgCBComm;//回应的命令public Map<String,String> MsgDatas=new HashMap<String, String>();//数据
}

View Code

调用方式:

 1 final SCServer sc = new SCServer(57641);
 2
 3         sc.RegistCallBack("DoSth", new CallBack() {
 4             @Override
 5             public void execute(Integer clientID, String callBackComm,Map<String, String> msgDatas) {
 6                     // 执行代码
 7                 msgDatas.clear();
 8                 msgDatas.put("Result", "OK");
 9                 sc.Send(clientID, callBackComm, msgDatas);
10             }
11         });

C#的Client端通讯简要代码

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;namespace LiteSocket
{public class SocketClient{public bool IsConnected = false;private static byte[] result = new byte[2048];string IP;int Port;Thread t_Server;Socket clientSocket;Dictionary<string, Action<string, Dictionary<string, string>>> Comms = new Dictionary<string, Action<string, Dictionary<string, string>>>();public SocketClient(string ip, int port){IP = ip;Port = port;}public void Close(){clientSocket.Close();t_Server.Abort();}public bool Connect(){try{IPAddress ip = IPAddress.Parse(IP);clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);clientSocket.Connect(new IPEndPoint(ip, Port)); //配置服务器IP与端口  t_Server = new Thread(() =>{while (clientSocket.Connected){try{int receiveLength = clientSocket.Receive(result);if (receiveLength > 0){//接收数据处理string msgStr = Encoding.UTF8.GetString(result, 2, receiveLength - 2);Console.WriteLine(msgStr);Message msg = JsonConvert.DeserializeObject<Message>(msgStr);Action<string, Dictionary<string, string>> action = null;if (!Comms.TryGetValue(msg.MsgComm, out action)){Console.WriteLine("MsgComm :" + msg.MsgComm + " 不存在");}else{action(msg.MsgCBComm, msg.MsgDatas); //回调
                                  }}}catch (Exception ex){}}});t_Server.IsBackground = false;t_Server.Start();}catch (Exception ex){Console.WriteLine(ex.Message);}IsConnected = clientSocket.Connected;return IsConnected;}/// <summary>/// 注册回调方法/// </summary>/// <param name="Comm">消息命令</param>/// <param name="CallBack">回调方法</param>public void RegistComm(string Comm, Action<string/*返回消息命令*/, Dictionary<string, string>> CallBack){if (!Comms.ContainsKey(Comm)){Comms.Add(Comm, CallBack);}else{Comms[Comm] = CallBack;}}public void UnRegistComm(string Comm){if (Comms.ContainsKey(Comm)){Comms.Remove(Comm);}}/// <summary>/// 发送数据给服务端,需要返回,回调响应/// </summary>/// <param name="comm">命令消息</param>/// <param name="callBackComm">返回消息</param>/// <param name="msgDatas">消息内容</param>public void PostData(string comm, string callBackComm, Dictionary<string, string> msgDatas){Message m = new Message();m.MsgComm = comm;m.MsgCBComm = callBackComm;m.MsgDatas = msgDatas;string json = JsonConvert.SerializeObject(m);Console.WriteLine(json);if (clientSocket.Connected){clientSocket.Send(Encoding.UTF8.GetBytes(json + "\n"));}else{Console.WriteLine("Connected Is Broken");}}/// <summary>/// 发送命令给服务端,不需要返回数据/// </summary>/// <param name="comm"></param>/// <param name="msgDatas"></param>public void PostData(string comm, Dictionary<string, string> msgDatas){PostData(comm, "", msgDatas);}/// <summary>/// 发送命令给服务端,并等待返回的消息。/// </summary>/// <param name="comm"></param>/// <param name="waitSeconds">命令执行超时时间 默认60s</param>/// <returns></returns>public Dictionary<string, string> SendData(string comm, int waitSeconds = 60){return SendData(comm, new Dictionary<string, string>(), waitSeconds);}/// <summary>/// 发送命令和数据给服务端,并等待返回的消息。/// </summary>/// <param name="comm"></param>/// <param name="msgDatas"></param>/// <param name="waitSeconds">命令执行超时时间 默认60s</param>/// <returns></returns>public Dictionary<string, string> SendData(string comm, Dictionary<string, string> msgDatas, int waitSeconds = 60){DateTime waitTime = DateTime.Now.AddSeconds(waitSeconds);Dictionary<string, string> returnMsgDatas = null;string RdComm = RandomStr(8); //随机生成返回消息命令RegistComm(RdComm, (cbkey, data) =>{returnMsgDatas = data;});Message m = new Message();m.MsgComm = comm;m.MsgCBComm = RdComm;m.MsgDatas = msgDatas;string json = JsonConvert.SerializeObject(m);if (clientSocket.Connected){clientSocket.Send(Encoding.UTF8.GetBytes(json + "\n"));}else{Console.WriteLine("Connect Is Broken");}//等待返回数据double wait = 0.00;while (returnMsgDatas == null && wait<=0){Thread.Sleep(500);wait = (DateTime.Now - waitTime).TotalSeconds;}UnRegistComm(RdComm); //注销命令return returnMsgDatas;}public static string CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";/// <summary>/// 真·随机字符串/// </summary>/// <param name="lenght">长度</param>/// <returns></returns>public string RandomStr(int lenght){StringBuilder sb = new StringBuilder();Random r = new Random(Guid.NewGuid().GetHashCode());for (int i = 0; i < lenght; i++){sb.Append(CHAR[r.Next(25)]);}return sb.ToString();}}
}

Message:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace LiteSocket
{public class Message{public string MsgComm { set; get; }public string MsgCBComm { set; get; }private Dictionary<string, string> _MsgDatas = new Dictionary<string, string>();public Dictionary<string, string> MsgDatas{get { return _MsgDatas; }set { _MsgDatas = value; }}}
}

调用方法:

      SocketClient SC = new SocketClient(ip, port);Dictionary<string, string> Dic_doSth = new Dictionary<string, string>();Dic_doSth.Add("somethingKey", "somethingValue");var result = SC.SendData("DoSth", Dic_doSth);//发送并接收返回数据//ORSC.RegistComm("SthOver", (rekey, value) => { //处理返回数据
            });SC.PostData("DoSth", "SthOver", Dic_doSth); //发送 异步处理返回数据

以上的交互完成了,后面就是业务代码了。APN添加切换 网络模式切换

网上搜了下,得到一个例子:Android开发之APN网络切换 心中暗喜:有前辈给出了解决方案,还有代码实例,这实现起来还不简单么。照猫画虎。。。后发现出了个错:

No permission to write APN settings:

查询了一翻发现android 4.0以上对这一权限进行回收了。我们的测试机为小米4,按照网上说的方法进行了 重新系统签名,系统权限设置均无效,依然会有权限错误,中间为了得到android4.4.4的platform.pk8文件还下载了8G的android 4.4.4源码。可能是MIUI的与android原生的系统签名不一样 总是就是要不没权限 要不安装不上。 网上还有一种方法是 MM编译,得在Linux环境下;Eclipse+NDK配置又是很多的配置,看着教程实在感受不到爱了。。。 索性就放弃了这方案 曲线救国的方式来实现需求-----模拟用户屏幕操作。 adb有个Input命令,可以模拟键盘输入,屏幕点击,屏幕滑动。

adb shell input keyevent “value”
usage: input ...input text <string>input keyevent <key code number or name>input tap <x> <y>input swipe <x1> <y1> <x2> <y2>

常用键:

input keyevent 3    // Home
input keyevent 4    // Back
input keyevent 19  //Up
input keyevent 20  //Down
input keyevent 21  //Left
input keyevent 22  //Right
input keyevent 23  //Select/Ok
input keyevent 24  //Volume+
input keyevent 25  // Volume-
input keyevent 82  // Menu 菜单

抄个这段代码,Android上执行终端命令,Root权限?小米4:—_—

public static void execShellCmd(String cmd) {try {// 申请获取root权限Process process = Runtime.getRuntime().exec("su");OutputStream outputStream = process.getOutputStream();DataOutputStream dataOutputStream = new DataOutputStream(outputStream);dataOutputStream.writeBytes(cmd);dataOutputStream.flush();dataOutputStream.close();outputStream.close();} catch (Throwable t) {t.printStackTrace();}}

那么,当我需要添加一个APN的时候:

Android:

final SCServer sc = new SCServer(57641);sc.RegistCallBack("AddApn", new CallBack() {@Overridepublic void execute(Integer clientID, String callBackComm,Map<String, String> msgDatas) {Intent intent = new Intent(Settings.ACTION_APN_SETTINGS);startActivity(intent);         SystemClock.Sleep(1000);for (int i = 0; i < msgDatas.values().size(); i++) {String strDo = msgDatas.get(i + "");FoolHand.execShellCmd(strDo);Log.d("strDo", strDo);SystemClock.sleep(1000);}msgDatas.clear();msgDatas.put("Result", "OK");sc.Send(clientID, callBackComm, msgDatas);}});        

C#:

public bool AddApn(string Name, string APN){Dictionary<string, string> doSth = new Dictionary<string, string>();int i = 0;doSth.Add((i++).ToString(), "input tap 463 1810");//点击新建doSth.Add((i++).ToString(), "input tap 650 290"); //点击名称doSth.Add((i++).ToString(), "input text " + Name); //输入名称doSth.Add((i++).ToString(), "input tap 846 1040");  //点击确定doSth.Add((i++).ToString(), "input tap 650 470");  //点击APNdoSth.Add((i++).ToString(), "input text " + APN); //输入APNdoSth.Add((i++).ToString(), "input tap 846 1040");  //点击确定doSth.Add((i++).ToString(), "input keyevent 4"); //退出 (弹出保存确认框)doSth.Add((i++).ToString(), "input tap 730 1780");  // 确认保存var result = SC.SendData("AddApn", doSth);if (result["Result"] == "OK"){return true;}else{return false;}}

效果:

sc.RegistCallBack("SetNetMode", new CallBack() {@Overridepublic void execute(Integer clientID, String callBackComm,Map<String, String> msgDatas) {Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);startActivity(intent);for (int i = 0; i < msgDatas.values().size(); i++) {String strDo = msgDatas.get(i + "");FoolHand.execShellCmd(strDo);Log.d("strDo", strDo);SystemClock.sleep(1000);}msgDatas.clear();msgDatas.put("Result", "OK");sc.Send(clientID, callBackComm, msgDatas);}});

切换网络模式Java

public bool ChangeNetMode(string NetMode){Dictionary<string, string> doSth = new Dictionary<string, string>();int i = 0;doSth.Add((i++).ToString(), "input swipe 640 550 640 1440");  //滑到最顶端doSth.Add((i++).ToString(), "input tap 640 430");doSth.Add((i++).ToString(), "input tap 640 1040");switch (NetMode){case "4G":doSth.Add((i++).ToString(), "input tap 640 260");//选择4Gbreak;case "3G":doSth.Add((i++).ToString(), "input tap 640 430");//选择3Gbreak;case "2G":doSth.Add((i++).ToString(), "input tap 640 600");//点击2Gbreak;default:break;}doSth.Add((i++).ToString(), "input keyevent 4");doSth.Add((i++).ToString(), "input keyevent 4");// 640   260 430 var result = SC.SendData("SetNetMode", doSth);if (result["Result"] == "OK"){return true;}else{return false;}}

切换网络模式C#

这玩意模拟键盘输入,所以得记住屏幕位置。

这玩意模拟键盘输入,所以不能录入中文。

源码:  AndroidAPNSettings

转载于:https://my.oschina.net/u/567492/blog/697287

Android与.Net交互模拟用户屏幕操作添加APN和网络4G/3G切换相关推荐

  1. Gremlins.js – 模拟用户随机操作的 JS 测试库

    Gremlins.js 是基于 JavaScript 编写的 Monkey 测试库,支持 Node.js 平台和浏览器中使用.Gremlins.js 随机模拟用户操作:单击窗口中的任意位置,在表格中输 ...

  2. Android与Js交互时,屏幕不适配问题

    //支持javascriptmStarWebView.getSettings().setJavaScriptEnabled(true); // 设置可以支持缩放mStarWebView.getSett ...

  3. live555 android,Android RTSP/UDP“RTSP/1.0 461 Unsupported transport”通过蜂窝网络(4G)

    I am currently working on video streaming via RTSP/UDP for Android devices. My goal is to stream a v ...

  4. android 使用shell模拟触屏_Android随笔之——用shell脚本模拟用户按键、触摸操作...

    之前写过两篇关于Android中模拟用户操作的博客(其实用一篇是转载的),现在就来讲讲用shell脚本来模拟用户按键操作.本次的目标是用shell脚本打开微信并在其搜索框中搜索相关内容. 本文的模拟功 ...

  5. android 滑动过程 触发,android 代码实现模拟用户点击、滑动等操作

    /** * 模拟用户点击 * * @param view 要触发操作的view * @param x 相对于要操作view的左上角x轴偏移量 * @param y 相对于要操作view的左上角y轴偏移 ...

  6. Python爬虫:详解Appium如何爬取手机App数据以及模拟用户操作手势

    目录 Appium 模拟操作 屏幕滑动 屏幕点击 屏幕拖动 屏幕拖拽 文本输入 动作链 实战:爬取微博首页信息 Appium 在前文的讲解中,我们学会了如何安装Appium,以及一些基础获取App元素 ...

  7. Android手机上,利用bat脚本模拟用户操作

    大家可能会遇到这样的情景: #  也许你是一个通过App的销售人员,需要不断靠App的点击率来拿利润 #  也许你是一个个人开发者,想要自己写个脚本点击banner广告 #  也许你是一个业务经理,你 ...

  8. Android SDK上手指南:用户交互

    在这篇教程中,我们将对之前所添加的Button元素进行设置以实现对用户点击的检测与响应.为了达成这一目标,我们需要在应用程序的主Activity类中略微涉及Java编程内容.如果大家在Java开发方面 ...

  9. android跨进程事件注入(程序模拟用户输入)

    转载请注明出处 早想写这篇,一直没空,现在总结下. 需求: 需要在程序内模拟用户输入,比如点击屏幕,或者输入键盘.模拟用户的滑动等.具体的需求,比如测试的时候,测试打开浏览器1000次.或者通过网络发 ...

最新文章

  1. 《Groovy官方文档》1.2安装Groovy
  2. Python语言学习之字母A开头函数使用集锦:assert用法之详细攻略
  3. .NET内存管理五大基础知识
  4. Pytorch 为什么每一轮batch需要设置optimizer.zero_grad
  5. 施一公:如何提高英文的科研写作能力
  6. ss模型复模态的物理意义及adams复模态振型求解
  7. maven如何实现创建带源代码的jar包
  8. vue 导入excel解析_Vue实现Excel导入并解析
  9. 5.3 Zend_Log_Filter
  10. Python学习笔记整理总结【Django】Ajax
  11. android hardware解析
  12. php+tcpdf+表格,PHP使用tcpdf类生成PDF文件
  13. 函数信号发生器的设计与实现_北邮大二上电子电路基础实验报告
  14. opencv-pythons实现图像周长面积(三角形)检测DIY整理
  15. android 微信 耗电吗,微信太耗电了怎么办?微信耗电的两种解决方案
  16. 办公族如何防治鼠标手?
  17. Android 引入第三方字体库的简单使用
  18. crc16校验c语言单片机实现,三种常用的CRC16校验算法的C51程序的优化
  19. (5)Linux基础——opendir/closedir 、readdir、mkdir 、rmdir、getcwd、chdir详细含义用法及介绍(基础)
  20. abacus 基本操作

热门文章

  1. 如何在Windows上打开命令提示符
  2. HTTP cookie格式与约束
  3. day7CSS3新特性
  4. (QT) QUdpSocket / 在QT中通过Winsock2实现UDP通讯
  5. 【VIM】多行缩进空格与删除
  6. MQTTBox下载及设置相关参数
  7. 备忘录照片提取文字怎么操作
  8. CTF杂项题基础(1.文件识别与分离及图片隐写)
  9. AI-WEB-1.0超详细多方法教程
  10. 农村安装隐蔽监控摄像头有必要吗 农村隐蔽式监控有什么优势