一、UIAutomator

Android自动化测试工具有很多,但是要免费、易上手,本人觉得就直接使用Eclipse自带的UIAutomator就不错。测试人员无需跟开发要代码信息,只要手机上有安装之后的APP自己就能做出自动测试用例,况且一通百通,就算是不满足于UI测试的,找个简单易上手的先明白原理,再深入了解其它复杂工具也会轻松很多。何乐而不为呢?

UIAutomator是Eclipse自带的用于UI自动化测试工具,可仿真APP上的单击、滑动、输入文本等操作。

在使用之前,需要安装好java有关的JDK,SDK,然后配置java环境变量。关于安装JDK,SDK,配置JAVA环境变量网上有数不清的教程,这里就不废话了。直接进入主题。

还是简单介绍一下自动化测试代码中使用到的类关系:现在还不清楚也没有关系,先让程序跑起来,然后在使用API的过程中自然就理解了。

1、创建java工程

打开Eclipse,操作步骤: 点击“File—>New—>java Project”,然后打开如下图的界面:

其它的先不用管,既然要求必须输入Project name,那就输入。请用英文命名,很多时候中文会带来许多意想不到的问题。

输入Project name之后,点击“Next”进入下一步,看到的界面如下:

Source下的东西无需关注,点击“Libraries”,既然是调用java自带的UIAutomator,就要先引入UIAutomator使用到的包。导入uiautomator相关包:点击“Add External JARs”

先要找到自己的android.jar 和 Uiautomator.jar 包的位置,一般在安装文件下的adt-bundle-windows-x86-20130917\sdk\platforms\android-18\ 下,具体要找到自己电脑上的jar位置哦,

选中两个jar文件,“打开”导入。

此外还需要再加入本地库,点击“Add Library”,选择Junit,后点击“next”,使用默认的JUit3。点击“Finish”。

回到New Java Project,点击“Finish”即可创建工程成功。

2、创建class文件

刚刚创建的工程,Src下还没有任何东西,我们需要创建一个Class文件,用来写自动化程序代码。

创建class文件:项目名称右键——NEW——class,写class内容如下

2.1 import UIautomator两个包

2.2 class 名称必须和XXX.java名称相同

2.3每一个public开头,且以test命名开始的函数,系统就视为一个case,运行时方法的调用顺序并不是代码中的位置顺序,而是根据方法名的ASCII码大小顺序调用的。

2.4每一个class都必须继承自UiAutoMatorTestCase

3、生成xml 文件

找到target版本:>android list targets

Id:1

创建build.xml到测试工程中去,用于生成.jar文件:>Android create UItest-project –n testprojectname –t 1 –p testprojectpath

4.build jar 文件

4.1 Update Ant to 1.9

4.2 配置ant使用的javac版本:

右键build.xml—》RUN AS —》 External Tools Configurations。

        在main Tab 下输入Argumens: -Dbuild.compiler=javac1.7——》

Apply。

4.3配置sdk中的build.xml 文件E:\Android\android\adt-bundle-windows-x86-20130917\sdk\tools\ant\build.xml: 找到javac,添加includeantruntime="false"

<javac encoding="${java.encoding}"

source="${java.source}" target="${java.target}"

debug="true" extdirs="" includeantruntime="false"

destdir="${out.classes.absolute.dir}"

bootclasspathref="project.target.class.path"

verbose="${verbose}"

classpathref="project.javac.classpath"

fork="${need.javac.fork}" includeantruntime="false">

<src path="${source.absolute.dir}" />

<src path="${gen.absolute.dir}" />

<compilerarg line="${java.compilerargs}" />

</javac>

5. push jar文件到手机

Cmd环境下需要先找到adb.exe所在位置目录,完整的命令语句:

adb push <path_to_output_jar>  /data/local/tmp

“<path_to_output_jar>”即要放到手机里的jar文件名及路径

相反的如果是从手机复制文件到电脑:

6. 运行测试

Cmd环境下需要先找到adb.exe所在位置目录,完整的命令语句:

adb shell uiautomator runtest  XXX.jar  -c  XXXClassname

