Appium 解决部分机型 appium 安装应用时需要手动确认的问题

扮猪吃老虎 · 2015年05月20日 · 最后由 miqitangguo 回复于 2019年02月13日 · 3641 次阅读
本帖已被设为精华帖!

Update:

有朋友提示用 uiaotumator,多线程,一边安装一边检测是否有弹窗,亲测可行,完美解决。

# encoding:UTF8
import pexpect, sys, os.path, subprocess
from uiautomator import Device
from time import sleep
import threading

def install_apk(device, path_to_apk):
    print 'Installing apk: {0} to device: {1}'.format(path_to_apk, device)
    P = subprocess.Popen("adb -s {0} install {1}".format(device, path_to_apk),stdout=subprocess.PIPE, shell=True)
    data = P.stdout.read()
    if "Success" in str(data):
        print 'Installing apk: {0} for device: {1} Success'.format(path_to_apk, device)
        exit(1)
    if "Failure" in str(data):
        print 'Installing apk: {0} for device: {1} Failure'.format(path_to_apk, device)
        exit(0)

def uninstall_apk(device, package):
    print 'Uninstalling apk: {0} for device: {1}'.format(package, device)

    result = str(pexpect.run('adb -s {0} uninstall {1}'.format(device, package))).strip().lower()

    if 'failure' == result:
        print 'WARN: Uninstall apk: {0} for device: {1} has error, maybe not exists.'.format(package, device);
        exit(0)

    if 'success' != result:
        print 'Uninstall apk: {0} for device: {1} failed, msg: {2}'.format(package, device, result)
        exit(1)

def protect(nub,device):
    sleep(2)
    for i in range(nub):
        d = Device(device)
        el1 = d(text="安装")
        el2 = d(text="确定")
        if el1.exists:
            el1.click() 
        if el2.exists:
            el2.click()  
        sleep(2)

if __name__ == '__main__':
    if len(sys.argv) < 4:
        print 'Arguments error, usage: python install_app <install | uninstall> <device> <apk | package>'
        exit(1)

    op = sys.argv[1]
    device = sys.argv[2]
    apk = sys.argv[3]

    if op == 'install':
        threads = []
        install = threading.Thread(target=install_apk, args=(device,apk))
        protect = threading.Thread(target=protect, args=(30,device))
        threads.append(install)
        threads.append(protect)
        for t in threads:
            t.setDaemon(True)
            t.start()
        t.join()        
    else :
        package = sys.argv[3]
        uninstall_apk(device, package)

原文:

最近在 moto x 上(5.0 的,不确定是不是所有 5.0 都这样)跑 case 遇到一个问题:每次在 appium 安装应用的时候系统会弹窗提示是否确认安装。需要手动点击确认才能继续,否则就只能卡在这里。

翻看源码发现 appium 使用的是adb install来安装应用的,手动执行adb install确实会弹窗
度娘那里得来还有一种安装方法pm install
手动尝试先把包 push 到手机上,再pm install,没有弹窗,可以解决问题。
于是注释掉 appium 安装,顺手也注释掉 unlock 和 app setting 的安装。
文件路径:/usr/local/lib/node_modules/appium/lib/devices/android/android.js ,如果使用的是 selendroid 模式,则需要注释掉 selendroid.js 里的相关内容

//this.uninstallApp.bind(this),
//this.installAppForTest.bind(this),
//this.pushSettingsApp.bind(this),
//this.pushUnlock.bind(this),

手动写了一个 package、unlock、app setting 的安装方法,每次执行 case 集前调用一次,即解决了个别机器弹窗的问题,又避免了 appium 每执行一个 case 都要安装三个应用,提高了执行速度。

# encoding:UTF8
import pexpect, sys, os.path, subprocess


def install_apk(device, path_to_apk):
    print 'Installing apk: {0} for device: {1}'.format(path_to_apk, device)
    file_name = os.path.basename(path_to_apk)
    pexpect.run('adb -s {0} push {1} /sdcard/{2}'.format(device, path_to_apk, file_name))
    p = pexpect.spawn('adb -s {0} shell'.format(device))
    p.logfile = sys.stdout
    p.expect('.*shell@.*', 20)
    p.sendline('pm install /sdcard/{0}'.format(file_name))
    index = p.expect(['Success', '.*shell@.*'], 120)
    p.sendline('rm /sdcard/{0}'.format(file_name))

    if index == 1:
        print 'Intall apk: {0} for device: {1} failed.'.format(path_to_apk, device)
        exit(1)

