RPA 之 Appium.Net 自动化控制 Android App

Appium 是一个开源、跨平台的测试自动化工具,适用于原生、混合以及移动 Web 和桌面应用程序。我们支持模拟器(iOS)、模拟器(Android)和真实设备(iOS、Android、Windows、Mac)

总的来说,其实三端都支持的,啥都可以干,Windows、Mac、Android、Ios,Web 都支持。

主要是我的手机是安卓 黑鲨手机,所以,就拿安卓手机来进行测试。.

另外,我这边尽量以.Net C#代码为主,也会提示如何用 Node.js 或者 java 敲代码,巴拉巴拉。

Appium 之安卓环境

适用于 Android 的 UiAutomator2 驱动程序

需要配置

  1. 1. 为您的平台正确安装和配置了 Java 8

  2. 2. 最低要求的 Android SDK Build Tools 版本为 24

装个 Android Studio 就差不多了,再配置一下 SDK 的版本,这一块,可以网上找资料。

Appium 需要的应用软件

  1. 1. Appium GUI Desktop (服务端)

  2. 2. Appium Inspector (客户端能查看 UIA 结构 xpath)

以下是具体的地址:

https://github.com/appium/appium-desktop/releases/tag/v1.22.3-4

https://github.com/appium/appium-inspector/releases/tag/v2022.5.4

下载安装后,是这个样子的

Appium GUI Desktop

RPA 之 Appium.Net 自动化控制 Android App

Appium Inspector

RPA 之 Appium.Net 自动化控制 Android App

手机开发者模式打开 Debug 调试模式

RPA 之 Appium.Net 自动化控制 Android App

打开 Debug 模式,然后,USB 线连接到电脑上,通过 adb 命令来查看设备的连接状态.

adb 的地址大概在 D:\Android\android-sdk\platform-tools 你自己的 android-sdk 下面。

当然,如果你电脑上 adb.exe 多的话,可以任选一个,都是可以的。

电脑 adb.exe 连接手机

.\adb.exe devices

通过此命令来查看 当前连接的设备信息(可以是虚拟机,不过我用的是真机)

大致如下:

RPA 之 Appium.Net 自动化控制 Android App

我的安卓设备名字就是 (2ff57aa0) 也可能是一个 IP 地址。

开启 Appium GUI Desktop 服务

RPA 之 Appium.Net 自动化控制 Android App

这个服务是需要配置 Android 相关信息的。

RPA 之 Appium.Net 自动化控制 Android App

我这边配置如上。

这时候,就可以直接开启服务了。

RPA 之 Appium.Net 自动化控制 Android App

就像下边这样子

RPA 之 Appium.Net 自动化控制 Android App

其中这个里面就是服务端的控制台,里面会经过内置的 http 协议,很多问题也可以从这个里面查看。

大致实现应用逻辑

大致的业务逻辑是这样的,我会打开安卓里的快手短视频 APP,然后,划拉两下,然后,录个视频,顺便,截个图,最后,退出应用。

要做的事情就是

  1. 1. 如何打开快手的 APP

  2. 2. 如何划拉视频

  3. 3. 如何录制视频

  4. 4. 如何截图

  5. 5. 如何退出应用

会围绕着,这几个点进行展开操作。

如何打开快手 APP

首先我们得获取手机快手 APP 的 PackageName 和启动的主 ActivityName,包名可以通过应用的详细信息里找到。但是,启动的 ActivityName 当下现在只能先通过 ADB 的命令获取。

我们先打开快手 APP,然后,执行以下 ADB 命令(要先保证 设备是上线的)

 .\adb.exe -s 2ff57aa0 shell dumpsys window w |findstr \/ |findstr name=

其中 2ff57aa0 是我手机的设备 id。找不到的可以执行上边【电脑 adb.exe 连接手机】这一步进行获取

执行上面后,结果如下:

RPA 之 Appium.Net 自动化控制 Android App

这里就看到了快手 APP 的 PackageName 和 主 ActivityName 。

com.smile.gifmaker/com.yxcorp.gifshow.HomeActivity

