安卓測試相關

CPU

CPU有大小之分、系統會自動調配。javascript

查看CPU:php

$ adb shell top -n 1 -m 10 -d 1
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
User 6%, System 23%, IOW 0%, IRQ 0%
User 8 + Nice 0 + Sys 29 + Idle 88 + IOW 0 + IRQ 0 + SIRQ 0 = 125
  PID PR CPU% S  #THR     VSS     RSS PCY UID      Name
 7543  0  16% R     1   1344K    568K     shell    top
  212  2   9% S     8  12816K   3032K     root     /system/bin/netd
 1043  0   4% S    95 663500K  81248K  fg system   system_server
 6484  0   2% S    20 532780K  46148K  bg u0_a82   com.coomix.app.bus
 1832  0   0% S     6   7304K    640K     root     /system/bin/mpdecision
26896  0   0% S     1      0K      0K     root     kworker/0:0H
  206  0   0% S     1   1072K    388K     system   /system/bin/servicemanager
  227  0   0% S     1    944K    408K     system   /system/bin/qrngd
    3  0   0% S     1      0K      0K     root     ksoftirqd/0
   30  1   0% S     1      0K      0K     root     smsm_cb_wq

-m 10 表示查看使用CPU最多的是個進程。css

User 8 + Nice 0 + Sys 29 + Idle 88 + IOW 0 + IRQ 0 + SIRQ 0 = 125

上面表示每種狀態消耗的時間(10s內,單位ms),最大值是100×CPU個數。html

$ adb shell dumpsys cpuinfo
Load: 17.55 / 18.49 / 20.27
CPU usage from 10958ms to 955ms ago with 99% awake:
  19% 1043/system_server: 17% user + 1.1% kernel / faults: 453 minor
  16% 212/netd: 2% user + 14% kernel / faults: 2354 minor
  8.5% 2885/dnsmasq: 0.9% user + 7.6% kernel
  5.1% 10968/com.coomix.app.bus: 2.7% user + 2.4% kernel / faults: 135 minor
  1.4% 227/qrngd: 0% user + 1.4% kernel
  0.8% 3/ksoftirqd/0: 0% user + 0.8% kernel
  0.6% 11016/com.coomix.app.bus:remote: 0.4% user + 0.2% kernel / faults: 34 minor
  0.5% 131/w1_bus_master1: 0% user + 0.5% kernel
  0.5% 1832/mpdecision: 0% user + 0.5% kernel
  0.4% 1804/mcd: 0.3% user + 0.1% kernel
  0.3% 1326/com.android.phone: 0.2% user + 0.1% kernel
  0% 7/kworker/u:0H: 0% user + 0% kernel
  0.1% 151/mmcqd/0: 0% user + 0.1% kernel
  0% 205/healthd: 0% user + 0% kernel
  0% 207/vold: 0% user + 0% kernel
  0% 226/thermal-engine: 0% user + 0% kernel
  0.1% 281/sdcard: 0% user + 0.1% kernel
  0% 387/xtwifi-client: 0% user + 0% kernel
  0% 1113/com.android.systemui: 0% user + 0% kernel / faults: 1 minor
  0% 1375/com.miui.whetstone: 0% user + 0% kernel
  0% 1533/com.miui.powerkeeper:service: 0% user + 0% kernel / faults: 10 minor
  0% 1983/com.lbe.security:service: 0% user + 0% kernel / faults: 1 minor
  0.1% 2061/com.lbe.security.miui: 0% user + 0.1% kernel
  0% 2566/com.miui.securitycenter: 0% user + 0% kernel
  0% 5518/kworker/0:0: 0% user + 0% kernel
  0% 8118/kworker/0:1: 0% user + 0% kernel
  0% 21358/kworker/0:4: 0% user + 0% kernel
  0% 26896/kworker/0:0H: 0% user + 0% kernel
  0.1% 28973/kworker/u:3: 0% user + 0.1% kernel
50% TOTAL: 23% user + 24% kernel + 1.5% softirq

 

第一行爲過去的1,5和15分鐘內的平均CPU負載。而後時近7秒全部應用CPU使用狀況。java

 

 

