2012-08-31 14 views
6

Al utilizar MonkeyRunner, de vez en cuando me sale un error como:¿Cómo capturo SocketExceptions en 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 

Por lo que he leído, a veces la conexión ADB va mal, y hay que volver a conectar. El único problema es que no puedo ver el SocketException. Voy a terminar mi código de este modo:

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

Pero la excepción, aparentemente, no se eleva hasta el final a la persona que llama. He verificado que MonkeyRunner/jython puede capturar las excepciones de Java de la manera que cabría esperar:

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

¿Cómo puedo lidiar con estas excepciones socket?

Respuesta

2

A continuación se muestra la solución que terminé usando. Cualquier función que puede sufrir de fallas adb sólo tiene que utilizar el siguiente decorador:

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 

Debido a que esto puede crear una nueva conexión, es necesario envolver la conexión actual en un objeto de dispositivo de modo que pueda ser cambiado. Aquí está mi clase de dispositivos (la mayor parte de la clase es por comodidad, lo único que es necesario es la connection miembro de:

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) 

Un ejemplo de cómo usar el decorador:

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

Bueno, tengo que decir que no se debe a una inestabilidad 'adb' pero las de MonkeyRuner/Chimpchat. [AndroidViewclient/culebra] (https://github.com/dtmilano/AndroidViewClient) utiliza un 'adbclient', un módulo de python que encapsula todo el diálogo con' adb' y es bastante estable. –

6

Va a conseguir que la error cada vez que inicias MonkeyRunner porque el comando monkey --port 12345 en el dispositivo no se detiene cuando el script se detiene. Es un error en el mono

Una forma más fácil de resolver este problema es matando mono cuando SIGINT se envía a su secuencia de comandos (cuando usted ctrl+c). En otras palabras: $ killall com.android.commands.monkey.

forma rápida de hacerlo:

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() 

Editar: como adición, también encontré una manera de darse cuenta de los errores de Java: Monkey Runner throwing socket exception broken pipe on touuch

Cuestiones relacionadas