那么 ,配置里的

  1. 1. appPackage : com.smile.gifmaker

  2. 2. appActivity : com.yxcorp.gifshow.HomeActivity

Appium Inspector 找相应的元素

打开应用

打开应用,然后,填写如下信息

RPA 之 Appium.Net 自动化控制 Android App

最少需要填写这一个条件,当然还有其他条件,可以参考官方

{
  "platformName": "Android"
}

界面大致介绍

RPA 之 Appium.Net 自动化控制 Android App
  1. 1. 手机界面临时截图,显示速度有点慢大致 2 秒才显示出来。

  2. 2. 选择视频中的元素,7 和 9 就会出现相应的结构和 Xpath

  3. 3. 是滑动操作(点一下是起始点,点第二下是目标点,松手后,有动作)

  4. 4. 点击的坐标点

  5. 5. 刷新当前 1 的截图

  6. 6. 录制动作(很实用,支持 js,java,python,就是不支持 C# ,不过我就是用 C#写的)

  7. 7. 元素结构列表类似于谷歌游览器的 F12

  8. 8. 具体的元素实际信息有 Xpath 信息

  9. 9. 对当前元素进行点击

我们录制一个 往下滑的视频

RPA 之 Appium.Net 自动化控制 Android App

可以看到,我们已经录制了相关的脚本

js%20的大致如下:

driver.touchAction([
  {action: 'press', x: 434, y: 1400},
  {action: 'moveTo', x: 561, y: 447},
  'release'
]);
driver.touchAction([
  {action: 'press', x: 525, y: 1547},
  {action: 'moveTo', x: 571, y: 613},
  'release'
]);

java%20的大致如下:

(new TouchAction(driver))
  .press(PointOption.point(434, 1400}))
  .moveTo(PointOption.point(561, 447}))
  .release()
  .perform();
 
(new TouchAction(driver))
  .press(PointOption.point(525, 1547}))
  .moveTo(PointOption.point(571, 613}))
  .release()
  .perform();

python%20的大致如下:

actions = ActionChains(driver)
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
actions.w3c_actions.pointer_action.move_to_location(434, 1400)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.move_to_location(561, 447)
actions.w3c_actions.pointer_action.release()
actions.perform()
    
actions = ActionChains(driver)
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
actions.w3c_actions.pointer_action.move_to_location(525, 1547)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.move_to_location(571, 613)
actions.w3c_actions.pointer_action.release()
actions.perform()

基于Appium.Net的开发

如果是node.js%20那就是%20webdriverio%20的开发,基于.Net的开发那就是%20Appium.Net

先安装Nuget包

Install-Package Appium.WebDriver -Version 4.3.1

整体业务代码如下:

        static void Main(string[] args)
        {
            var url = new Uri("http://127.0.0.1:4723/wd/hub");
            var capabilities = new AppiumOptions();
            capabilities.AddAdditionalCapability(MobileCapabilityType.AutomationName, AutomationName.AndroidUIAutomator2);
            capabilities.AddAdditionalCapability(MobileCapabilityType.PlatformName, "Android");
            capabilities.AddAdditionalCapability(MobileCapabilityType.PlatformVersion, "10");
            capabilities.AddAdditionalCapability(MobileCapabilityType.DeviceName, "2ff57aa0");

            var _driver = new AndroidDriver<AppiumWebElement>(url, capabilities, TimeSpan.FromSeconds(180));
            _driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);

            //启动快手APP
            _driver.StartActivity("com.smile.gifmaker", "com.yxcorp.gifshow.HomeActivity");
            //滑动两次
            var touchAction = new TouchAction(_driver);
            touchAction.Press(507, 1612).Wait(1000).MoveTo(576, 500).Release().Perform();
            Thread.Sleep(5 * 1000);

            var touchAction2 = new TouchAction(_driver);
            touchAction2.Press(434, 1622).Wait(1000).MoveTo(494, 564).Release().Perform();
            Thread.Sleep(5 * 1000);

            //录屏,目前没声音
            _driver.StartRecordingScreen(AndroidStartScreenRecordingOptions
            .GetAndroidStartScreenRecordingOptions()
            .WithTimeLimit(TimeSpan.FromSeconds(20)));
            var result = _driver.StopRecordingScreen();
            byte[] decode = Convert.FromBase64String(result);
            //录屏的MP4保存到本地
            var fileName = "VideoRecording_test.mp4";
            File.WriteAllBytes(fileName, decode);

            //截屏
            var Screen = _driver.GetScreenshot();
            using (MemoryStream memoryStream = new MemoryStream(Screen.AsByteArray))
            {
                using var img = Image.FromStream(memoryStream);
                img.Save("123.jpg", ImageFormat.Jpeg);
            }
            //结束app
            _driver.TerminateApp("com.smile.gifmaker");
            _driver?.Quit();
            Console.ReadLine();
        }