备注:XXX.jar需要运行的jar文件名,

Classname: 需要运行jar中的哪个类的名字

7.测试结果

运行结果如下:

Current: 当前运行的测试编号,与方法名称相关

Class:当前运行的函数所在的类

Numtests:测试的总数,每一个public testXX就是一个测试数

Test: 当前测试的函数名称

INSTRUMENTAION_STATUS_CODE:测试状态码,1表示正在执行,0表示执行成功,-1编辑运行错误

二、使用UIAUTOMATORVIEWER获取APP控件

1.手机打开app

使用UIAutomator获取手机app控件时,先在手机上运行起对应app

2 .电脑连接手机

确保电脑与手机已连通。

先在cmd界面到达java的adb.exe所在位置,

运行命令“ >adb shell ”获取当前连接电脑的设备,如下图,如果adb shell 运行正常,会弹出“shell@hwH30-C00:/$”

有多个设备连接开发机器时(模拟器或真机),通过设置ANDROID_SERIAL环境变量指定需要截图的设备。比如下图的:shell@hwH30-C00:/$ set ANDROID_SERIAL= hwH30-C00,设置连接成功后,返回:“set ANDROID_SERIAL=hwH30-C00”

adb devices命令查看连接中的设备编号

如果当前只连接了一个设备,则不用再在Cmd中执行连接命令

3.运行uiautomatorviewer.bat

找到uiautomatorviewer.bat安装目录,本机中的位置如下:

E:\Android\android\adt-bundle-windows-x86-20130917\sdk\tools\uiautomatorviewer.bat

双击运行uiautomatorviewer.bat,打开界面如下图:

上图中左半部分显示当前手机的呈现界面,若要获取其它界面的控件,需要在手机上后,点击上图左上角顶部的刷新,重新获取新界面。

上图右半部分显示当前界面的所有ui元素层即控件信息。右上半部分显示层级,右下部分显示指定层级上具体的控件属性,比如当前属性“class”:当前控件的所在class

“Package”:显示当前控制所在包

“Resource-id”:这个属性有最好了,有些Android开发人员没有为每个控件单独命名,在抓取的时候就非常不方便。如果有Resource-id,在抓取控件时,直接用Resource-id就能获取到正确的控件,并且操作正确,如果没有Resource-id,就只能通过查找同类控件后再按顺序获取了。比如先找到所有的textbox控件,再从所有的textbox控件中按序取几个,才能抓取到对应控件。

还有一些控件属性显示是否可见,是否checked,有实际应用需要时,可以获取

这里附上一个简单的测试java源代码,供参考代码格式

import com.android.uiautomator.core.UiDevice;

import com.android.uiautomator.core.UiObject;

import com.android.uiautomator.core.UiObjectNotFoundException;

import com.android.uiautomator.core.UiScrollable;

import com.android.uiautomator.core.UiSelector;

import com.android.uiautomator.testrunner.UiAutomatorTestCase;