RUM

真實用戶測量(RUM Real User Measurement): 收集應用的運行時數據,彙總的結果,輸出報告和尋找數據中可能出現的問題。android

有些公司本身開發RUM。若是不能本身開發,市面也有一些免費或者收費的工具。git

集成SDK:github

咱們這裏安裝了:web

Crashlytic:成立於2011年,是專門爲移動應用開者發提供的保存和分析應用崩潰信息的工具。算法

Crittercism:提供了世界上首個領先的移動應用性能管理(mAPM)解決方案。其SDK被嵌入了成千上萬的應用中,在全世界有近十億用戶。該公司致力於收集性能數據,例如錯誤報告、崩潰診斷細節、麪包屑(breadcrumbs,指導航記錄)、設備/載體/OS統計和用戶行爲等。這些數據大部分是非結構化的,而且隨着應用程序、版本、設備和使用模式的不一樣變化很大。

Google分析(Google Analytics): 由Google所提供的網站流量統計服務。

NewRelic:  參考資料:https://en.wikipedia.org/wiki/New_Relic。

另外國內的聽雲作得也不錯,雖然技術比不上國外同行,可是網絡相對比較快捷點。對數據安全等要求比較高的,建議不要使用聽雲。

好比顯示圖片的方法:

imageViews=new ImageView[100];
public int Imagelooper(int numberofaddedimages, int totalImageCount, RelativeLayout rl){
    for(int i=0;i<numberofaddedimages;i++)
    {
        totalImageCount = totalImageCount++;
        //for analytics I want to track crashes.. so lets force it to crash
        // if(totalImageCount ==100){
        // totalImageCount=0;
        // }
        //if totalImageCount reaches 100 the app crashes since I have exceeded the array size
        imageViews[totalImageCount]=new ImageView(this);

若是數據越界達到100,日誌報錯以下:

03-13 14:01:32.351 13772-13837/com.sillars.imagescroll I/image downloaded? number: 99
03-13 14:01:32.469 13772-13837/com.sillars.imagescroll I/ImageDownloader?
image99responsetime (2RTT): 38
03-13 14:01:34.637 13772-13772/com.sillars.imagescroll E/AndroidRuntime?
FATAL EXCEPTION: main
Process: com.sillars.imagescroll, PID: 13772
java.lang.ArrayIndexOutOfBoundsException: length=100; index=100
at com.sillars.imagescroll.MyActivity.Imagelooper(MyActivity.java:327)
at com.sillars.imagescroll.MyActivity$3.onScrollStopped(MyActivity.java:178)
at com.sillars.imagescroll.MyScrollView$1.run(MyScrollView.java:37)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run
(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
03-13 14:01:35.329 13772-13797/com.sillars.imagescroll I/Fabric?
Crashlytics report upload complete: 550346640083-0001-35CC-27DD9B9DA026.cls
03-13 14:01:54.291 13772-15861/com.sillars.imagescroll I/com.newrelic.agent.android?
Harvester: connected
03-13 14:01:54.291 13772-15861/com.sillars.imagescroll I/com.newrelic.agent.android?
Harvester: Sending 102 HTTP transactions.
03-13 14:01:54.291 13772-15861/com.sillars.imagescroll I/com.newrelic.agent.android?
Harvester: Sending 1 HTTP errors.
03-13 14:01:54.292 13772-15861/com.sillars.imagescroll I/com.newrelic.agent.android?
Harvester: Sending 0 activity traces.
03-13 14:02:05.070 13772-13772/com.sillars.imagescroll I/Process?
Sending signal. PID: 13772 SIG: 9

Crashalytics的報告以下:

詳情參見:http://crashes.to/s/5ba7d984fe7。

New Relic還能夠針對具體的HTTP代碼進行分析,以下:

Google Analytics能夠分析用戶行爲:

 

咱們進行分頁,也能夠從用戶的退出看出問題:

/initialize tracker at top of the screen
t = analytics.newTracker(R.xml.app_tracker);
// Enable Advertising Features.
t.enableAdvertisingIdCollection(true);
t.enableExceptionReporting(true);
t.setScreenName("top of scroll");
t.send(new HitBuilders.ScreenViewBuilder().build());
<snip>
//10 more images were just requested, so update the screen name in Google Analytics
t.setScreenName(totalImageCount + " images");
t.send(new HitBuilders.ScreenViewBuilder().build());
//and add a crittercism Breadcrumb
Crittercism.leaveBreadcrumb(totalImageCount + " images")

 

增長網絡分析:

t.send(new HitBuilders.EventBuilder()
    .setCategory("RTT Event")
    .setValue(AvgRTT.longValue())
    .setAction("ImageRTT").setLabel(networkConnection).build());
Crittercism.beginTransaction(networkConnection);
Crittercism.setTransactionValue(networkConnection, AvgRTT.intValue());
Crittercism.endTransaction(networkConnection);

 

 

最後,要注意sdk自己的性能。

網絡性能

 

無線鏈接時,功耗從4.5mA增大125 mA。 Wi-Fi 240mA。可是無線鏈接的服務質量保證開銷大,實際耗電更多。Nexus 6的Wi-Fi開啓鏈接3mA,發送數據240 mA。

注意通話時對流量會有影響。

Wireshark是強大的抓包工具,在windows下配合Connectify使用,能夠放出wifi來抓包。

 

Fiddler是一個HTTP代理,能夠查看HTTPS流量,可是不能解開HTTPS的文件和內容。

MITMProxy和Fiddler相似, 用法參見:https://mitmproxy.org/doc/mitmproxy.html

 

應用程序資源優化(ARO Application Resource Optimizer)是監控Android和iOS的應用網絡性能的工具。這是來自AT&T的免費/開源工具,它包含許多和Wireshark的和Fiddler相同的數據包捕獲功能。且能夠收集蜂窩網絡數據。

對於Android版本,ARO Data Collector APK在設備上執行tcpdump,須要root權限。若是沒有root,也能夠運行,可是不能查看進程對應的鏈接 。

ARO會截取對應的屏幕。缺點,不能解析HTTPS。

對於包含網頁的應用,http://www.webpagetest.org/。

 

安卓網絡優化

網絡優化一般是儘量快地下載,而後關閉無線以節省電量。


•減小HTTP請求
•使用CDN(內容分發網絡 Content Delivery Network)

•添加一個Expire頭
•Gzip組件
•將樣式表放在頂部
•腳本放在底部
•避免CSS表達式
•將JavaScript和CSS放在外部文件
•減小DNS查找
•減小JavaScript
•避免重定向
•刪除重複的腳本
•配置的ETag
•緩存Ajax

參考資料:http://www.ibm.com/developerworks/cn/web/1308_caiys_jsload/

 

Gzip是最多見的算法。Zopfli算法比Gzip壓縮強5%,可是壓縮和解壓更耗時,適用於不是常常變的內容。開啓壓縮,通常在 .htaccess文件:

 

<ifModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file .(html?|txt|css|js|php|pl)$
mod_gzip_item_include handler ^cgi-script$

mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude mime ^image/.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
</ifModule>

 

文本文件最小化,好比下面代碼:

<html>
<title> A Sample Page</title>
<body>
with some sample text
<--do more here-->

</body>
</html>

修改成:

<html><title> A Sample Page</title><body>with some sample text</body></html>

一般能夠減小20-50%。

 

圖片的質量和大小之間要有個合適點。要存儲不一樣分辨率的圖片。要獲取圖片的MetaData,google的圖片一般壓縮了85%,要根據不一樣的場景使用不一樣的壓縮。google的WebP格式也能夠考慮。

緩存:客戶端開啓緩存;服務器端設置緩存時間。

Android 4.0及以上版本開啓緩存的方法:

private void enableHttpResponseCache() {
    try {
    long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
    File httpCacheDir = new File(getCacheDir(), "http");
    Class.forName("android.net.http.HttpResponseCache")
        .getMethod("install", File.class, long.class)
        .invoke(null, httpCacheDir, httpCacheSize);
    } catch (Exception httpResponseCacheNotAvailable) {
        Log.d(TAG, "HTTP response cache is unavailable.");
    }
}

設備上每一個文件的緩存時間由服務器設置在頭部。在緩存時間裏,文件直接從cache獲取。緩存時間事後,會從新請求並設置緩存時間,若是文件沒有修改,則會返回304。控制的方法以下:

緩存控制(添加Expire頭):是最經常使用的方法。能夠設置Private/Public,多用於CDN;no-store表示不緩存;no-cache:使用以前先驗證,實際是有緩存的; max age=X表示緩存時間,單位是秒,一般爲 0 (等同於no-cache); 60, 300, 600, 3600 (1 hour),86400 (1 day), 3153600 (1 year)。
 

ETag或實體標籤(entity tag)是萬維網協議HTTP的一部分。ETag是HTTP協議提供的若干機制中的一種Web緩存驗證機制,而且容許客戶端進行緩存協商。這就使得緩存變得更加高效,並且節省帶寬。若是資源的內容沒有發生改變,Web服務器就不須要發送一個完整的響應。ETag也可用於樂觀併發控制,做爲一種防止資源同步更新而相互覆蓋的方法。


ETag是隨機標識符,由Web服務器根據URL上的資源的特定版本而指定,相似於指紋,若是沒有修改則返回304了,適用於頻繁修改的文件。好比:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=86400
Content-Type: image/jpeg
Date: Tue, 28 Jan 2014 00:14:55 GMT
Etag: "b17ad00-1f17-46723595372c0"
Expires: Wed, 29 Jan 2014 00:14:55 GMT
Last-Modified: Thu, 09 Apr 2009 18:23:47 GMT
Server: Apache/2.2.3 (CentOS)
X-Cache: HIT
Content-Length: 7959

Expire不如ETag和緩存經常使用。失效時間單位是天。

儘量組合鏈接數。要儘可能使用已有鏈接,蜂窩和無線的使用查詢:

public static String getNetworkClass(Context context) {
    ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo info = cm.getActiveNetworkInfo();
    if(info==null || !info.isConnected())
        return "-"; //not connected
    if(info.getType() == ConnectivityManager.TYPE_WIFI)
        return 「wifi";
    if(info.getType() == ConnectivityManager.TYPE_MOBILE){
        return 「cellular";
        }
    }
    return 「unknown";
}

一些動做須要再網絡開啓的時候再操做,好比:

if (Tel.getDataActivity() >0){
    if (Tel.getDataActivity() <4){
        //1, 2, 3 response means that the cellular radio is transmitting!
        //download the image here using image getter
        imagegetter(counter, numberofimages);
        //and show the ad
        AdRequest adRequest = new AdRequest();
        adRequest.addTestDevice(AdRequest.TEST_EMULATOR);
        adView.loadAd(adRequest);
        // Initiate a generic request to load it with an ad
        adView.loadAd(new AdRequest());
    }
}

超過15s不使用的鏈接,儘可能要關閉。

HttpURLConnection connectionCloseProperly = (HttpURLConnection) ulrn.openConnection();
connectionCloseProperly.setRequestProperty("connection", "close"); //this disables "keep-alive"
connectionCloseProperly.setUseCaches(true);
connectionCloseProperly.connect();
Object response = connectionCloseProperly.getContent();
InputStream isclose = connectionCloseProperly.getInputStream();
...download and render bitmap image
connectionCloseProperly.disconnect();

 

內容分發網絡(Content delivery network或Content distribution network,縮寫:CDN)是指一種經過互聯網互相鏈接的電腦網絡系統,提供高性能、可擴展性、及低成本的網絡將內容傳遞給用戶。

 

內容分發網絡的總承載量能夠比單一骨幹最大的帶寬還要大。這使得內容分發網絡能夠承載的用戶數量比起傳統單一服務器多。也就是說,若把有100Gbps處理能力的服務器放在只有10Gbps帶寬的數據中心,則亦只能發揮出10Gbps的承載量。但若是放到十個有10Gbps的地點,整個系統的承載量就能夠到10*10Gbps。
同時,將服務器放到不一樣地點,能夠減小互連的流量,進而下降帶寬成本。
對於TCP傳輸而言,TCP的速度(throughput)會受到延遲時間(latency)與數據包漏失率(packet loss)影響。爲了改善這些負面因素,內容分發網絡一般會指派較近、較順暢的服務器節點將數據傳輸給用戶。雖然距離並非絕對因素,但這麼作能夠儘量提升性能,用戶將會以爲比較順暢。這使得一些比較高帶寬的應用(傳輸高清畫質的視頻)更容易推進。
內容分發網絡另一個好處在於有異地備援。當某個服務器故障時,系統將會調用其餘鄰近地區的服務器服務,進而提供接近100%的可靠度。
除此以外,內容分發網絡提供給服務提供者更多的控制權。提供服務的人能夠針對客戶、地區,或是其餘因子調整。

 

路由控制可使用OpenWrt Linux。Faraday能夠用於隔離無線信號。AT&T Network Attenuator 能夠模擬各類無線信號:

根據網絡類型區分速度:

TelephonyManager teleMan = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
int networkType = teleMan.getNetworkType();
switch (networkType)
{case 1: netType = "GPRS";
    networkSpeed = "slow";
    break;
case 2: netType = "EDGE";
    networkSpeed = "slow";
    break;
case 3: netType = "UMTS";
    networkSpeed = "medium";
    break;
// we'll leave out a few network types, but you get the idea.
//You can see the full code on Github
case 13: netType = "LTE";
    networkSpeed = "fast";
    break;}

 

固然上面代碼應該考慮信號強度。而後能夠根據不一樣網絡選擇不一樣的圖片:

switch(networkSpeed){
    case "fast":
        new ImageDownloader().execute(urlbig); //image is 143KB
        break;
    case "medium":
        new ImageDownloader().execute(urlmed); //image is 41KB
        break;
    case "slow":
        new ImageDownloader().execute(urlsmall); //image is 27KB
        break;
}

記錄下時間:1,服務器返回200的時間;2,下載圖片的時間。計算出時延和吞吐量。

private Bitmap downloadBitmap(String url) {
    Long start = System.currentTimeMillis(); //download start time
    final DefaultHttpClient client = new DefaultHttpClient();
    final HttpGet getRequest = new HttpGet(url);
        try {HttpResponse response = client.execute(getRequest);
        //check 200 OK for success
            final int statusCode = response.getStatusLine().getStatusCode();
            Long gotresponse = System.currentTimeMillis(); //time 200 response received
        }
        final HttpEntity entity = response.getEntity();
        contentlength = entity.getContentLength();//get ContentLength of file
        if (entity != null) {
            InputStream inputStream = null;
            try {
                inputStream = entity.getContent();
                final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                Long gotimage = System.currentTimeMillis();
                //time image download completed
                responsetime = gotresponse - start;
                //time to the 200 ok response
                imagetime = gotimage-start;
                //download time
                throughput = ((double)contentlength/1024)/((double)imagetime/1000); //KB/s
                return bitmap;
}

高延時的環境,能夠先多下載一些圖片。

If (latency = normal){
    if (ImagesBelowtheFold<2){
        <get next batch of images>
    }
}
Else {
    //latency is high
    if (ImagesBelowtheFold<4){
        <get next batch of images>
    //consider getting more images too
    //also, smaller images?
}

Android 4.4.4之後能夠開啓Bluetooth HCI snoop log。

 

耗電因素

 

 

強行切換Android M的狀態:

adb shell dumpsys battery unplug //tricks the device to stop charging
adb shell dumpsys deviceidle step //reusing this step walks you through the various states

 

基本電池分析

Settings → Battery中能夠看到應用的電池使用狀況。點擊具體應用能夠查看應用的消耗。點擊上部能夠查看歷史電池消耗狀況。能夠比較流量和電量的消耗,計算出流量的速率,若是有大量的低速流量,說明設計很差。

Android M中增長了App Standby功能,阻止不經常使用的應用在非充電時鏈接網絡或者發起進程。經過adb shell dumpsys usagestats能夠查看使用頻率。

 

高級電池監控

安卓4.4之後的adb中增長了batterystats功能。

adb shell dumpsys batterystats --reset
adb shell dumpsys batterystats --enable full-wake-history (5.0才支持)
adb shell dumpsys batterystats --charged (最近充滿以後的電池消耗)

下面部分概述各階段消耗狀況:

Discharge step durations:

#0: +2m28s313ms to 88 (screen-on, power-save-off)
#1: +2m38s364ms to 89 (screen-on, power-save-off)
#2: +2m27s323ms to 90 (screen-on, power-save-off)
#3: +2m8s449ms to 91 (screen-on, power-save-off)
#4: +2m17s115ms to 92 (screen-on, power-save-off)
#5: +2m7s924ms to 93 (screen-on, power-save-off)
#6: +2m17s693ms to 94 (screen-on, power-save-off)
#7: +2m6s425ms to 95 (screen-on, power-save-off)
#8: +1m50s298ms to 96 (screen-on, power-save-off)
#9: +3m0s436ms to 97 (screen-on, power-save-off)

下面部分對電源消耗作了統計:

Statistics since last charge:
System starts: 0, currently on battery: false
Time on battery: 30m 36s 621ms (99.3%) realtime, 27m 58s 456ms (90.8%) uptime
Time on battery screen off: 3m 31s 100ms (11.4%) realtime, 52s 935ms (2.9%) uptime
Total run time: 30m 48s 839ms realtime, 28m 10s 674ms uptime
Start clock time: 2014-10-17-22-54-33
Screen on: 27m 5s 521ms (88.5%) 1x, Interactive: 27m 5s 837ms (88.5%)
Screen brightnesses:
dark 27m 5s 521ms (100.0%)
Total full wakelock time: 29m 16s 938ms
Total partial wakelock time: 17s 153ms
Mobile total received: 187.99KB, sent: 201.15KB (packets received 750, sent 742)
Phone signal levels:
none 35s 29ms (1.9%) 10x
poor 11m 7s 494ms (36.3%) 96x
moderate 18m 29s 647ms (60.4%) 94x
good 24s 451ms (1.3%) 7x
Signal scanning time: 0ms

 

上次充滿電後的統計:

Device battery use since last full charge
Amount discharged (lower bound): 10
Amount discharged (upper bound): 11

 

電池使用預計

Estimated power use (mAh):
Capacity: 3220, Computed drain: 359, actual drain: 322-354
Uid u0a117: 106
Screen: 96.6
Uid 1000: 26.1
Uid 0: 24.9
Cell standby: 22.9

 

電池歷史統計:

adb bugreport > bugreport.txt //download the output to your computer

./historian.py bugreport.txt > out.html //create the html file

主要建議用安卓5以上版本。分析工具下載地址:https://github.com/google/battery-historian。

 

 

JobScheduler在安卓5.0增長,更好地調度任務。

 

 

google相關網址

http://developer.android.com/tools/testing/index.html

http://developer.android.com/tools/testing/testing_android.html

http://developer.android.com/tools/testing/testing-tools.html

Top 5 Android Testing Frameworks (with Examples) http://testdroid.com/tech/top-5-android-testing-frameworks-with-examples

 

googlesamples/android-testing https://github.com/googlesamples/android-testing

 

Developing Android unit and instrumentation tests - Tutorial http://www.vogella.com/tutorials/AndroidTesting/article.html

 

Introduction to Android development with Android Studio - Tutorial http://www.vogella.com/tutorials/Android/article.html

 

Building Android applications with Gradle - Tutorial http://www.vogella.com/tutorials/AndroidBuild/article.html

 

How Do Top Android Developers QA Test Their Apps? http://techcrunch.com/2012/06/02/android-qa-testing-quality-assurance/

 

Android Testing Tutorial: Unit Testing like a True Green Droid http://www.toptal.com/android/testing-like-a-true-green-droid

 

https://www.youtube.com/watch?v=L-k6dpfXqBY

Android Testing Options https://github.com/codepath/android_guides/wiki/Android-Testing-Options  

相關文章
相關標籤/搜索