def uninstall_apk(device, package):
    print 'Uninstalling apk: {0} for device: {1}'.format(package, device)

    result = str(pexpect.run('adb -s {0} uninstall {1}'.format(device, package))).strip().lower()

    if 'failure' == result:
        print 'WARN: Uninstall apk: {0} for device: {1} has error, maybe not exists.'.format(package, device);
        exit(0)

    if 'success' != result:
        print 'Uninstall apk: {0} for device: {1} failed, msg: {2}'.format(package, device, result)
        exit(1)

if __name__ == '__main__':
    if len(sys.argv) < 4:
        print 'Arguments error, usage: python install_app <install | uninstall> <device> <apk | package>'
        exit(1)

    op = sys.argv[1]
    device = sys.argv[2]

    if op == 'install':
        apk = sys.argv[3]
        install_apk(device, apk)
    else :
        package = sys.argv[3]
        uninstall_apk(device, package)

另外小米的机器不管使用什么方法,仍然会有弹窗,我还是没能解决,不知道各位高手有什么方法。

共收到 44 条回复 时间 点赞

顶一个!

有些机型,在安装时确实会弹框。我也在头疼此事。

为什么不省略掉安装这个步骤?没必要每次跑 case 都去安装一遍应用。

#3 楼 @xuxu 要装的,没执行一次所用的包都是不同的,我们一个版本会出很多 debug 包,每次都要用最新的包做测试。 而且我目前是每执行一个 task 前安装一下,一个 task 可能包含很多 case。

#2 楼 @snake 小米的机器依然令我头疼

把手机助手、安全软件全部先干掉。

#6 楼 @xuxu 全干掉了,moto 的机器拿回来什么都没装就这样子。昨天试了另外一 moto 的机器也是这样子。

#7 楼 @sunrise 把安装步骤注释掉,用 UiAutomator 自己写一个安装方法呗~

#8 楼 @weamylady python 的 UiAutomator 模块是吧? 安装时不会弹窗么? 我去研究下。

#9 楼 @sunrise 我的意思不是不会弹窗,UiAutomator 可以操作 Android 上所有 native 的控件,与 App 无关,所以如果出现弹窗可以点掉呀~

此种方式我也试过,部分机型 pm 安装也一样弹出来
我的解决方式为,写一个 uiautomator 的脚本处理,安装后,运行个线程来启动 uiautomator 脚本,检查是否有这个窗口出现,有则处理掉

#10 楼 @weamylady @fan2597 两位提到了一个共同的方法,此法可行,赞

楼主...报这个错是怎么个情况...

Arguments error, usage: python install_app <install | uninstall> <device> <apk | package>

#13 楼 @tlbin 传入的参数对不对呀,是不是少了什么参数?

47楼 已删除

#15 楼 @shenkai600 解决了呀,上面不是贴了解决方法么

#15 楼 @shenkai600 其实小米的最简单的方法是安装开发版 MIUI

#17 楼 @xdlhy 嗯,已经解决掉了,不管他是什么系统,只要有弹窗,就自动点掉了

@sunrise Hi 这个问题是怎么解决的呢,能详细说下吗

@wanghuan @sunrise 这个问题咋解决的?具体说下呗

小米手机安装提示解决方法:

pattern = re.compile("adb -s (.*?) +")
        search = pattern.search(cmd)
        oscmd = ''
        if search:
            serialname = search.groups()
            sysstr = platform.system()
            if (sysstr == "Windows"):
                oscmd = 'adb -s %s shell dumpsys activity|findstr mFocusedActivity|findstr PackageInstallerActivity' % str(serialname[0])
            elif (sysstr == "Linux"):
                oscmd = 'adb -s %s shell dumpsys activity|grep mFocusedActivity|grep PackageInstallerActivity' % str(serialname[0])
             else:
                print ("Other System tasks")
        # end by wsa 20160907
        while timeout:

            time.sleep(0.1)

            if cmd.find('install') >= 0:  # 小米手机安装提示未知的PC工具
                # root_logger.info(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) + ' 当前命令为 ' + cmd + '\n')
                if os.system(oscmd) == 0:
                    root_logger.info('发现安装提示,执行命令'+'adb -s %s shell uiautomator runtest TestDemo.jar -c Install > nul' % str(serialname[0]))
                    create_process('adb -s %s shell uiautomator runtest TestDemo.jar -c Install ' % str(serialname[0]))