public class MyTestCase extends UiAutomatorTestCase {

public void testDemo() throws UiObjectNotFoundException {

//点击home回到桌面

UiDevice device = getUiDevice();

device.pressHome();

// 点击并等待打开app

UiObject czmApp = new UiObject(new UiSelector().className(android.widget.TextView.class.getName()));

czmApp.clickAndWaitForNewWindow();

sleep(1000);

//登录;instan从0开始,

UiObject username= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/login_username_edit"));

username.clearTextField();

username.setText("15300000018");

UiObject pwd= new UiObject(new UiSelector().className("android.widget.EditText").enabled(true).resourceId("com.wlyc.warehousechampions:id/login_password_edit"));

pwd.clearTextField();

pwd.setText("00OA8C");

device.pressBack();

sleep(500);

UiObject enter= new UiObject(new UiSelector().className("android.widget.Button").resourceId("com.wlyc.warehousechampions:id/login_btn"));

enter.click();

sleep(5000);

//跳转到“我的”

UiObject My= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/personcenter"));

My.clickAndWaitForNewWindow();

sleep(500);

//检查认证状态

UiObject result= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/personcenter_apply_status"));

int n=result.getText().length();

//认证审核中

n=Dur_verify(n);

//认证失败 或者认证成功

if(n==4)

{

UiObject failed= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/apply_certificate_layout"));

failed.click();

UiObject isedit= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/titlebar_right_layout"));

//认证失败

if(isedit.exists())

{

isedit.click();

write();

sleep(500);

}

else //认证成功

{

UiObject backtoMy= new UiObject(new UiSelector().className("android.widget.LinearLayout").resourceId("com.wlyc.warehousechampions:id/titlebar_left_layout"));

backtoMy.click();

Release();

}

}

n=result.getText().length();

n=Dur_verify(n);

Release();

}

//录入并提交企业认证

public void write()throws UiObjectNotFoundException

{

//跳转到企业认证

UiObject Company= new UiObject(new UiSelector().className("android.widget.RadioButton").resourceId("com.wlyc.warehousechampions:id/company_certificate"));

Company.click();

//录入企业认证信息

UiObject CompanyName=new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/cettificate_companyname_edit"));

CompanyName.setText("test");

sleep(500);

//拍照

UiObject photo= new UiObject(new UiSelector().className("android.widget.ImageView").resourceId("com.wlyc.warehousechampions:id/uploadimage_image"));

photo.click();

sleep(500);

UiObject takephoto= new UiObject(new UiSelector().className("android.widget.TextView").instance(1));

takephoto.click();

sleep(500);

UiObject phototake= new UiObject(new UiSelector().className("android.widget.ImageView").resourceId("com.android.gallery3d:id/shutter_button"));

phototake.click();

sleep(500);

UiObject photook= new UiObject(new UiSelector().className("android.widget.ImageView").resourceId("com.android.gallery3d:id/btn_done"));

photook.click();

sleep(500);

//提交认证

UiObject commit= new UiObject(new UiSelector().className("android.widget.Button").resourceId("com.wlyc.warehousechampions:id/commit_btn"));

commit.click();

sleep(500);

}

//发布仓库

public void Release() throws UiObjectNotFoundException

{

UiObject wh_release= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/warehouse_distribute"));

wh_release.click();

UiObject wh_name= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_name_edit"));

wh_name.setText("nametest");

UiObject wh_ctg= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/spinnerview_layout"));

wh_ctg.clickAndWaitForNewWindow();

UiObject wh_ctg1= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/spinnerview_listitem_text"));

wh_ctg1.click();

UiObject wh_mod= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/spinnerview_layout").instance(1));

wh_mod.click();

sleep(500);

UiObject wh_mod1= new UiObject(new UiSelector().className("android.widget.TextView").enabled(true).instance(0));

wh_mod1.click();

sleep(500);

UiObject wh_area= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_area_edit"));

wh_area.setText("5000");

UiObject wh_area_low= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_rentarea_edit"));

wh_area_low.setText("500");

UiObject wh_area_cpb= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_availrentarea_edit"));

wh_area_cpb.setText("1000");

UiObject wh_pric= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_price_edit"));

wh_pric.setText("26");

UiObject wh_etpris= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_company_edit"));

wh_etpris.setText("companyname");

UiObject wh_floor= new UiObject(new UiSelector().className("android.widget.CheckBox").enabled(true).instance(0));

wh_floor.click();

sleep(500);

//向下滑动屏幕

UiScrollable  wh_scroll=new UiScrollable(new UiSelector().className("com.wlyc.warehousechampions:id/pulltorefresh_listview"));

wh_scroll.setAsVerticalList();

;

UiObject wh_addre= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/warehousedetail_hightemperature_unit"));

wh_addre.click();

}

//认证审核中等待

public int Dur_verify(int n) throws UiObjectNotFoundException

{

while (n!=4) {

UiObject My= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/personcenter"));

My.clickAndWaitForNewWindow();

sleep(5000);

UiObject result= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/personcenter_apply_status"));

n=result.getText().length();

}

return n;

}

}

三、Uiautomator Api分析

