自动化测试MonkeyRunner、adb shell向Android APP中自动输入中文

 更新时间:2016年9月20日 19:58  点击:1950
在Android开发中,测试完全模拟人为点击时输入中文比较麻烦,不过我们可以用 MonkeyRunner 自动输入中文字符,下面我们来分享这个的实现方法。

在做一些Android自动化脚本,比如向手机QQ中输入中文聊天内容,由于使用Robotium、Appium等工具来做时,由于TX作了签名校验的,所以登录不了QQ的。后来只能使用最傻的MonkeyRunner来做,不过它是完全模拟人为点击,而数据中文就很麻烦了。为此我们尝试了多种方案,简单分享一下:

1. 通过PC的剪贴板与Android模拟器中共享来实现:先将要输入的中文放到PC的剪贴板中,再到Android模拟器中粘贴到QQ聊天输入框中。其缺点是,很不稳定,有时候成功,有时候比较莫名的失败。

2. adb shell input 命令可直接将字符串输入到Android应用的Input框中,但问题来了,它不支持unicode字符啊(仅支持ASICC字符)。于是,我根据网上资料找到了一个方法:使用unicode字符原样输出,然后再转化为encoded字符。比如:https://github.com/bingwei/inputchineseviaadb,就实现了这样的转换,在app中复制到android的剪贴板中,然后可以在QQ聊天框等地方粘贴就好了。当然这里他这个gitlab项目中utils/inputunicode.py文件有点小bug,对于python 2.x,需要在adb shell input后跟的string用encode(‘unicode-escape’)编码。
这个种方式的速度不是很快,不过还是算不错的,因为本来做UI自动化耽误个一两秒钟也是可以接受的;我们最终就是采用了这种方案,目前运行下来是非常稳定的。

3. 使用一个外部REST服务器,搞一个key-value对放到REST sever中,其中key是全英文的,而value中可以包含中文;然后通过adb shell input将key传入到Andoid中,在Android中根据key去请求远程REST API从而得到包含中文字符的Value。没试过这种方法,不过应该是可行的;但其效率估计比第2中方法还要效率低下。

4. 一种专门为unicode做的输入法,可以adb shell input输入unicode,它帮你转成中文之类的字符。没真正尝试过。

Android 模拟器 Emulator 速度真心不给力,, 现在我们来介绍使用 Intel HAXM 技术为 Android 模拟器加速,使模拟器运行度与真机比肩。

周末试玩了一下在Eclipse中使用ADT开发Android应用程序的环境,当然诚如大家都懂的那样,Android Emulator模拟器启动和运行的速度确实是比较慢的,也都“有口皆碑”了的,呵呵。当然,Intel去年开发并在Google Android官方网站发布了一个对Android Emulator的驱动,大幅提升了在Intel x86平台上Android Emulator的启动和运行效率,从而提升Android应用程序开发者在使用Android模拟器开发程序时的效率。
在Windows、MacOS上使用HAXM (Hardware Accelerated Execution Manager) 来加速Android Emulator,在Linux平台上直接使用KVM加速即可。据官方介绍,一般来说使用了HAXM或KVM,有5~10x的性能提高,我简单看了下确实也有几倍的提高。
HAXM必须要求有Intel硬件虚拟化的支持,一般在BIOS中可以设置。BTW,ThinkPad X230的BIOS中,居然是在Security选项中设置VT的。
可以通过Android SDK Manager那下载HAXM, 在 Package 列表的最下面就是要用到的 Intel HAXM 扩展 (Intel x86 Emulator Accelerator (HAXM)),也需要安装对应的Image(Intel x86 Atom System Image)。下载后的HAXM安装文件在sdkextrasintelHardware_Accelerated_Execution_Manager 目录下,点击安装即可(注意如果Intel VT没有打开是不能安装成功的)。

在制作AVD时,需要设置其“CPU/ABI”属性为“Intel Atom (x86)”。对于图形方面的加速,编辑AVD设置时,Emulation Option选择“Use Host GPU”。

Android Virtual Device Manager中启动某个某个AVD时,可能会看到如下:

Starting emulator for AVD 'AVD_for_Nexus_S_by_Google'
emulator: device fd:620
HAX is working and emulator runs in fast virt mode
creating window 0 0 438 729

Eclipse中ADT运行时,对应的输出如下(可以看到HAX是否work):

