A. 用android uiautomator做自动化测试,怎么连接真机进行
Google在sdk4.0以后提供了一个自动化解决方案uiautomator:
优点:可以跨应用了;这可是亲生的;
缺点:必须sdk4.0以上版本;要想实现的好,最好有开发配合;java项目编译为jar后需要push到手机才能运行,也就是说必须打印日志暴力调试。
Appium基于Android InstrumentationFramework和UIAutomator,也就是说这个工具是可以跨应用的。说远了,好吧,为了帮大家更容易理解appium的使用,我这里就讲一下uiautomator的使用方法。
你应该有android-sdk吧,升级到4.0以上,进入目录android-sdk ools,你会看到两个文件:
traceview.bat 和 uiautomatorviewer.bat,这俩文件让你想起了monkeyrunner了吧,是的,traceview.bat就对应于hierarchyviewer.bat,用来查看程序的ui界面的,通常也是使用管理员权限启动的。
好了,现在用eclipse创建一个java project,是的,你没看错,是java project不是android project,添加引用:
在project.properties中内容为:
# Project target.
target=android-16
这里的android-16需要和之前的android.jar和uiautomator.jar位置相一致。
然后呢?写代码吧,建立一个类,得,发个给大家参考:
packagecom.uia.example.my;
importorg.apache.http.util.EncodingUtils;
importandroid.graphics.Bitmap;
importandroid.graphics.BitmapFactory;
importandroid.graphics.Rect;
importandroid.os.Environment;
importcom.android.uiautomator.core.UiObject;
importcom.android.uiautomator.core.UiObjectNotFoundException;
importcom.android.uiautomator.core.UiScrollable;
importcom.android.uiautomator.core.UiSelector;
importcom.android.uiautomator.testrunner.UiAutomatorTestCase;
importjava.io.File;
importjava.io.FileOutputStream;
importjava.io.IOException;
{
publicStringsLog;
publicFilefout=null;
=null;
publicvoidwrite2file(String filename,String sData)
{
String sLog="";
//初始化日志文件
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
sLog = Environment.getExternalStorageDirectory().getAbsolutePath();
try{
fout=newFile(sLog,filename);
outStream=newFileOutputStream(fout,true);//此处的true是append
sData=sData +" ";
outStream.write(sData.getBytes());
outStream.flush();
outStream.close();
fout=null;
}
catch(Exception e){
e.printStackTrace();
}
}else{
System.out.println("该手机没有SD卡");
}
}
publicvoidtestDemo() {
//1.启动app
getUiDevice().pressHome();
UiObject allAppsButton =newUiObject(newUiSelector().description("Apps"));
allAppsButton.clickAndWaitForNewWindow();
UiObject appsTab =newUiObject(newUiSelector().text("Apps"));
appsTab.click();
UiScrollable appViews =newUiScrollable(newUiSelector().scrollable(true));
UiObject settingsApp = appViews.getChildByText(newUiSelector().className(android.widget.TextView.class.getName()),"Efilm");
settingsApp.clickAndWaitForNewWindow();
//2.进入主界面
System.out.println("into main view");
System.out.println(getUiDevice().waitForWindowUpdate("com.eshore.efilm", 60000));
System.out.println("intoed main view");
UiObject tv1 =newUiObject(newUiSelector().text("影院"));
tv1.click();
//3.点击影院
UiObject oyy=newUiObject(newUiSelector().description("cinema_row"));
System.out.println("wait yingyuan come out");
oyy.waitForExists(60000);
System.out.println("yingyuan come out");
oyy.clickAndWaitForNewWindow();
System.out.println("click yingyuan");
//4.场次
UiObject occ=newUiObject(newUiSelector().description("LinearLayout10"));
System.out.println("wait changci come out");
oyy.waitForExists(60000);
System.out.println("changci come out");
occ.clickAndWaitForNewWindow();
System.out.println("click changci");
//5.座位
UiObject oseat=newUiObject(newUiSelector().description("cinema_shows_list_item").index(0).childSelector(newUiSelector().description("LinearLayout10")));
System.out.println("wait seat come out");
oseat.waitForExists(5000);
inth=getUiDevice().getDisplayHeight();
intw=getUiDevice().getDisplayWidth();
System.out.println("(h/2,w/2)="+h/2+","+w/2);
getUiDevice().click(h/2,w/2);
//System.out.println("seat count:"+String.valueOf(oseat.getChildCount()));
//System.out.println("seat getText:"+ oseat.getText());
//截座位图
Process process;
try{
process = Runtime.getRuntime().exec("screencap /mnt/sdcard/EfilmFailSnapShot01.png");
try{
process.waitFor();
}catch(InterruptedException e) {//TODOAuto-generated catch block
e.printStackTrace();
}
}catch(IOException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}
//takeScreenShots("EfilmSeatSnapShot");
}
}
这个例子是随便写的,可能不够严谨。大体就这么个情况吧。下一步就是编译执行了,先插上手机usb接口,然后打开cmd,执行:
找到SDKID,也就是android create中的-t参数:
cd C: PROGRAMandroid-sdk ools
android list
找到t参数的值以后:
cd C: PROGRAMandroid-sdk ools
android create uitest-project -n TAppWorkAssistV1 -t 25 -p C:android自动化Tv2.0TestSetting
cd C:android自动化Tv2.0TestSetting
ant build
cd C:android自动化Tv2.0TestSettingin
adb push TAppWorkAssistV1.jar /data/local/tmp/
adb shell uiautomator runtest TAppWorkAssistV1.jar -c com.uia.example.my. TAppWorkAssistV1
看了看,好像没有什么特别值得解释的
-n TAppWorkAssistV1:类名
-p: 项目所在目录
Ant build 把这个类编译成一个jar包:TAppWorkAssistV1.jar
然后把jar包push到手机上,调用执行这个类就可以了
大致是这么个步骤,不过有一个非常重要的细节,就是如果你需要更省心,就最好把界面元素,无论动态的还是布局文件中的,都加上content-description属性,并保证唯一性,根据:
UiSelector:description(String desc)
Set the search criteria to match thecontent-description property for a widget.
那就可以统一只使用这一个引用界面元素的方法就行了,就不用去想方设法利用其它的属性来引用了。
B. 如何通过反射使用BluetoothHeadset类
Usb
setting 中tethering 设置流程
一 资源位置及入口文件
USB
tethering, usb_tethering_button_text
Xml/tether_prefs.xml------usb_tether_settings,布局文件
packages/apps/Settings/AndroidManifest.xml
<!-- Keep compatibility with old
shortcuts. -->
<activity-alias
android:name=".TetherSettings"
android:label="@string/tether_settings_title_all"
android:clearTaskOnLaunch="true"
android:exported="true"
android:targetActivity="Settings$TetherSettingsActivity">
<meta-data
android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.TetherSettings"
/>
<meta-data
android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
android:resource="@id/wireless_settings"
/>
<meta-data
android:name="com.android.settings.PARENT_FRAGMENT_TITLE"
android:resource="@string/wireless_networks_settings_title"
/>
<meta-data
android:name="com.android.settings.PARENT_FRAGMENT_CLASS"
android:value="com.android.settings.Settings$WirelessSettingsActivity"
/>
</activity-alias>
TetherSettings.java
二 触发流程
2.1
TetherSettings.java
packages/apps/Settings/src/com/android/settings/TetherSettings.java
onPreferenceTreeClick
SXlog.d(TAG,
"onPreferenceTreeClick - setusbTethering(" + newState + ")
mUsbTethering: " + mUsbTethering);
setUsbTethering(true);
下面是设置时抓取的log日志
D/TetherSettings( 543): onPreferenceTreeClick -
setusbTethering(true) mUsbTethering: true
D/Tethering(
271): setUsbTethering(true)
D/UsbDeviceManager( 271):
setCurrentFunction(rndis) default: false
W/UsbDeviceManager( 271): handleMessage:
2
W/UsbDeviceManager( 271): setEnabledFunctions:
functions = rndis
W/UsbDeviceManager( 271): setEnabledFunctions,
mDefaultFunctions: mtp,adb
W/UsbDeviceManager( 271): setEnabledFunctions,
mCurrentFunctions: mtp,adb
D/UsbDeviceManager( 271): setEnabledFunctions,
mSettingFunction: mtp,adb
W/UsbDeviceManager( 271): containsFunction,
functions: rndis
W/UsbDeviceManager( 271): containsFunction,
function: adb
W/UsbDeviceManager( 271): containsFunction
index: -1
W/UsbDeviceManager( 271): addFunction,
functions: rndis
W/UsbDeviceManager( 271): addFunction, function:
adb
W/UsbDeviceManager( 271): containsFunction,
functions: rndis
W/UsbDeviceManager( 271): containsFunction,
function: acm
W/UsbDeviceManager( 271): containsFunction
index: -1
W/UsbDeviceManager( 271): containsFunction,
functions: rndis,adb
W/UsbDeviceManager( 271): containsFunction,
function: acm
W/UsbDeviceManager( 271): containsFunction
index: -1
D/UsbDeviceManager( 271):
setUsbConfig(none)
W/UsbDeviceManager( 271): setUsbConfig, config:
none
public
boolean onPreferenceTreeClick(PreferenceScreen
screen, Preference preference) {
ConnectivityManager cm =
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
if
(preference == mUsbTether) {
if
(!mUsbTethering) {
boolean
newState = mUsbTether.isChecked();
mUsbTethering = true;
mUsbTetherCheckEnable = false;
mUsbTether.setEnabled(false);
SXlog.d(TAG,
"onPreferenceTreeClick - setusbTethering(" + newState + ")
mUsbTethering: " + mUsbTethering);
if
(newState) {
startProvisioningIfNecessary(USB_TETHERING);
} else
{
setUsbTethering(newState);
}
} else
{
return
true;
}
} else if
(preference == mBluetoothTether) {
private void
setUsbTethering(boolean
enabled) {
ConnectivityManager cm =
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
if
(cm.setUsbTethering(enabled) !=
ConnectivityManager.TETHER_ERROR_NO_ERROR) {
mUsbTether.setChecked(false);
mUsbTether.setSummary(R.string.usb_tethering_errored_subtext);
return;
}
mUsbTether.setSummary("");
}
2.2
ConnectivityManager.java
frameworks/base/core/java/android/net/ConnectivityManager.java
private
final IConnectivityManager
mService;
public int
setUsbTethering(boolean
enable) {
try
{
return
mService.setUsbTethering(enable);
} catch
(RemoteException e) {
return
TETHER_ERROR_SERVICE_UNAVAIL;
}
}
manager 和service有一个对应关系,固定的规则。 manager是为了sdk诞生的,方便app开发者调用。其实可以直接调用service,如mountservice是没有mountmanager的。
service是在系统起来是就被android系统启动的,而manager是后期有需要时实例化起来的。
Service的目录在:/frameworks/base/services/java/com/android/server/
manager的目录在:frameworks/base/core/java/android
2.3
ConnectivityService.java
frameworks/base/services/java/com/android/server
private
Tethering mTethering;
public int
setUsbTethering(boolean
enable) {
enforceTetherAccessPermission();
if
(isTetheringSupported()) {
return
mTethering.setUsbTethering(enable);
} else
{
return
ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
}
}
2.4
Tethering.java
frameworks/base/services/java/com/android/server/connectivity/Tethering.java
public int
setUsbTethering(boolean
enable) {
UsbManager usbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
………………
else
{
mUsbTetherRequested = true;
usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS,
false);
}
通过service,来获取manager,进行操作。这个tethering.java的构造函数
public
Tethering(Context context,
INetworkManagementService nmService,
INetworkStatsService statsService,
IConnectivityManager connService, Looper looper)
{
Context是获取全局信息的接口,
2.5 UsbManager.java
/frameworks/base/core/java/android/hardware/usb/UsbManager.java
public void setCurrentFunction(String
function, boolean makeDefault) {
try {
mService.setCurrentFunction(function,
makeDefault);
}
catch (RemoteException e) {
Log.e(TAG, "RemoteException in
setCurrentFunction", e);
}
}
2.6
frameworks/base/services/java/com/android/server/usb$
mengfd1@tablet-C:~/work/A2107/frameworks/base/services/java/com/android/server$
cd usb
mengfd1@tablet-C:~/work/A2107/frameworks/base/services/java/com/android/server/usb$
ls
UsbDeviceManager.java UsbHostManager.java
UsbService.java UsbSettingsManager.java
/frameworks/base/services/java/com/android/server/usb/UsbService.java
public void setCurrentFunction(String function, boolean
makeDefault) {
mContext.(android.Manifest.permission.MANAGE_USB,
null);
if
(mDeviceManager != null) {
mDeviceManager.setCurrentFunction(function,
makeDefault);
}
else {
throw new IllegalStateException("USB device
mode not supported");
}
}
/frameworks/base/services/java/com/android/server/usb/UsbDeviceManager.java
public void setCurrentFunction(String
function, boolean makeDefault) {
if
(DEBUG) Slog.d(TAG, "setCurrentFunction(" + function + ") default:
" + makeDefault);
mHandler.sendMessage(MSG_SET_CURRENT_FUNCTION,
function, makeDefault);
}
public void handleMessage(Message msg)
{
case
MSG_SET_CURRENT_FUNCTION:
String function =
(String)msg.obj;
boolean makeDefault = (msg.arg1 ==
1);
if (function != null
&&
function.equals(UsbManager.USB_FUNCTION_CHARGING_ONLY))
{
mSettingUsbCharging = true;
SXlog.d(TAG, "handleMessage -
MSG_SET_CURRENT_FUNCTION - USB_FUNCTION_CHARGING_ONLY -
makeDefault: " + makeDefault);
} else {
mSettingUsbCharging = false;
}
setEnabledFunctions(function,
makeDefault);
SXlog.d(TAG, "handleMessage -
MSG_SET_CURRENT_FUNCTION - function: " +
function);
break;
private void setEnabledFunctions(String
functions, boolean makeDefault) {
private boolean setUsbConfig(String config) {
SystemProperties.set("sys.usb.config",
config);
C. 如何创建android系统服务
1、 撰写一个aidl文件,定义服务的接口,将在编译过程中通过aidl工具生成对应的java接口。一般系统服务的aidl文件都放在framework\base\core\java\android\os目录中。
以我所写的IMyTool.aidl为例。在.aidl中定义自己需要加入的方法,编写规则和java接口差不多,这里不多说。
2、 将aidl文件名添加到frameworks\base\目录下的Android.mk编译脚本文件中。
如:
LOCAL_SRC_FILES += \
core/java/android/accessibilityservice/.aidl\
…\
core/java/android/os/IMyTool.aidl\
…
IMyTool.aidl即我加进去的aidl文件,加入后才能在make过程中编译到,否则将在后面的SystemServer添加系统服务时会报错提示找不到对应类。
3、 编写真正工作的服务类,继承IMyTool.Stub类(AIDL文件名.Stub,aidl生成的接口中的内部类,是一个Binder)。
服务类一般都放在framework\base\services\java\com\android\server目录中。
例如:
public class MyToolService extends IMyTool.Stub {
实现IMyTool.aidl中定义的接口。
}
4、 将自定义服务注册到SystemServer,使得开机过程中被添加。
在framework\base\services\java\com\android\server目录中的SystemServer中启动服务代码处加入:
try {
Slog.i(TAG, "MyToolService");
ServiceManager.addService(Context.MY_TOOL_SERVICE,new MyToolService(context));// MyToolService构造函数自己定义,一般都会用到Context
} catch(Throwable e) {
Slog.e(TAG, "Failure startingMyToolService", e);
}
上面代码中Context.MY_TOOL_SERVICE是自己在Context类中定义的常量,也就是给服务定义的名字,使用常量方便获取服务,而不需要记住注册服务时用的名字,且想换名字时只需改一个常量的值。