UiAutomator主要涉及以下几个类,大多数位于源码包的com.android.uiautomator.core下,其中粗体字部分为主要会接触到的类,熟知这5个类的作用,就可以大体顺畅的写出UiAutomator的测试用例。

UiAutomatorTestCase

UiDevice

UiSelector

UiScrollable

UiObject

UiCollection

UiTestAutomationBridge, UiAutomatorBridge

InteractionController, QueryController

UiWatcher

1、UiAutomatorTestCase

TestCase (Junit) -> UiAutomatorTestCase  -> App Test

每个测试用例(类)都需要继承UiAutomatorTestCase,以实现测试环境的setup,teardown(拆卸)等同能。而UiAutomatorTestCase则是通过继承Junit3中的TestCase类,并在其中的setUp() 、tearDown() 、getParams() 函数中。其中主要是用Bundle实现Android Activity之间的通讯。在UiAutomatorTestCase,还加入了getUiDevice()等关于UiDevice的 函数,以实现在测试的任意地方均可调用UiDevice()。

2、UiDevice

此类主要包含了获取设备状态信息,和模拟用户至于设备的操作两类api。

可以通过getDisplaySizeDp(), getDisplayWidth() , getDisplayHeight() ,getProductName() ,getCurrentActivityName(), getCurrentPackageName() 等获取设备相关信息。

pressMenu(), pressBack(), pressHome(), pressSearch() ,pressDPadCenter(), pressDPadRight(), pressDPadLeft(), pressDPadUp(), pressDPadDown() ,pressDelete(), pressEnter(), pressKeyCode(), pressRecentApps(),click(),swipe(),getDisplayRotation() setOrientationLeft()… wakeUp(), sleep() ,dumpWindowHierarchy(), waitForWindowUpdate()等API可以灵活的操纵设备。

而takeScreenshot() 允许随时对设备截屏。

3、UiSelector

主要是通过一定查询方式,定位到所要操作的UI元素。

一般UI元素均可通过以下API定位:text(), textMatches(String regex), textStartsWith(), textContains() ,className() ,classNameMatches(String regex), className(Class type) ,Description(), descriptionMatches(String regex),descriptionStartsWith(),descriptionContains() ,packageName(), packageNameMatches(String regex)。

值得注意的是index()和 instance() 两个函数,其中index()是当前页面的ID编号,instance()则表示在一定的搜索结果下,获取的子元素集的第几个元素。如:

new UiSelector().className("android.widget.ImageView").enabled(true).instance(2);

另有enabled(), focused(), focusable(), scrollable(), selected(), checked(), clickable() ,longClickable() ,childSelector()等检索条件,顾名思义。

4、UiObject

UiObject可代表页面的任意元素,它的各种属性定位通常通过UiSelector来完成。

比较常用的Api如clickAndWaitForNewWindow(),表示点击该元素,并且等待新窗口的展示完毕。这一过程是Android UI Testing框架支持的,不需要额外的控制等待时间。

UiObject允许点击该元素的具体一个部分,Api如clickTopLeft(), longClickBottomRight(),…

通过getText(), getContentDescription(), getVisibleBounds(),… 等api可获取UiObject的相关属性,getPackageName() 可用来明确是否打开了目标测试的App.

setText(), clearTextField() 可以 用来设置以及清空所关联的输入框。

waitForExists() 可以用来操纵相关等待或验证。

5、UiCollection

UiCollection一般与UiSelector连用,如它的构造函数也要求提供Uiselector: UiCollection(UiSelector selector)。

它的api较少,主要用以从Uiselector筛选出的元素集中挑出所要的元素:getChildByDescription(), getChildByInstance(), getChildByText() ,以及统计元素集的个数getChildCount()

6、UiScrollable

UiObject -> UiCollection ->UiScrollable

UiScrollable 用来表示可以滑动的界面元素,其继承关系如上图所示。