[2013-07-08 11:44:43 - JayFirstApp] ------------------------------
[2013-07-08 11:44:43 - JayFirstApp] Android Launch!
[2013-07-08 11:44:43 - JayFirstApp] adb is running normally.
[2013-07-08 11:44:43 - JayFirstApp] Performing com.example.jayfirstapp.MainActivity activity launch
[2013-07-08 11:44:43 - JayFirstApp] Automatic Target Mode: launching new emulator with compatible AVD 'AVD_for_Nexus_S_by_Google'
[2013-07-08 11:44:43 - JayFirstApp] Launching a new emulator with Virtual Device 'AVD_for_Nexus_S_by_Google'
[2013-07-08 11:44:47 - Emulator] emulator: device fd:620
[2013-07-08 11:44:47 - Emulator]
[2013-07-08 11:44:47 - Emulator] HAX is working and emulator runs in fast virt mode
[2013-07-08 11:44:50 - Emulator] creating window 0 0 438 729
[2013-07-08 11:44:50 - JayFirstApp] New emulator found: emulator-5554
[2013-07-08 11:44:50 - JayFirstApp] Waiting for HOME ('android.process.acore') to be launched...

BTW,HAXM是我们Team开发的,如果大家使用时遇到什么bug,也可以反馈给我,我可转交给HAXM的developer。 (Update:本人不在那个team,请大家有问题到Intel HAXM官方网站去反馈吧: http://software.intel.com/en-us/articles/intel-hardware-accelerated-execution-manager)

使用 Intel HAXM 为 Android 模拟器加速,媲美真机

Intel HAXM (Hardware Accelerated Execution Manager) 使用基于 Intel(R) Virtualization Technology (VT) 的硬件加速, 因此需要 CPU 支持 VT , 而且仅限于 Intel CPU, 与 AMD CPU 无缘, Intel HAXM 的描述如下:

使用 Intel VT 技术;

为 Android x86 虚拟设备的模拟运行提供硬件加速;

与 Android SDK 集成;

硬件需求如下:

支持 VT-x, EM64T 以及 Execute Disable Bit 的 Intel 处理器;

至少 1GB 可用内存

支持的操作系统:

Windows 7 (32/64-bit)

Windows Vista (32/64-bit)

Windows XP (32-bit only)

OS X 10.6 or 10.7 (32/64-bit)

下载并安装 Intel HAXM 扩展

启动 Android SDK Manager, 在 Package 列表的最下面就是要用到的 Intel HAXM 扩展, 打勾, 下载, 不用去 Intel 的网站, 如下图:

 

下载并安装 Intel HAXM 扩展

 

下载 HAXM 之后, 需要运行安装程序来进行安装, HAXM 下载的目录是 android-sdk\extras\intel\Hardware_Accelerated_Execution_Manager , 运行 IntelHaxm.exe 进行安装, 根据屏幕提示,一步一步安装即可。

下载 Android x86 镜像

Android SDK Manager 中已经有了 4.1.2 的 x86 镜像, 因此选择 4.1.2 x86 镜像, 如下图所示:

 

下载 Android x86 镜像

 

使用 Android x86 镜像

新建或者编辑 Android 模拟器, 将模拟器 CPU/ABI 设置为 Intel Atom X86 , 如下图所示:

 

配置 Android 模拟器使用 x86 系统镜像

 

如果上面的步骤都没有出错, 现在, Android 模拟器运行的速度几乎可以媲美真机了, 再也不用羡慕 MAC 平台上的 iOS 模拟器。

张志敏所有文章遵循创作共用版权协议,要求署名、非商业 、保持一致。在满足创作共用版权协议的基础上可以转载,但请以超链接形式注明出处。

本文我们来看看使用 Android Emulator 模拟器,如何设置自己的GPS地址位置信息的几个方法,做 Android 开发的朋友可以看下。

使用Android Emulator,可以自己设置GPS地理位置信息,根据Android官方文档的介绍,可以有如下几种方式:

1. 通过命令行:

telnet 5554    # 5554为emulator的console端口
 
geo fix 121.420413 31.215345  # 第一个数值是经度(longitude),第二个数值是纬度(latitude)
# 可从 google maps获取经纬度, https://maps.google.com/

2. 通过Eclipse:


设置路径为:Window > Show View > Other > Emulator Control.
In the Emulator Control panel, enter GPS coordinates under Location Controls as individual lat/long coordinates, with a GPX file for route playback, or a KML file for multiple place marks. (Be sure that you have a device selected in the Devices panel—available from Window > Show View > Other > Devices.)
Using DDMS

3. 通过DDMS:

With the DDMS tool, you can simulate location data a few different ways:
Manually send individual longitude/latitude coordinates to the device.
Use a GPX file describing a route for playback to the device.
Use a KML file describing individual place marks for sequenced playback to the device.

我还是比较喜欢用命令行的方式~
不过,我通常使用HAXM或KVM来加速我的emulator(里面时运行Intel的image),我就发现这些设置方法对我的emulator都没有生效。这个问题曾经困扰了我好几天,后来和一个同事交流时才发现,其实这个设置地理位置信息是依赖于Google API的,而使用Intel的一些image,里面默认时没有Google API的,所以不能生效(即使geo fix命令返回是ok)。所幸的是,Android SDK中从android 4.4开始也直接提供了x86 image的Google APIs。
对于Android 4.3/4.2等的X86镜像,需要通过如下步骤来手动添加Google API的支持。

 代码如下 复制代码
1.  In Android Virtual Device Manager create an AVD with target "Android 4.3/4.2"  (其实就是先使用arm的image)
2.    emulator -avd name_of_avd    (启动arm image)
3.   adb pull /system/etc/permissions/com.google.android.maps.xml     (下载google maps相关文件)
4.    adb pull /system/framework/com.google.android.maps.jar   (下载google maps相关文件)
5.    (optional) Remove the create AVD in Android Virtual Device Manager   (删掉刚才使用的ARM avd,可选)
6.   In Android Virtual Device Manager create an AVD with target "Intel Atom x86 system Image (Intel Corporation) - API Level 18"    (创建Intel x86 image)
8.   emulator -partition-size 1024 -no-snapshot-save -avd name_of_avd   (启动x86 image)
9.   adb remount rw    (让文件系统可写)
10.    adb push com.google.android.maps.xml /system/etc/permissions    (上传google maps相关文件到x86 iamge中)
11.   adb push com.google.android.maps.jar /system/framework   (上传google maps相关文件到x86 iamge中)
12.    Download mkfs.yaffs2.x86  (下载地址:https://android-group-korea.googlecode.com/files/mkfs.yaffs2.x86)
13.   adb push mkfs.yaffs2.x86 /data   (将mkfs.yaffs2.x86传到image里面)
14.   adb shell   (连接到该emualtor的shell)
15.   cd /data
16.    chmod 777 mkfs.yaffs2.x86
17.   ./mkfs.yaffs2.x86 /system system.img            (重新制作system.img文件)
18.    exit  (退出adb shell)
19.    adb pull /data/system.img  (下载制作好的system.img,需要较长时间,耐心等待吧)
20.    Copy system.img into avd directory    (将这个system.img 复制到的你的AVD目录中,覆盖掉原来的system.img,可以先备份原来的)
21.   Reboot emulator  (重启emulator,即可使用到新的system.img)



我第一次这么操作时,在上传google maps文件到x86 image中时,出现了如下的错误:

jay@jay-linux:~/adt-bundle-linux-x86_64-20140321/sdk$ adb push com.google.android.maps.xml /system/etc/permissions
failed to copy 'com.google.android.maps.xml' to '/system/etc/permissions/com.google.android.maps.xml': Out of memory

后来发现原因是,原本的system.img的容量只够存放自己的东西,里面已经存不下其他东西了。所以,在启动x86 AVD时,要用”emulator -partition-size 1024 avd-for-x86“命令来调整/data目录的大小为1024MB。

参考资料:


android官方文档:http://developer.android.com/guide/topics/location/strategies.html
Intel image使用Google Maps API: http://38911bytes.blogspot.de/2012/03/how-to-use-google-maps-api-in-android.html
http://stackoverflow.com/questions/9857325/google-maps-sdk-with-new-intel-atom-x86-emulator

一般的安卓应用都会提供自动更新的功能,让用户方便使用最新的版本,现在我们来讲讲如何实现安卓应用自动更新的功能实例方案,方便大家学习。

安卓应用实现自动更新比较简单,这里跟大家介绍下。

1. web接口

需要提供一个接口供客户端查询更新状态,并且在需要更新时,告知客户端新APK地址。

接口参数如下:

    package   包名,因为有时候会出现同一个应用换包名打包的情况
    version 版本号,即android清单文件里面的versionCode
    channel 渠道号
    os 操作系统,android/ios。ios 这里仅作预留。

 

之所以传入这些字段,是要在与服务器端的包匹配时,务必满足:

    package, channel, os 相等,并且服务器端的version 大于 客户端传入的version

代码如下:

os = request.GET.get('os')
pkg_name = request.GET.get('package')
channel = request.GET.get('channel')
version = request.GET.get('version')

if not os or not pkg_name or not channel or not version:
    return jsonify(**ret_dict)
pkg = Package.objects.filter(
    os=os,
    package=pkg_name,
    channel=channel,
    status__gt=config.PACKAGE_STATUS_NOT_UPDATE
).order_by('-version').first()
if pkg and int(version) < pkg.version:
    ret_dict['pkg_status'] = str(pkg.status)
    ret_dict['pkg_url'] = config.WEB_HOST + pkg.file.url
    ret_dict['update_prompt'] = pkg.info
return jsonify(**ret_dict)

2. 数据库设计

由于web端使用的是django,所以可以很方便的给出运营同学可以操作的后台界面,如下:

NewImage

注意红框内的元素,运营同学在上传时,是不允许修改的,而是由程序自动解析APK文件得到后填入的。

具体的解析方法,我们稍后给出。

而对应的models代码如下:

class Package(models.Model):
    file = models.FileField(u'文件', upload_to=config.PACKAGE_UPLOAD_PATH)
    package = models.CharField(u'包名', max_length=255, blank=True, default='')
    version = models.IntegerField(u"版本号", blank=True, default=0, null=True)
    channel = models.CharField(u"渠道", max_length=128, blank=True, default='')
    status = models.IntegerField(u'更新状态', default=config.PACKAGE_STATUS_NOT_UPDATE,
        choices=config.PACKAGE_UPDATE_STATUS)
    info = models.TextField(u'通知信息', blank=True, null=True)
    os = models.CharField(u'操作系统', max_length=64, default=config.PACKAGE_CLIENT_UNKNOW,
        choices=config.PACKAGE_CLIENT_OS, blank=True, null=True)

    def __unicode__(self):
        _,name = os.path.split(self.file.name)
        return name

    class Meta:
        unique_together = ('package', 'version', 'channel', 'os')

    def save(self, * args, ** kwargs):
        # 文件上传成功后,文件名会加上PACKAGE_UPLOAD_PATH路径
        path,_ = os.path.split(self.file.name)
        if not path:
            if self.file.name.endswith('.apk'):
                self.os = config.PACKAGE_CLIENT_ANDROID
                path = os.path.join('/tmp', uuid.uuid4().hex + self.file.name)
                # logger.error('path: %s', path)
                with open(path, 'wb+') as destination:
                    for chunk in self.file.chunks():
                        destination.write(chunk)
                info = parse_apk_info(path)
                os.remove(path)
                self.package = info.get('package', '')
                self.version = info.get('version', 0)
                self.channel = info.get('channel', '')
            elif self.file.name.endswith('ipa'):
                self.os = config.PACKAGE_CLIENT_IOS

        super(self.__class__, self).save(*args, ** kwargs)

    def display_filename(self):
        _,name = os.path.split(self.file.name)
        return name
    display_filename.short_description = u"文件"

3. APK文件解析

def parse_apk_info(apk_path, tmp_dir='/tmp'):
    """
    获取包名、版本、渠道:
    {'version': '17', 'channel': 'CN_MAIN', 'package': ‘com.fff.xxx'}
    :param apk_path:
    :return:
    """
    from bs4 import BeautifulSoup
    import os
    import shutil
    import uuid

    abs_apk_path = os.path.abspath(apk_path)
    dst_dir = os.path.join(tmp_dir, uuid.uuid4().hex)
    jar_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'apktool.jar'))
    cmd = 'java -jar %s d %s %s' % (jar_path, abs_apk_path, dst_dir)

    if isinstance(cmd, unicode):
        cmd = cmd.encode('utf8')

    # 执行
    os.system(cmd)

    manifest_path = os.path.join(dst_dir, 'AndroidManifest.xml')

    result = dict()

    with open(manifest_path, 'r') as f:
        soup = BeautifulSoup(f.read())
        result.update(
            version=soup.manifest.attrs.get('android:versioncode'),
            package=soup.manifest.attrs.get('package'),
        )

        channel_soup = soup.find('meta-data', attrs={'android:name': 'UMENG_CHANNEL'})
        if channel_soup:
            result['channel'] = channel_soup.attrs['android:value']

    shutil.rmtree(dst_dir)

    return result