基于 Node.js Appium 的开发

const fs = require('fs');
const wdio = require("webdriverio");

const opts = {
  path: '/wd/hub',
  port: 4723,
  capabilities: {
    platformName: "Android",
    platformVersion: "10",
    deviceName: "2ff57aa0",
    automationName: "UiAutomator2"
  }
};

async function main() {
  const driver = await wdio.remote(opts);
  await driver.startActivity("com.smile.gifmaker", "com.yxcorp.gifshow.HomeActivity");
  await driver.pause(2000);
  await driver.touchAction([
    {action: 'press', x: 507, y: 1612},
    {action: 'moveTo', x: 576, y: 500},
    'release'
  ]);
  await driver.pause(2000);
  await driver.touchAction([
    {action: 'press', x: 507, y: 1612},
    {action: 'moveTo', x: 576, y: 500},
    'release'
  ]);
  
  
  await driver.startRecordingScreen();
  let screen = await driver.stopRecordingScreen();
  let videoBinary = Buffer.from(screen, 'base64');
  await fs.writeFileSync('./video.mp4', videoBinary);

  let screenshot = await driver.takeScreenshot();
  let screenshotBinary = Buffer.from(screenshot, 'base64');
  await fs.writeFileSync('./screenshot.jpg', screenshotBinary);
  await driver.terminateApp("com.smile.gifmaker");
  await driver.deleteSession();
}
main();

node.js 我试了,有一些bug,应该是库的问题,但是整体逻辑目前没啥问题,有需要的朋友可以自行解决。

效果如下:

脚本的录屏和脚本的截图

RPA 之 Appium.Net 自动化控制 Android App

手机的操作界面

 

总结

RPA的东西,说容易也容易,说难也难,操作看着挺容易,内部的东西,还是挺麻烦的。

代码地址

https://github.com/kesshei/AppiumDemo.git

https://gitee.com/kesshei/AppiumDemo.git

参考文档

https://appium.io/

一键三连呦!,感谢大佬的支持,您的支持就是我的动力!

 

Appium%20Inspector

 

手机开发者模式打开%20Debug%20调试模式

 

打开%20Debug%20模式,然后,USB%20线连接到电脑上,通过%20adb%20命令来查看设备的连接状态.

adb%20的地址大概在%20D:\Android\android-sdk\platform-tools%20你自己的%20android-sdk%20下面。

当然,如果你电脑上%20adb.exe%20多的话,可以任选一个,都是可以的。

电脑%20adb.exe%20连接手机

.\adb.exe devices

通过此命令来查看%20当前连接的设备信息(可以是虚拟机,不过我用的是真机)

大致如下:

 

我的安卓设备名字就是%20(2ff57aa0)%20也可能是一个%20IP%20地址。

开启%20Appium%20GUI%20Desktop%20服务

 

这个服务是需要配置%20Android%20相关信息的。

 

我这边配置如上。

这时候,就可以直接开启服务了。

就像下边这样子

RPA 之 Appium.Net 自动化控制 Android App

其中这个里面就是服务端的控制台,里面会经过内置的 http 协议,很多问题也可以从这个里面查看。

大致实现应用逻辑

