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類中定義的常量,也就是給服務定義的名字,使用常量方便獲取服務,而不需要記住注冊服務時用的名字,且想換名字時只需改一個常量的值。