背景
需要批量往多设备中拷贝若干测试图片(几百张),然后对这些图片做一些的事。
思路
通过adb -s deviceName push remote_path local_path拷贝图片,然后通过广播更新媒体库。具体的原理就不说了。。。
实践
一、获取设备
获取设备跟另一篇的方法差不多Android测试--用Python批量给多设备安装app应用。
connectdeviceid = []
p = os.popen('adb devices')
outstr = p.read()
print outstr
connectdeviceid = re.findall(r'(\w+)\s+device\s', outstr)
return connectdeviceid
二、刷新媒体库
一开始使用的是ACTION_MEDIA_SCANNER_SCAN_FILE,在adb的相关命令中介绍过Android测试 adb常用命令。通过adb shell am broadcast来实现。
adb shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file_path
图片文件较多,循环遍历一下,
for device in connectdeviceid:
for path, subdirs, files in os.walk(indir):
for filename in files:
refresh_pic(filename, device)
实际测试中,在用adb shell发送广播时,遇到了问题。当使用os.popen这类命令时,一条一条的非常之慢。使用mulprocessing或者subprocess时候,却又太快,会导致adb连接出现connect reset peeer或者error: protocol fault (couldn't read status): Undefined error: 0等错误。
于是换个方向,看能不能直接刷新文件夹。去官网搜搜有关MEDIA Action相关资料。可以很清晰的看到并没有讹传的android.intent.action.MEDIA_SCANNER_SCAN_DIR,当然通过查看MediaScannerReceiver.java的源码也会发现这根本行不通。
不过仍然可以另辟蹊径,通过ACTION_MEDIA_MOUNTED来实现刷新文件夹。
官方是这么介绍的:
ACTION_MEDIA_MOUNTED
added in API level 1
String ACTION_MEDIA_MOUNTED
Broadcast Action: External media is present and mounted at its mount point. The path to the mount point for the mounted media is contained in the Intent.mData field. The Intent contains an extra with name "read-only" and Boolean value to indicate if the media was mounted read only.
Constant Value: "android.intent.action.MEDIA_MOUNTED"
命令仍然一样,试验一下是可以成功的,比坑纸坑纸一个文件一个文件的刷要方便太多了。
adb shell am broadcast -a android.intent.action.MEDIA_MOUNTED -d path
剩下的组织一下脚本就ok了。
#!usr/bin/python
# -*- coding:utf-8 -*-
# Author:John Hao
# 拷贝媒体文件到设备中。例如图片、MP4文件等
# 原理:拷贝之后,自动发送媒体广播,让设备实时更新图库
import os
import subprocess
import multiprocessing
import re
import threading
import time
import sys
device_name = "85UABM7HEBC2" # 指定单一的设备,如果为空则执行全部链接设备
indir = '/Users/johnhao/Pictures/marcelsiebert' # 输入文件夹
sdcard_path = "/sdcard/DCIM/marcelsiebert" # 拷贝到设备的路径
# exstr = "" # 设备中要刷新的文件的扩展名,例如".jpeg"。为空时,刷新全部文件夹;不为空,刷新指定文件结尾格式的文件
# 获取设备name
def get_conn_dev():
connectdeviceid = []
p = os.popen('adb devices')
outstr = p.read()
print outstr
if device_name == "":
print "未指定设备,执行全部"
connectdeviceid = re.findall(r'(\w+)\s+device\s', outstr)
return connectdeviceid
elif device_name in outstr:
print "执行设备: ", device_name
connectdeviceid.append(device_name)
return connectdeviceid
else:
print "指定设备未连接,请重新检查device name"
sys.exit()
# 拷贝媒体到设备中
def cpoy_to_devices(command):
print "拷贝图片并刷新"
print command
os.popen(command)
# 刷新单图
def refresh_pic(filename, deviceid):
cmd_refresh = "adb -s %s shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///storage/emulated/0/%s/%s" % (deviceid, sdcard_path[8:], filename)
print cmd_refresh
subprocess.Popen(cmd_refresh, shell=True)
# 刷新文件夹
def refresh_folder(deviceid):
cmd_refresh = "adb -s %s shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///storage/emulated/0/%s/" % (deviceid, sdcard_path[8:])
print cmd_refresh
subprocess.Popen(cmd_refresh, shell=True)
def main():
connectdevice = get_conn_dev()
commands = []
for device in connectdevice:
cmd_push = "adb -s %s push %s %s" % (device, indir, sdcard_path)
commands.append(cmd_push)
threads = []
threads_count = len(commands)
for i in range(threads_count):
t = threading.Thread(target = cpoy_to_devices, args = (commands[i],))
threads.append(t)
for i in range(threads_count):
time.sleep(1) # 防止adb连接出错
threads[i].start()
for i in range(threads_count):
threads[i].join()
for device in connectdevice:
refresh_folder(device)
time.sleep(0.5)
# 刷新单张图片
# for path, subdirs, files in os.walk(indir):
# for filename in files:
# if exstr == "":
# refresh_pic(filename, device)
# elif filename.endswith(exstr):
# refresh_pic(filename, device)
if __name__ == '__main__':
main()