大致的业务逻辑是这样的,我会打开安卓里的快手短视频 APP,然后,划拉两下,然后,录个视频,顺便,截个图,最后,退出应用。

要做的事情就是

  1. 1. 如何打开快手的 APP

  2. 2. 如何划拉视频

  3. 3. 如何录制视频

  4. 4. 如何截图

  5. 5. 如何退出应用

会围绕着,这几个点进行展开操作。

如何打开快手 APP

首先我们得获取手机快手 APP 的 PackageName 和启动的主 ActivityName,包名可以通过应用的详细信息里找到。但是,启动的 ActivityName 当下现在只能先通过 ADB 的命令获取。

我们先打开快手 APP,然后,执行以下 ADB 命令(要先保证 设备是上线的)

 .\adb.exe -s 2ff57aa0 shell dumpsys window w |findstr \/ |findstr name=

其中 2ff57aa0 是我手机的设备 id。找不到的可以执行上边【电脑 adb.exe 连接手机】这一步进行获取

执行上面后,结果如下:

RPA 之 Appium.Net 自动化控制 Android App

这里就看到了快手 APP 的 PackageName 和 主 ActivityName 。

com.smile.gifmaker/com.yxcorp.gifshow.HomeActivity

那么 ,配置里的

  1. 1. appPackage : com.smile.gifmaker

  2. 2. appActivity : com.yxcorp.gifshow.HomeActivity

Appium Inspector 找相应的元素

打开应用

打开应用,然后,填写如下信息

RPA 之 Appium.Net 自动化控制 Android App

最少需要填写这一个条件,当然还有其他条件,可以参考官方

{
  "platformName": "Android"
}

界面大致介绍

RPA 之 Appium.Net 自动化控制 Android App
  1. 1. 手机界面临时截图,显示速度有点慢大致 2 秒才显示出来。

  2. 2. 选择视频中的元素,7 和 9 就会出现相应的结构和 Xpath

  3. 3. 是滑动操作(点一下是起始点,点第二下是目标点,松手后,有动作)

  4. 4. 点击的坐标点

  5. 5. 刷新当前 1 的截图

  6. 6. 录制动作(很实用,支持 js,java,python,就是不支持 C# ,不过我就是用 C#写的)

  7. 7. 元素结构列表类似于谷歌游览器的 F12

  8. 8. 具体的元素实际信息有 Xpath 信息

  9. 9. 对当前元素进行点击

我们录制一个 往下滑的视频

RPA 之 Appium.Net 自动化控制 Android App

可以看到,我们已经录制了相关的脚本

RPA 之 Appium.Net 自动化控制 Android App

js 的大致如下:

driver.touchAction([
  {action: 'press', x: 434, y: 1400},
  {action: 'moveTo', x: 561, y: 447},
  'release'
]);
driver.touchAction([
  {action: 'press', x: 525, y: 1547},
  {action: 'moveTo', x: 571, y: 613},
  'release'
]);

java 的大致如下:

(new TouchAction(driver))
  .press(PointOption.point(434, 1400}))
  .moveTo(PointOption.point(561, 447}))
  .release()
  .perform();
 
(new TouchAction(driver))
  .press(PointOption.point(525, 1547}))
  .moveTo(PointOption.point(571, 613}))
  .release()
  .perform();

python 的大致如下:

actions = ActionChains(driver)
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
actions.w3c_actions.pointer_action.move_to_location(434, 1400)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.move_to_location(561, 447)
actions.w3c_actions.pointer_action.release()
actions.perform()
    
actions = ActionChains(driver)
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
actions.w3c_actions.pointer_action.move_to_location(525, 1547)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.move_to_location(571, 613)
actions.w3c_actions.pointer_action.release()
actions.perform()

基于Appium.Net的开发

如果是node.js 那就是 webdriverio 的开发,基于.Net的开发那就是 Appium.Net

先安装Nuget包

Install-Package Appium.WebDriver -Version 4.3.1