其Api中,setAsVerticalList(), setAsHorizontalList() 用以设置Ui元素列表是基于横向滚动还是纵向滚动。其后可以用getMaxSearchSwipes() ,flingForward(), flingBackward() ,scrollForward(),scrollBackward() ,scrollToEnd(), scrollToBeginning() 等函数控制滑动,以及getChildByDescription(), getChildByInstance(), getChildByText() ,scrollIntoView(), scrollTextIntoView(),… 来选择是否已经转换到具有目标元素的页面。如:

UiScrollable appViews = new UiScrollable(new UiSelector().scrollable(true));

appViews.setAsHorizontalList();

UiObject helperApp;

helperApp = appViews.getChildByText(new UiSelector()

.className(android.widget.TextView.class.getName()), " 91助手 ");  则若当前页面没有91助手APP, 测试会自动滑动页面,直到91助手App出现。

下面介绍下UI Testing Framework构成的重要类:

7、UiTestAutomationBridge

这是整个Testing Framework的基础,此类负责连接系统了,记录最新的可链接事件(AccessibilityEvent) , 窗口内容查询Api等。可以被Android App调用,或者Java程序从shell调用。

这里需要注意两个概念:

7.1、AccessibilityEvent:所有的Ui元素可以被操纵,因为这些Event都是AccessibilityEvent。对于怎样令页面元素可以被操纵,使得相关的事件都是AccessibilityEvent,请参见Uiautomator 词条-"确认程序可以被测试" 部分。

7.2、AccessibilityNodeInfo:视窗中的组件树节点,也就是uiautomtorViewer中展示的各个节点。

Api中connect(), disconnect() 负责建立与设备的实际连接。

executeCommandAndWaitForAccessibilityEvent() performAccessibilityAction() findAccessibilityNodeInfosByText(), findAccessibilityNodeInfoByViewIdInActiveWindow() 都是其中重要的Api。

8、UiAutomatorBridge

UiAutomatorBridge是UiTestAutomationBridge的子类,区别主要是在构造函数中加上了InteractionController 和QueryController 两大对象的调用。以及一些常量定义等。除了上述差异,UiAutomatorBridge还定义了executeCommandAndWaitForAccessibilityEvent() 、onAccessibilityEvent() 、waitForIdle() 、addAccessibilityEventListener() 等函数。

9、InteractionController

介绍InteractionController,需要先提InteractionProvider,它负责注入用户事件(如点击、输入等) ,并且反应事件的对应坐标。

InteractionController则定义了几乎所有至于手机的基础操作,如runAndWaitForEvents(), clickAndWaitForEvents() ,click(), longTap(), scrollSwipe(),Swipe() ,clickAndWaitForNewWindow() ,touchUp(), touchDown(), TouchMove() ,isNaturalRotation(), setRotationRight(), setRotationLeft() ,freezeRotation() ,wakeDevice(), sleepDevice() 等。

10、QueryController

QueryController负责把UiSelector 的查找信息转化为AccessibilityNodeInfo。

具体Api如下:findNodePatternRecursive(), translatePatternSelector(), translateReqularSelector(), translateCompoundSelector(), getRootNode() ,findAccessibilityNodeInfo()。

11、UiWatcher

UiWatcher只在UiSelector无法找到匹配的结果时被调用,意在重试、等待页面更新 (如弹出对话框)等。其中只有一个主要函数:checkForCondition() 。

它的相关函数均在UiDevice中,如:UiDevice.registerWatcher() ,UiDevice. resetWatcherTriggers() ,UiDevice.runWatchers() ,UiDevice.removeWatcher()

转载于:https://www.cnblogs.com/wangchaozhi/p/5158575.html

