Python Python3 UI 自动化通过线程解决安装 apk 时的权限弹窗 (一)

xiaoxiao · 2019年05月25日 · 最后由 sunny0330 回复于 2019年12月17日 · 3887 次阅读

在我们做 UI 自动化之前,需要自动安装 apk,然而很多时候会遇到很多系统弹窗,那么我们可以用 python3 的线程去监控点掉系统弹窗,直到我们的目标 apk 安装成功,再退出这个线程。

# -*- coding: utf-8 -*-
# @author: xiaoxiao
# @date  : 2019/4/6


import threading
import os
import uiautomator2 as u2

driver = u2.connect("882QADT9UWT")

class usb_install_thread(threading.Thread): # 安装确认

    def __init__(self):
        threading.Thread.__init__(self)

    def run(self): # 把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
        self.usb_install()

    # 判断apk是否已经安装
    def is_apk_has_installed(self, package_name, device_id = ''):
        if device_id != '':
            cmd = "adb -s " + str(device_id) + " shell pm list package | grep '" + str(package_name) +"'"
        else:
            cmd = "adb shell pm list package | grep '" + str(package_name) + "'"
        result = os.popen(cmd).read()
        return result

    # 判断app应用是否启动到前台
    def is_activity_started(self, package_name, device_id=''):
        if device_id != '':
            cmd_current_activity = "adb -s %s shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p' | grep 'ActivityRecord'" % device_id
        else:
            cmd_current_activity = "adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p' | grep 'ActivityRecord'"
        cmd_result = str(self.shell(cmd_current_activity))
        # 如果当前应用处于前台或resume后台状态,返回True
        if package_name in cmd_result:
            # 启动app后,如果有系统权限弹窗,也需要点掉它
            if 'GrantPermissionsActivity' in cmd_result:
                return False
            return True
        else:
            return False

    # 等待元素出现,默认等待10s
    def wait_for_element_visible(self, text, timeout=10):
        # return bool
        return driver(text = text).wait(timeout=timeout)

    # 点掉系统弹窗
    def usb_install(self):
        print("Begin to install apk...\n")
        while True:
            # 如果apk安装成功,则退出
            if self.is_apk_has_installed('com.xxx'):
                # print("The apk has been installed~")
                break
            # 点掉弹窗"允许"、"确认"、"继续安装"等
            try:
                driver(text="允许").click()
            except:
                pass
            try:
                driver(text="确认").click()
            except:
                pass
            try:
                driver(text="继续安装").click()
            except:
                pass
           try:
                driver(text="安装").click()
            except:
                pass

    # 执行adb shell
    def shell(self, cmd):
        p = os.popen(cmd)
        return p.read()

    # 安装apk
    def app_install(self, app_file_path, device_id = ''):
        if device_id == '':
            cmd = 'adb install -r ' + str(app_file_path)
        else:
            cmd = 'adb -s ' + str(device_id) +'install -r ' + str(app_file_path)
        self.shell(cmd)


thread1 = usb_install_thread()
thread1.start()
print("The thread is alive: " + str(thread1.is_alive()))
thread1.app_install('/Users/xiaoxiao/Downloads/xxx.apk', '882QADT9UWT')
print("After install the apk, the thread is alive: " + str(thread1.is_alive()))

注意:如果是 oppo、vivo 等手机可能会遇到权限报错(如下图):java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission

问题原因:因为 oppo、vivo 手机的开发者选项中没有开启 “USB 模拟点击”,无法通过自动化进行屏幕点击操作
解决步骤:
1.到开发者选项中开启 “USB 模拟点击”
2.如果遇到开启失败,需要返回到手机设置一级目录,退出你的 vivo 账号,再重新登录账号
3.重复步骤 1 即可

更多内容,请看下篇:https://testerhome.com/topics/19579

共收到 11 条回复 时间 点赞