import time
import subprocess
import threading
import uiautomator as ui
class AppInitDeal(object):
    def __init__(self,device=None,apk_path=None,app_packagename=None):
        self.device = device
        self.apk_path = apk_path
        self.app_packagename = app_packagename
    def ui_operate(self):
        '对于apk在安装、卸载和执行case初始化时操作各种提示,暂定安装过程5秒'
        d = ui.Device(self.device)
        time_interval = 0
        start_time = time.time()
        while time_interval<10:
            text_names=['安装','确定']#有其他需要的操作可扩展进来
            for text_name in text_names:
                time.sleep(1)
                if d(text=text_name).exists:
                    d(text=text_name).click()
                    print("%s click sucess" % text_name)
                    break
            time_interval= int(time.time()-start_time)
        print('安装过程10秒')
    def install_app(self):
        '安装apk,多设备连入时需指定设备'
        if self.device is None:
            args = "adb install -r {0}".format(self.apk_path)
        else:
            args = "adb -s {0} install -r {1}".format(self.device,self.apk_path)
        if self.apk_path is None:
            return print('apk_path must be passed')
        with subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True) as p1:
            p1.wait()
            stdout_result = p1.stdout.read()
            if 'Success' in str(stdout_result):
                print("{0} install sucess".format(self.apk_path))
            elif 'Failure' in str(stdout_result):
                print('{0} install fail'.format(self.apk_path))
    def unstall_app(self):
        '卸载apk,多设备连入时需指定设备'
        if self.device is None:
            args = "adb uninstall {0}".format(self.app_packagename)
        else:
            args = "adb -s {0} uninstall {1}".format(self.device, self.app_packagename)
        if self.app_packagename is None:
            return print('app_packagename must be passed')
        with subprocess.Popen(args,stdout=subprocess.PIPE,shell=True) as p1:
            p1.wait()
            stdout_result = p1.stdout.read()
            if 'Success' in str(stdout_result):
                print("{0} uninstall sucess".format(self.app_packagename))
            elif 'Failure' in str(stdout_result):
                print('{0} uninstall fail'.format(self.app_packagename))

    def app_init_ui_operate(self):
        '多线程处理'
        Thread_list=[]
        fun_list = [self.install_app,self.ui_operate]
        for fun in fun_list:
            t = threading.Thread(target=fun)
            t.setDaemon(True)
            Thread_list.append(t)
            t.start()
        [thread.join() for thread in Thread_list]

pp = AppInitDeal(device="953cb9d0",apk_path=r"D:\360Downloads\Apk\9.0.1.073001.apk")
pp.app_init_ui_operate()

脚本单独能运行,但是放在 appium 的 case 里 setup 里脚本执行后,报错
A new session could not be created. (Original error: UiAutomator quit before it successfully launched)
appium 日志:
info: [debug] Attempting to kill all 'uiautomator' processes
info: [debug] Getting all processes with 'uiautomator'
看日志自己去杀掉了进程,大神门这个何解

在没有安装成功的情况下,怎么获取 Appiumdriver?去执行 appiumdriver.findElementById("继续安装").click();是无效的?求解,求 java 的程序啊?

#22 楼 @A_tester 这个是因为你 appium 用的 uiautomator 模式,appium 会起一个 uiautomator,而你 python 脚本里又起了一个 uiautomator,两者冲突了

#23 楼 @kzyfree appium 起一个 session 就可以查找元素的,跟安装不安装没有关系的
正常情况下,指定了 apk 路径,起 session 以后就自动去安装了,这个时候就可以起一个单独的线程,使用 appium driver 去检查有没有弹窗

#22 楼 @A_tester 放在 appium 里面执行,建议就不要用 python 的 uiautomator 模块了,直接起一个线程,用 appium 的 driver 去检查弹窗

pexpect 的 run 和 spawn windows 不支持,有啥看替代的?