android app自动化测试之UIAutomator相关推荐

  1. APP自动化测试之录制脚本:3.运行录制的脚本

    APP自动化测试之录制脚本:3.运行录制的脚本 1.前提 基于win10专业版64位系统+jdk1.8+python3+pycharm+android SDK+appium+unittest.运行录制 ...

  2. android性能测试自动化,Android App自动化性能测试探究

    作者:彭海波 前言 Android App的性能测试是移动测试过程中必不可少的一个环节.在我们项目组内,性能测试的过程是这样的,先设置测试场景,然后一边手工执行场景,一边通过工具获取性能数据,为了减少 ...

  3. 干货|app自动化测试之Appium 原理 与 JsonWP 协议分析

    想要使用 Appium 进行测试,那么就一定要先了解Appium的原理.Appium 不仅能够实现移动端的 JSONWP,并且延伸到了 Selenium 的 JSONWP,它能够控制不同移动设备的行为 ...

  4. |app自动化测试之Appium问题分析及定位

    Appium 是由 Node.js 来实现的 HTTP 服务,它并不是一套全新的框架,而是将现有的优秀的框架进行了集成,在 Selenium WebDriver 协议(JsonWireProtocol ...

  5. 干货|app自动化测试之Appium 源码修改定制分析

    Appium 是由 Node.js 来实现的 HTTP 服务,它并不是一套全新的框架,而是将现有的优秀的框架进行了集成,在 Selenium WebDriver 协议(JsonWireProtocol ...

  6. app自动化测试之Appium问题分析及定位

    使用 Appium 进行测试时,会产生大量日志,一旦运行过程中遇到报错,可以通过 Appium 服务端的日志以及客户端的日志分析排查问题. Appium Server日志-开启服务 通过命令行的方式启 ...

  7. 干货|app自动化测试之模拟器控制

    测试时需要用到测试设备,通常测试人员会选择使用模拟器进行自动化调试.模拟器分为原生模拟器与第三方模拟器.我们今天介绍的就是原生模拟器是 Android SDK 中自带的模拟器,可以通过 Android ...

  8. App自动化测试之Adb基础命令使用

    一.ADB介绍 adb(android debug bridge)是android sdk自带的一个工具. Adb是用来连接android设备和PC端的桥梁,通过adb工具,用户可以在PC端对手机进行 ...

  9. app自动化测试之Appium 源码分析

    Appium 是由 Node.js 来实现的 HTTP 服务,它并不是一套全新的框架,而是将现有的优秀的框架进行了集成,在 Selenium WebDriver 协议(JsonWireProtocol ...

最新文章

  1. 【java项目实战】代理模式(Proxy Pattern),静态代理 VS 动态代理
  2. [Spring cloud 一步步实现广告系统] 19. 监控Hystrix Dashboard
  3. 解读:一种来自Facebook团队的大规模时间序列预测算法(附github链接)
  4. PHP内核的学习--PHP生命周期
  5. Get Set的问题解决
  6. 数据库-MySQL-SQL语句
  7. 新博开通,近期将推出系列博客之测试工具篇!
  8. secureCRT 如何上传下载文件
  9. 拓端tecdat|R语言文本挖掘NASA数据网络分析,tf-idf和主题建模
  10. jenkins集成钉钉
  11. Symantec BE 安装及备份oracle 完整版
  12. 利用OpenCV将图片反色
  13. GitHub:基于搜狗微信搜索的微信公众号爬虫接口
  14. HttpClient超时时间 timeout
  15. 夜雨数竞笔记-极限(11)-欧拉常数
  16. 天线越大越好吗_路由器的天线是不是越多越好?告知你真实答案,很多人都买错了...
  17. 没有备份iPhone误删通讯录怎么恢复
  18. 告别win10,拥抱linux
  19. SQL优化不会?推荐4 款工具
  20. 如果哈夫曼树有67个结点,则可知叶结点总数为

热门文章

  1. html audio 本地无效源,音频文件无法播放?H5的audio标签在win7下运行时报错“无效源”?...
  2. 超链接的内部链接和外部链接
  3. C#教程4:数据类型
  4. 服务器微信了早上好,微信问候语早上好 微信早上好经典问候语
  5. java中8进制常量_下列关于Java语言简单数据类型的说法中,正确的一项是______。A.以0开头的整数代表8进制整型常量B....
  6. ajax使用json下拉框,ajax请求后台得到json数据后动态生成树形下拉框的方法
  7. Python代码调试方法
  8. android 市场自动安装软件,Android 应用中跳转到应用市场评分示例
  9. vue 请求多个api_Vue 创建多人共享博客
  10. Spring学习8之静态代理