整体业务代码如下:

        static void Main(string[] args)
        {
            var url = new Uri("http://127.0.0.1:4723/wd/hub");
            var capabilities = new AppiumOptions();
            capabilities.AddAdditionalCapability(MobileCapabilityType.AutomationName, AutomationName.AndroidUIAutomator2);
            capabilities.AddAdditionalCapability(MobileCapabilityType.PlatformName, "Android");
            capabilities.AddAdditionalCapability(MobileCapabilityType.PlatformVersion, "10");
            capabilities.AddAdditionalCapability(MobileCapabilityType.DeviceName, "2ff57aa0");

            var _driver = new AndroidDriver<AppiumWebElement>(url, capabilities, TimeSpan.FromSeconds(180));
            _driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);

            //启动快手APP
            _driver.StartActivity("com.smile.gifmaker", "com.yxcorp.gifshow.HomeActivity");
            //滑动两次
            var touchAction = new TouchAction(_driver);
            touchAction.Press(507, 1612).Wait(1000).MoveTo(576, 500).Release().Perform();
            Thread.Sleep(5 * 1000);

            var touchAction2 = new TouchAction(_driver);
            touchAction2.Press(434, 1622).Wait(1000).MoveTo(494, 564).Release().Perform();
            Thread.Sleep(5 * 1000);

            //录屏,目前没声音
            _driver.StartRecordingScreen(AndroidStartScreenRecordingOptions
            .GetAndroidStartScreenRecordingOptions()
            .WithTimeLimit(TimeSpan.FromSeconds(20)));
            var result = _driver.StopRecordingScreen();
            byte[] decode = Convert.FromBase64String(result);
            //录屏的MP4保存到本地
            var fileName = "VideoRecording_test.mp4";
            File.WriteAllBytes(fileName, decode);

            //截屏
            var Screen = _driver.GetScreenshot();
            using (MemoryStream memoryStream = new MemoryStream(Screen.AsByteArray))
            {
                using var img = Image.FromStream(memoryStream);
                img.Save("123.jpg", ImageFormat.Jpeg);
            }
            //结束app
            _driver.TerminateApp("com.smile.gifmaker");
            _driver?.Quit();
            Console.ReadLine();
        }

基于 Node.js Appium 的开发

const fs = require('fs');
const wdio = require("webdriverio");

const opts = {
  path: '/wd/hub',
  port: 4723,
  capabilities: {
    platformName: "Android",
    platformVersion: "10",
    deviceName: "2ff57aa0",
    automationName: "UiAutomator2"
  }
};

async function main() {
  const driver = await wdio.remote(opts);
  await driver.startActivity("com.smile.gifmaker", "com.yxcorp.gifshow.HomeActivity");
  await driver.pause(2000);
  await driver.touchAction([
    {action: 'press', x: 507, y: 1612},
    {action: 'moveTo', x: 576, y: 500},
    'release'
  ]);
  await driver.pause(2000);
  await driver.touchAction([
    {action: 'press', x: 507, y: 1612},
    {action: 'moveTo', x: 576, y: 500},
    'release'
  ]);
  
  
  await driver.startRecordingScreen();
  let screen = await driver.stopRecordingScreen();
  let videoBinary = Buffer.from(screen, 'base64');
  await fs.writeFileSync('./video.mp4', videoBinary);

  let screenshot = await driver.takeScreenshot();
  let screenshotBinary = Buffer.from(screenshot, 'base64');
  await fs.writeFileSync('./screenshot.jpg', screenshotBinary);
  await driver.terminateApp("com.smile.gifmaker");
  await driver.deleteSession();
}
main();

node.js 我试了,有一些bug,应该是库的问题,但是整体逻辑目前没啥问题,有需要的朋友可以自行解决。

效果如下:

脚本的录屏和脚本的截图

RPA 之 Appium.Net 自动化控制 Android App

手机的操作界面

RPA 之 Appium.Net 自动化控制 Android App

总结

RPA的东西,说容易也容易,说难也难,操作看着挺容易,内部的东西,还是挺麻烦的。

代码地址

https://github.com/kesshei/AppiumDemo.git

https://gitee.com/kesshei/AppiumDemo.git

参考文档

https://appium.io/