【问题标题】:How do I catch SocketExceptions in MonkeyRunner?如何在 MonkeyRunner 中捕获 SocketExceptions?
【发布时间】:2012-08-25 20:29:40
【问题描述】:

在使用 MonkeyRunner 时,我经常会收到如下错误:

120830 18:39:32.755:S [MainThread] [com.android.chimpchat.adb.AdbChimpDevice] Unable to get variable: display.density
120830 18:39:32.755:S [MainThread] [com.android.chimpchat.adb.AdbChimpDevice]java.net.SocketException: Connection reset

根据我的阅读,有时 adb 连接会变坏,您需要重新连接。唯一的问题是,我无法赶上SocketException。我将像这样包装我的代码:

try:
    density = self.device.getProperty('display.density')
except:
    print 'This will never print.'

但显然异常并没有一直引发到调用者。我已经验证 MonkeyRunner/jython 可以按照我期望的方式捕获 Java 异常:

>>> from java.io import FileInputStream
>>> def test_java_exceptions():
...     try:
...         FileInputStream('bad mojo')
...     except:
...         print 'Caught it!'
...
>>> test_java_exceptions()
Caught it!

我该如何处理这些套接字异常?

【问题讨论】:

    标签: jython monkeyrunner


    【解决方案1】:

    每次启动 MonkeyRunner 时都会出现该错误,因为脚本停止时设备上的 monkey --port 12345 命令并未停止。这是猴子的错误。

    解决此问题的更好方法是在将SIGINT 发送到您的脚本时杀死猴子(当您ctrl+c 时)。换句话说:$ killall com.android.commands.monkey

    快速的方法:

    from sys, signal
    from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
    
    device = None
    
    def execute():
        device = MonkeyRunner.waitForConnection()
        # your code
    
    def exitGracefully(self, signum, frame):
        signal.signal(signal.SIGINT, signal.getsignal(signal.SIGINT))
        device.shell('killall com.android.commands.monkey')
        sys.exit(1)
    
    if __name__ == '__main__'
        signal.signal(signal.SIGINT, exitGracefully)
        execute()
    

    编辑: 作为附录,我还找到了一种注意到 Java 错误的方法:Monkey Runner throwing socket exception broken pipe on touuch

    【讨论】:

      【解决方案2】:

      以下是我最终使用的解决方法。任何可能遭受 adb 失败的函数只需要使用以下装饰器:

      from subprocess import call, PIPE, Popen
      from time import sleep
      
      def check_connection(f):
          """
          adb is unstable and cannot be trusted.  When there's a problem, a
          SocketException will be thrown, but caught internally by MonkeyRunner
          and simply logged.  As a hacky solution, this checks if the stderr log 
          grows after f is called (a false positive isn't going to cause any harm).
          If so, the connection will be repaired and the decorated function/method
          will be called again.
      
          Make sure that stderr is redirected at the command line to the file
          specified by config.STDERR. Also, this decorator will only work for 
          functions/methods that take a Device object as the first argument.
          """
          def wrapper(*args, **kwargs):
              while True:
                  cmd = "wc -l %s | awk '{print $1}'" % config.STDERR
                  p = Popen(cmd, shell=True, stdout=PIPE)
                  (line_count_pre, stderr) = p.communicate()
                  line_count_pre = line_count_pre.strip()
      
                  f(*args, **kwargs)
      
                  p = Popen(cmd, shell=True, stdout=PIPE)
                  (line_count_post, stderr) = p.communicate()
                  line_count_post = line_count_post.strip()
      
                  if line_count_pre == line_count_post:
                      # the connection was fine
                      break
                  print 'Connection error. Restarting adb...'
                  sleep(1)
                  call('adb kill-server', shell=True)
                  call('adb start-server', shell=True)
                  args[0].connection = MonkeyRunner.waitForConnection()
      
          return wrapper
      

      因为这可能会创建一个新连接,所以您需要将当前连接包装在一个 Device 对象中,以便可以对其进行更改。这是我的设备类(大部分类是为了方便,唯一需要的是connection 成员:

      class Device:
          def __init__(self):
              self.connection = MonkeyRunner.waitForConnection()
              self.width = int(self.connection.getProperty('display.width'))
              self.height = int(self.connection.getProperty('display.height'))
              self.model = self.connection.getProperty('build.model')
      
          def touch(self, x, y, press=MonkeyDevice.DOWN_AND_UP):
              self.connection.touch(x, y, press)
      

      如何使用装饰器的示例:

      @check_connection
      def screenshot(device, filename):
          screen = device.connection.takeSnapshot()
          screen.writeToFile(filename + '.png', 'png')
      

      【讨论】:

      • 好吧,我不得不说这不是因为 adb 不稳定,而是因为 MonkeyRuner/Chimpchat。 AndroidViewclient/culebra 使用了一个adbclient,这是一个python 模块,它用adb 封装了所有对话框,并且非常稳定。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-16
      • 2023-03-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多