Below is the workaround I used. Any function that might suffer from adb crashes should just use the following decorator:
from subprocess import call, PIPE, Popen from time import sleep def check_connection(f): """ adb is unstable and cannot be trusted. When there 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:
Since this can create a new connection, you need to wrap your current connection in the Device object so that it can be changed. Here is my device class (most classes are for convenience, the only thing needed is a connection member:
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)
An example of using a decorator:
@check_connection def screenshot(device, filename): screen = device.connection.takeSnapshot() screen.writeToFile(filename + '.png', 'png')
source share