一、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()

android app自动化测试之UIAutomator的更多相关文章

  1. Android app自动化测试之Python+Appium环境搭建

    1.安装JDK (1)JDK安装时会有两次,一次是jdk,第二次是jre. (2)环境变量配置: 添加JAVA_HOME变量, 值:Jdk的安装路径 添加CLASSPATH变量,值: .;%JAVA_ ...

  2. Appium+Python app自动化测试之脚本启动和停止Appium服务

    研究了一段时间的Appium android app的自动化测试,工作中需要连接多台手机终端同时执行测试用例,我实现的方式是获取用例中需要执行用例的设备id个数以及实际连接到的设备数(通过adb de ...

  3. Android App性能测试之adb命令

    本篇文章总结了Android App性能测试过程中常用的adb命令.通过这些adb命令,可以查看App的性能数据,为评判性能好坏作参考. CPU相关 显示占用CPU最大的5个应用 adb shell ...

  4. android自动化测试之uiautomator

    1.通过monkeyrunner.bat   monkey_record.py启动MonkeyRecorder进行拿到各个控件的坐标(要连上手机才可以启动) 2.也可以通过uiautomatorvie ...

  5. app自动化测试之实战应用(百度app简单测试)

    模拟在百度app中搜索python相关内容代码如下: from appium import webdriver desired_caps = {} desired_caps['deviceName'] ...

  6. 【Python + ATX基于uiaotumator2】之Android—APP自动化简易例子

    上代码: import uiautomator2 as u2 from time import sleep d = u2.connect_usb('608ad0fe') #打开小卖 # d(text= ...

  7. app UI测试之UIAutomator

    执行UIAutomator测试步骤 1.新建Java项目,导入android.jar和uiautomator.jar包,继承UiAutomatorTestCase 2.生成编译文件 android c ...

  8. Android APP 性能测试之 GT 工具

    一.介绍: GT(随身调)是 APP 的随身调测平台,它是直接运行在手机上的"集成调测环境"(IDTE, Integrated Debug Environment).利用 GT,仅 ...

  9. Android App性能测试之二:CPU、流量

    CPU---监控值的获取方法.脚本实现和数据分析 1.获取CPU状态数据 adb shell dumpsys cpuinfo | findstr packagename 自动化测试脚本见cpustat ...

随机推荐

  1. linux bash & profile &bash_profile 小结

    login 方式:: su - oracle 依次 /etc/bash.bashrc———— /home/$user/.bashrc ———— /ect/profile ———— /home/$use ...

  2. iOS-策略模式

    在实际开发过程中,app需求都是由产品那边给出,往往是他给出第一版功能,我们写好代码后,会相应的给出第二版.第三版功能,而这些功能是在实际使用中,根据用户需求而不断增加的.如果在编码之初,我们并未认识 ...

  3. C 中读取键盘码

    键盘码在底层开发中经常用到,有时候我们会忘记它们,就要急急忙忙的去找 键-码 对照表查看,其实程序可以自己打印出 键-码 对应值 #include <stdio.h> #include & ...

  4. 1.安装Redis

    首要条件:安装VMware,在虚拟机中安装CentOS. 安装步骤: 1.打开终端(Terminal) 2.在终端输入:wget http://download.redis.io/releases/r ...

  5. Design5:Sql server 文件组和文件

    1,文件组和文件的作用 Sql Server的数据存储在文件中,文件是实际存储数据的物理实体,文件组是逻辑对象,Sql server通过文件组来管理文件. 一个DataBase有一个或多个FileGr ...

  6. [WPF]有滑动效果的进度条

    先给各位看看效果,可能不太完美,不过效果还是可行的. 我觉得,可能直接放个GIF图片上去会更好. 我这个不是用图片,而是用DrawingBrush画出来的.接着重做ProgressBar控件的模板,把 ...

  7. NGUI 可裁剪的灰度Shader

    Shader "Custom/Unlit - Transparent Colored Grayed (SoftClip)" { Properties { _MainTex (&qu ...

  8. android.os.networkonmainthreadexception怎么解决

    在android2.3以后,在主线程中必须使用另一个线程,如handler机制,或者异步任务获取网络数据. 如果你的访问网络操作必须放在主线程中执行,那么在onCreate()中添加 if (GetV ...

  9. 学习ng2,从zonejs开始(非官方翻译) ----angular2系列(一)

    Zone是什么: 官方解释:zone.js为JavaScript提供了执行上下文,可以在异步任务之间进行持久性传递. 最开始我一直没理解到这句话,学习过程中我也因为自己的一些失误而一直纠结徘徊,情况是 ...

  10. 应用 CSS3 动画实现12种风格的通知提示

    今天我们想分享几个简单的款式的网站通知效果.有很多的方式来呈现消息给用户:从经典的咆哮般的通知到窗口顶部的通知栏.各个网站的通知样式和效果千篇一律,难得出现创意的通知效果.这里是实现一个应用 CSS3 ...