#26 楼 @sunrise 好的,回头试验一下

#23 楼 @kzyfree http://stackoverflow.com/questions/37641670/adb-install-failure-install-canceled-by-user 我尝试着进入小米的安全中心,进入授权管理,取消了 USB 安装管理。后面通过 adb 安装 apk 都不用手动确认了

@A_tester 后来可以直接起一个 appium 的 session 么?

记录个遇到的问题:监控开启后,webdriver 启动不了,疑似在开启监控后,uiautomator 进程继续存在,导致 webdriver 启动失败。
解决方法:在监控结束后,kill 掉响应的 uiautomator 进程。

#23 楼 @kzyfree 请问解决了吗?

nub 参数为啥要传 30 进去,另外 device 参数具体是什么格式的值。还有 settings、Unclock 是启动 app 时就会弹出的,所以 protect 从一开始就要一直处于待命状态

app 没有启动的话,driver 怎么获取对象

不用这么麻烦,试试 UiWatcher 监听器,自带观察者模式,注册一个 watcher 即可

public class InCallTest extends UiAutomatorTestCase {  
    private static final long TIME_OUT = 10 * 60 * 1000;  
    private long currentTime;  
    private boolean flag = true;  

    long eslcape = 0;  

    public void test_Call() throws InterruptedException,  
            UiObjectNotFoundException {  
        UiWatcher inComingWatcher = new MyWatcher();  
        getUiDevice().registerWatcher("来电", inComingWatcher);  

        getUiDevice().pressBack();  
        UiObject dialButton = new UiObject(  
                new UiSelector()  
                        .resourceId("com.android.sprdlauncher1:id/workspace"));  
        System.out.println("waiting for incoming");  
        currentTime = System.currentTimeMillis();  
        while (flag && eslcape < TIME_OUT) {  
            dialButton.clickAndWaitForNewWindow();  
            eslcape = System.currentTimeMillis() - currentTime;  
        }  
        assertFalse("没有收到来电", flag);  
    }  

    class MyWatcher implements UiWatcher {  

        @Override  
        public boolean checkForCondition() {  
            UiObject inCall = new UiObject(  
                    new UiSelector()  
                            .resourceId("com.android.dialer:id/IncomingCallRejectButton"));  

            while (eslcape < TIME_OUT) {  
                if (inCall.exists()) {  
                    System.out.println("you have a call");  
                    try {  
                        inCall.clickAndWaitForNewWindow();  
                        flag = false;  
                        return true;  
                    } catch (UiObjectNotFoundException e) {  
                        e.printStackTrace();  
                    }  
                }  
                eslcape = System.currentTimeMillis() - currentTime;  
            }  
            return false;  
        }  
    }  
}  

遇到了同样的问题,你最后是怎么处理的?

为什么楼主的代码在 vivo 和小米手机上都是时而行时而不行,也没报什么错,就是安装失败,没有点击,成功的概率很小

cheng 回复

代码很老了,部分可能需要更新

matt gong appium 如何屏蔽系统提示信息 中提及了此贴 12月11日 09:46

【直接起一个线程,用 appium 的 driver 去检查弹窗】这个方法我试过了不行,driver 把这个会话线程占住了,其他线程就得等待,所以这个方法不科学

遇见一个坑,app 安装后,第一个允许不会点击,会自动等待到第二个允许,才会点击,见代码


for i in range(8):
    # allow = d(textContains="允许")
    allow = d(resourceId="com.huawei.systemmanager:id/btn_allow")
    print(allow.exists)
    total_allow = d(resourceId="com.android.packageinstaller:id/permission_allow_button")
    if allow.exists is True:
        allow.click()
    if total_allow.exists is True:
        total_allow.click()

您好,具体要怎么操作了,不是很明白,新手

需要手动确认的手机,使用 from uiautomator import Device 也不能自动安装 app-uiautomator.apk 和 app-uiautomator-test.apk,还需要手动点击,好像不是很行得通

fengyi 回复

把手动变自动,有办法的

Kun 回复

请问怎么 kill 掉响应的 uiautomator 进程,能贴一下解决方法么?

Kun 回复

请问,在监控结束后,kill 掉响应的 uiautomator 进程,这个如何实现,能贴一下实现代码么?

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