如果启动 app 后,弹出了系统弹窗,如授权存储权限等,可以改写 usb_install() 如下:

def usb_install(self):
    print("Begin to install apk...\n")
    while True:
        # # 如果apk安装成功,则退出线程
        # if self.is_apk_has_installed('com.xxx'):
        #     # print("The apk has been installed~")
        #     break

        # 如果app启动成功,并已经点掉了类似于获取存储权限等的弹窗,则退出线程
        if self.is_activity_started('com.xxx'):
            print("The app starts success~")
            break
        # 点掉弹窗"允许"、"确认"、"继续安装"等
        try:
            driver(text="允许").click()
        except:
            pass
        try:
            driver(text="确认").click()
        except:
            pass
        try:
            driver(text="继续安装").click()
        except:
            pass
        try:
            driver(text="安装").click()
        except:
            pass

为了提高点击效率,可以用 click_exists(timeout=1.0),代码如下:

def usb_install(self):
    print("Begin to install apk...\n")
    while True:
        # # 如果apk安装成功,则退出
        # if self.is_apk_has_installed('com.xxx'):
        #     # print("The apk has been installed~")
        #     break

        # # 如果app启动成功,并点掉类似于存储权限的弹窗,则退出
        # if self.is_activity_started('com.xxx'):
        #     print("The app starts success~")
        #     break

        # 点掉弹窗"允许"、"确认"、"继续安装"等
        try:
            driver(text="允许").click_exists(timeout=1.0)
        except:
            pass
        try:
            driver(text="确认").click_exists(timeout=1.0)
        except:
            pass
        try:
            driver(text="继续安装").click_exists(timeout=1.0)
        except:
            pass
        try:
            driver(text="安装").click_exists(timeout=1.0)
        except:
            pass
匿名 在 [已转移] Python3 通过线程解决安装 apk 时的权限弹窗问题 中提及了此贴 05月25日 23:48

你能正常安装吗?我用你的试了了下 ,线程一直在跑,但是手机一直没有任务反应呢

提个问题,运行时动态权限弹框怎么办

cccSummary 回复

手机没有反应是什么意思?执行安装程序了么?

cmlanche 回复

把线程抽出成一个类,然后直接调用,一直让线程跑着,直到最后的 case 跑完,再停掉线程。

xiaoxiao 回复

嗯,这个是可以,那 UIAutomator2 本身就是一个 apk,它自身通过 adb 安装弹出的提示框怎么解决?

cmlanche 回复

我这个是基于已经手动安装了 u2 环境的,想要自动安装 u2 的 apk 的话,可能得另外想办法了。比如用 watchdog 等,也是一个 apk😅

cmlanche 回复

还有一个办法,就是安装 u2 的时候,执行adb shell dumpsys activity | grep "Run #"不断拿当前页面的 activity,如果有弹窗了,肯定是一个 grantPermisson 相关的 activity,这时候,根据不同型号的手机,点坐标好了,也可以把安装弹窗给点掉。😃

可以参考一下 u2 的 Watcher 的用法,监控页面的元素,如果存在 xxx,点击 xxx 这样的操作

或者直接用 u2 的 watcher 监听,可以通过 watchers.watched 设置 True/False 打开或关闭监听

# 监听弹窗
    def watcher(self, driver):
        try:
            driver.watcher("继续安装").when(text="继续安装").click(text="继续安装")
            driver.watcher("允许").when(text="允许").click(text="允许")
            driver.watcher("始终允许").when(text="始终允许").click(text="始终允许")
            driver.watcher("确定").when(text="确定").click(text="确定")
            driver.watcher("确认").when(text="确认").click(text="确认")
            driver.watchers.watched = True
        except Exception as e:
            print(e)
xiaoxiao 回复

不知道楼主有没有想过,面向手机上所有安装的应用,检测并点击所有应用中出现的弹窗,是要用穷举法,列出所有弹窗的文本,进行全部适配吗?期待楼主答复

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册