当然,正如大家所看到的,我们需要依赖于 apktool.jar 这个文件,具体大家可以在网上下载。

下面来给各位介绍android studio升级失败提示 Connection failed解决方法,希望文章可以给各位带来帮助.

关于这个问题,查了不少地方,发现解决方法都差不多,但或多或少有些问题或没说清楚。关于在windows系统中的解决方法,基本没什么问题,都是如下修改:

在Android Studio安装目录“/bin/studio.exe.vmoptions”文件中追加以下几行


-Djava.net.preferIPv4Stack=true
-Didea.updates.url=http://dl.google.com/android/studio/patches/updates.xml
-Didea.patches.url=http://dl.google.com/android/studio/patches/

但有点要注意,64位系统就应该修改“/bin/studio64.exe.vmoptions”这个文件。
而下面主要说明修改mac中的升级问题:

在Mac和Linux版本中,网上提供的解决办法,差不多都是如下修改:
直接修改环境变量:


$ export REQUIRED_JVM_ARGS="-Didea.updates.url=http://dl.google.com/android/studio/patches/updates.xml -Didea.patches.url=http://dl.google.com/android/studio/patches/"
$ bin/studio

此方法多次尝试,均以失败告终。最后不得不自行想办法解决,发现如下修改可解决这个问题:

在“/Applications/Android Studio.app”中,右键点击“显示包内容”,打开“/bin/idea.vmoptions”文件,在后面追加入windows中的内容,即:

-Djava.net.preferIPv4Stack=true
-Didea.updates.url=http://dl.google.com/android/studio/patches/updates.xml
-Didea.patches.url=http://dl.google.com/android/studio/patches/
后保存退出,重新打开Android Studio,Check for Updates …就可以看到升级提示了。

注:此方法只是自己摸索尝试在本机上可行,非官方提供,失败勿喷,谢谢!a

[!--infotagslink--]

相关文章

  • js URLdecode()与urlencode方法支持中文解码

    下面来介绍在js中来利用urlencode对中文编码与接受到数据后利用URLdecode()对编码进行解码,有需要学习的机友可参考参考。 代码如下 复制代码 ...2016-09-20
  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • 解决@SpringBootTest 单元测试遇到的坑

    这篇文章主要介绍了解决@SpringBootTest 单元测试遇到的坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-14
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • 关于Mysql中文乱码问题该如何解决(乱码问题完美解决方案)

    最近两天做项目总是被乱码问题困扰着,这不刚把mysql中文乱码问题解决了,下面小编把我的解决方案分享给大家,供大家参考,也方便以后自己查阅。首先:用show variables like “%colla%”;show varables like “%char%”;这两条...2015-11-24
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • C#读取中文文件出现乱码的解决方法

    这篇文章主要介绍了C#读取中文文件出现乱码的解决方法,涉及C#中文编码的操作技巧,非常具有实用价值,需要的朋友可以参考下...2020-06-25
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • Mysql在debian系统中不能插入中文的终极解决方案

    在debian环境下,彻底解决mysql无法插入和显示中文的问题Linux下Mysql插入中文显示乱码解决方案mysql -uroot -p 回车输入密码进入mysql查看状态如下:默认的是客户端和服务器都用了latin1,所以会乱码。解决方案:mysql>use...2013-10-04
  • Android设置TextView竖着显示实例

    TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
  • linux mint 下mysql中文支持问题

    一.mysql默认不支持中文,它的server和db默认是latin1编码.所以我们要将其改变为utf-8编码,因为utf-8包含了地球上大部分语言的二进制编码 1.关闭mysql服务 sudo /etc/init.d/mysql stop 2.修改mysql配置文件 mysql配...2015-10-21
  • Windows服务器MySQL中文乱码的解决方法

    我们自己鼓捣mysql时,总免不了会遇到这个问题:插入中文字符出现乱码,虽然这是运维先给配好的环境,但是在自己机子上玩的时候咧,总得知道个一二吧,不然以后如何优雅的吹牛B。...2015-03-15
  • php怎么用拼音 简单的php中文转拼音的实现代码

    小编分享了一段简单的php中文转拼音的实现代码,代码简单易懂,适合初学php的同学参考学习。 代码如下 复制代码 <?phpfunction Pinyin($_String...2017-07-06
  • DWVA上传漏洞挖掘的测试例子

    DVWA (Dam Vulnerable Web Application)DVWA是用PHP+Mysql编写的一套用于常规WEB漏洞教学和检测的WEB脆弱性测试程序。包含了SQL注入、XSS、盲注等常见的一些安全漏洞...2016-11-25
  • PHP测试成功的邮件发送案例

    mail()函数的作用:连接到邮件服务器,利用smtp协议,与该服务器交互并投邮件。注意:1、mail函数不支持esmtp协议,---即,只能直投,不能登陆2、由上条,我们只能直投至最终的收件服务器地址.而该地址,又是在PHP.ini中指定的,所...2015-10-30