6

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!

これらのソケット例外にどのように対処できますか?

4

2 に答える 2

8

monkey --port 12345スクリプトの停止時にデバイスのコマンドが停止しないため、MonkeyRunnerを起動するたびにこのエラーが発生します。サルのバグです。

この問題を解決するためのより良い方法は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=None):
    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エラーに気付く方法も見つけました:MonkeyRunnerがソケット例外をスローしてパイプが壊れています

編集:信号には2つのパラメーターが必要なようですが、常にそうであるかどうかはわかりませんが、3番目のパラメーターはオプションになっています。

于 2015-01-21T09:29:23.160 に答える
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オブジェクトでラップして、変更できるようにする必要があります。これが私の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')
于 2012-11-06T21:23:22.513 に答える