前言:
最近開始研究Android自動化測試方法,對其中的一些工具、方法和框架做了一些簡單的整理,其中包括android測試框架、CTS、Monkey、Monkeyrunner、benchmark、其它test tool等等。因接觸時間很短,很多地方有不足之處,希望能和大家多多交流。
一、 什么是Monkey
Monkey是Android中的一個命令行工具,可以運行在模擬器里或實際設備中。它向系統發送偽隨機的用戶事件流(如按鍵輸入、觸摸屏輸入、手勢輸入等),實現對正在開發的應用程序進行壓力測試。Monkey測試是一種為了測試軟件的穩定性、健壯性的快速有效的方法。
二、 Monkey的特征
1、 測試的對象僅為應用程序包,有一定的局限性。
2、 Monky測試使用的事件流數據流是隨機的,不能進行自定義。
3、 可對MonkeyTest的對象,事件數量,類型,頻率等進行設置。
三、Monkey的基本用法
基本語法如下:
$ adb shell monkey [options]
如果不指定options,Monkey將以無反饋模式啟動,並把事件任意發送到安裝在目標環境中的全部包。下面是一個更為典型的命令行示例,它啟動指定的應用程序,並向其發送500個偽隨機事件:
$ adb shell monkey -p your.package.name -v 500
四、Monkey測試的一個實例
通過這個實例,我們能理解Monkey測試的步驟以及如何知道哪些應用程序能夠用Monkey進行測試。
Windows下(注:2—4步是為了查看我們可以測試哪些應用程序包,可省略):
1、 通過eclipse啟動一個Android的emulator
2、 在命令行中輸入:adb devices查看設備連接情況
C:\Documents and Settings\Administrator>adb devices
List of devices attached
emulator-5554 device
3、 在有設備連接的前提下,在命令行中輸入:adb shell 進入shell界面
C:\Documents and Settings\Administrator>adb shell
#
4、 查看data/data文件夾下的應用程序包。注:我們能測試的應用程序包都在這個目錄下面
C:\Documents and Settings\Administrator>adb shell
# ls data/data
ls data/data
com.google.android.btrouter
com.android.providers.telephony
com.android.mms
com.android.providers.downloads
com.android.deskclock
com.android.email
com.android.providers.media
com.android.settings
jp.co.omronsoft.openwnn
com.android.providers.userdictionary
com.android.quicksearchbox
com.android.protips
com.android.browser
com.android.launcher
com.android.term
com.android.speechrecorder
com.android.server.vpn
com.android.defcontainer
com.svox.pico
com.android.customlocale
com.android.development
com.android.soundrecorder
com.android.providers.drm
com.android.spare_parts
com.android.providers.downloads.ui
com.android.fallback
com.android.providers.applications
com.android.netspeed
com.android.wallpaper.livepicker
android.tts
com.android.htmlviewer
com.android.music
com.android.certinstaller
com.android.inputmethod.pinyin
com.android.providers.subscribedfeeds
com.android.inputmethod.latin
com.android.gallery
com.android.systemui
com.android.contacts
com.android.phone
com.android.sdksetup
com.android.calculator2
com.android.packageinstaller
com.android.camera
com.android.providers.settings
com.thestore.main
com.android.providers.contacts
5、 以com.android.calculator2作為對象進行MonkeyTest
#monkey -p com.android.calculator2 -v 500
其中-p表示對象包 –v 表示反饋信息級別
運行過程中,Emulator中的應用程序在不斷地切換畫面。
按照選定的不同級別的反饋信息,在Monkey中還可以看到其執行過程報告和生成的事件。
注:具體參數的設定可參考:http://developer.android.com/guide/developing/tools/monkey.html
五、關於Monkey測試的停止條件
Monkey Test執行過程中在下列三種情況下會自動停止:
1、如果限定了Monkey運行在一個或幾個特定的包上,那么它會監測試圖轉到其它包的操作,並對其進行阻止。
2、如果應用程序崩潰或接收到任何失控異常,Monkey將停止並報錯。
3、如果應用程序產生了應用程序不響應(application not responding)的錯誤,Monkey將會停止並報錯。
通過多次並且不同設定下的Monkey測試才算它是一個穩定性足夠的程序。
monkeyrunner工具
一、什么是monkeyrunner
monkeyrunner工具提供了一個API,使用此API寫出的程序可以在Android代碼之外控制Android設備和模擬器。通過monkeyrunner,您可以寫出一個Python程序去安裝一個Android應用程序或測試包,運行它,向它發送模擬擊鍵,截取它的用戶界面圖片,並將截圖存儲於工作站上。monkeyrunner工具的主要設計目的是用於測試功能/框架水平上的應用程序和設備,或用於運行單元測試套件,但您當然也可以將其用於其它目的。
二、monkeyrunner工具同Monkey工具的差別
Monkey:
Monkey工具直接運行在設備或模擬器的adb shell中,生成用戶或系統的偽隨機事件流。
monkeyrunner:
monkeyrunner工具則是在工作站上通過API定義的特定命令和事件控制設備或模擬器。
三、monkeyrunner的測試類型
1、多設備控制:monkeyrunner API可以跨多個設備或模擬器實施測試套件。您可以在同一時間接上所有的設備或一次啟動全部模擬器(或統統一起),依據程序依次連接到每一個,然后運行一個或多個測試。您也可以用程序啟動一個配置好的模擬器,運行一個或多個測試,然后關閉模擬器。
2、 功能測試: monkeyrunner可以為一個應用自動貫徹一次功能測試。您提供按鍵或觸摸事件的輸入數值,然后觀察輸出結果的截屏。
3、 回歸測試:monkeyrunner可以運行某個應用,並將其結果截屏與既定已知正確的結果截屏相比較,以此測試應用的穩定性。
4、 可擴展的自動化:由於monkeyrunner是一個API工具包,您可以基於Python模塊和程序開發一整套系統,以此來控制Android設備。除了使用monkeyrunner API之外,您還可以使用標准的Python os和subprocess模塊來調用Android Debug Bridge這樣的Android工具。
四、運行monkeyrunner
您可以直接使用一個代碼文件運行monkeyrunner,抑或在交互式對話中輸入monkeyrunner語句。不論使用哪種方式,您都需要調用SDK目錄的tools子目錄下的monkeyrunner命令。如果您提供一個文件名作為運行參數,則monkeyrunner將視文件內容為Python程序,並加以運行;否則,它將提供一個交互對話環境。
monkeyrunner的命令語法為:
monkeyrunner -plugin <plugin_jar> <program_filename> <program_options>
五、實例
以sample中的ApiDemos為例,先將其生成ApiDemos.apk。
前提:已有device連接
1、 將ApiDemos.apk放在$Android_Root\tools下。
2、 在$Android_Root\tools下新建一個monkeyrunnerprogram.py文件,里面內容為:
2
3 from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage
4
5 # Connects to the current device, returning a MonkeyDevice object
6
7 device = MonkeyRunner.waitForConnection()
8
9 # Installs the Android package. Notice that this method returns a boolean, so you can test
10
11 # to see if the installation worked.
12
13 device.installPackage('./ApiDemos.apk')
14
15
16 # Runs the component
17
18 device.startActivity(component='com.example.android.apis/.ApiDemos')
19
20
21 # Presses the Menu button
22
23 device.press('KEYCODE_MENU','DOWN_AND_UP')
24
25
26 # Takes a screenshot
27
28 result = device.takeSnapshot()
29
30
31 # Writes the screenshot to a file
32
33 result.writeToFile('./shot1.png','png')
注意:SDK上的例子有些錯誤,不可直接復制,否則執行命令時會發生錯誤。具體可與我的上面這段代碼對照。
3、 打開命令行轉到Android_Root\tools目錄下運行一下命令:
monkeyrunner monkeyrunnerprogram.py
110307 15:33:19.625:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: wake.
110307 15:33:20.625:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: wake.
110307 15:33:21.625:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: wake.
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] Error starting command: monkey --port 12345
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]com.android.ddmlib.ShellCommandUnresponsiveException
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at com.android.ddmlib.AdbHelper.executeRemoteCommand(AdbHelper.java:408)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at com.android.ddmlib.Device.executeShellCommand(Device.java:276)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at com.android.monkeyrunner.adb.AdbMonkeyDevice$1.run(AdbMonkeyDevice.java:89)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.util.concurrent.Executors$RunnableAdapter.call(UnknownSource)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.util.concurrent.FutureTask.run(Unknown Source)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.util.concurrent.ThreadPoolExecutor$Worker.run(UnknownSource)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.lang.Thread.run(UnknownSource)
110307 15:33:57.437:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: press KEYCODE_MENU.
110307 15:33:59.171:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: quit.
注:里面exception的提示我們可以忽略,因為我們可以看見 Monkey Command: press KEYCODE_MENU已經執行成功。
4、 可以Android_Root\tools下查看生成的shot1.png的截圖。
六、實例擴展
因為ApiDemos首頁上按下MENU鍵沒有菜單出現,為了更加形象化,在實例五的基礎上繼續試驗:
1、 在$Android_Root\tools下新建一個monkeyrunnerprogram1.py文件,里面內容為:
2
3 from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage
4
5 # Connects to the current device, returning a MonkeyDevice object
6
7 device = MonkeyRunner.waitForConnection()
8
9 # Takes a screenshot
10
11 result = device.takeSnapshot()
12
13 # Writes the screenshot to a file
14
15 result.writeToFile('./shotbegin.png','png')
16
17 # Presses the Down button
18
19 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
20
21 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
22
23 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
24
25 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
26
27 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
28
29 # Takes a screenshot
30
31 result = device.takeSnapshot()
32
33 # Writes the screenshot to a file
34
35 result.writeToFile('./shotend.png','png')
2、 將畫面定位在Apidemos的首頁,並將光標定位在第一項上。
3、 在$Android_Root\tools目錄下運行一下命令:
monkeyrunner monkeyrunnerprogram1.py
4、在運行過程中我們可以看見光標不斷向下移動,並且可以在當前目錄下我們自定義的截圖:
運行前:shotbegin.png
運行后(做了五次下移操作):shotend.png
Monkey 的專項測試淺談
前言
其實去年年底我就說過很多公司功能測試都做的差不多了,接下來就開始折騰什么性能測試啊,安全測試啊,持續集成啊,Hybrid啦等等。果不其然,最近很多測試同學開始問我性能相關的問題。當然我們專業點來講這個叫做專項測試,那么專項測試其實也是區分什么人去做,工具組的人也在做,業務組的人也在做,只不過大家做的切入點會很不同。也許很多同學也比較好奇我畢竟也去那么多公司撕逼了,到底我平時在做什么,怎么做的。這邊那就簡單說下吧。
做了什么
嗯,我想想我做了什么。其實我現在就是,公司做到移動無線的應用專項測試就會想到我。然后之前一年我一個人做了持續集成,BDD,功能回歸自動化,接口測試,靜動態掃描,從客戶端發起的接口測試等等。其實到現在為止我還是覺得我不知道我怎么走上專項測試這條路的,貌似某一天公司說我來做就變成我來做了。
怎么做
其實別的先不說吧,我們先來說下專項吧。專項這個東西關鍵在於幾點
怎么手動獲取數據
怎么自動化獲取數據
怎么分析數據
怎么定位問題
怎么優化
當然我這里還是要吐槽一句,大家醒醒吧。花個幾年去研究UI 功能的自動化有意義嗎?要學的東西太多了,還是醒醒吧。其實很多同學關鍵是上面我說的這些都不知道,而且不僅如此,還不知道的有
我什么階段去做
每個階段做什么
做到什么顆粒度
怎么才算完成了
。。。
也許還有很多,我也不想列了。其實我想說的是,其實也沒有什么絕對,還是看你的團隊,看你的項目,看你這次的目的,看項目階段等等。不同的時間都是不同的策略。好吧,我們一個一個來吧。
什么階段去做,每個階段做什么,做什么顆粒度
如果你有時間,並且項目是初級階段。那么按照周為單位需要去做一次專項評估。那么這個過程中你至少要mvn或者gradle或者pod install等都success,否則搞個毛線。那么這個過程中需要去根據本周代碼的修改,從業務和技術角度去給出專項數據。這個數據到底給什么,是根據這次新功能的定位,這個產品的定位來定的,不要去想着有什么固定模式。比如騰訊qq和支付寶這兩個產品無論怎么樣,在專項的技術上面不會差很多,但是專項測試想到達到的目的和測試的場景肯定是天差地別的,明白?接着隨着功能完善之后,最終還是需要做一次類似於所有模塊集成之后的專項測試,記住,顆粒度,范圍,數據怎么獲取,完全根據測試這個owner的策略來。
反過來,如果你沒有時間。那么在success的基礎之上,你可以在功能完全完成之后直接來做一次集成專項評估。雖然效果不會差太多,但是這個方式的弊端在於,如果有問題,可能開發修改的時間就會很少,而且專項測試本身消耗的時間就會很長,所以最好是循序漸進,而不是集成之后去做,往往時間來不及。
真實場景模擬
比如這次有一個新項目,剛開始的項目,然后代碼編譯都ok了。那么作為新項目而言,我們專項其實有很多,但是又不可能都做,這個受限於你的團隊的大小,以及功能的完整度。那么可能先做最關鍵的,比如CR,比如功能體驗路徑的對比,比如內存消耗的對比,比如不同網絡下的數據對比,那么這些都是相對一個移動應用來講最最重要的。剩下的可以在之后的迭代中陸續去評估掉。顆粒度的話,還是那句話,目前除了電量以外,剩下的數據基本上都是可以通過各種方式(插樁,越獄,調用原生API,Hook等)拿到的。
怎么才算完成
這個的確是一個比較逼人的問題。所以我才會說評估不僅僅是通過打出來的apk或者ipa來做的。而是在在項目迭代中持續去做的,那么直到功能完成度100%的時候就差不多可以做一輪完整的。那么問題又來了,一般應用都還會繼續去改,怎么辦呢?所以我說要CR啊。要結合業務重要性,功能重要性,代碼的修改來一起評估每次修改所造成的影響。我們不可能每次都去做一次專項,所以這個是必須會的技能。
好了,這些解釋完了。那么我們繼續來看最最上面我提到的專項的關鍵點怎么辦。這個我就拿我在西安寫的keynote為例子吧。
Monkey
好吧。這個不是我,是工具。我先澄清下。
使用不同的策略:其實就是根據自己的策略(各種操作比重不同)來制定腳本,包括也可以簡單的二次開發,現在流行的做法就是去讀取當前所有的Views,然后去做遍歷,保證monkey可以在每個Activity上面都執行的到。
使用不同渠道商的腳本:現在各個渠道商都是有自己的monkey腳本來做測試的,如果不通過那么一樣耶會被退回來,那么與其這樣,不如提前去做。
修復所有的bug:那么這個就是標准了,0 crash和 0 ANR。這兩個都是不允許的。
內存
這個其實也是很重要的一個數據。那么我們在做之前首先先要來關注每個機器的OS給每個應用分配了多少內存占用量,否則你怎么知道數據是大是小呢。
這里提供的這個方法是我推薦的,原因是它給出的數據是個規則的矩陣,容易去做分析。但是也不是所有機器都可以去使用這個命令的,如果提示不能使用,大部分情況就是因為沒有procrank這個文件,自己Google下去下載一個push到手機里即可。當然沒有root的貌似應該不行。
當然我們也還有別的方式。
當然我們還有別的方式,比如adb shell dumpsys這個命令也可以拿到比較全的內存信息
啟動性能
啟動性能分這兩種,畢竟有緩存和沒有緩存差別很大。那么問題來了,雖然類型就兩種,但是緯度很多。
Native啟動時間
通過插樁或者grep ActivityManager或者ActivityLauncher都ok,當然更細節的話應該還有別的方式。
Hybrid啟動時間
這個詳見我QCon的PPT。這個插樁的地方就非常多了才能夠很准確的去獲取到
業務功能對應的網絡消耗時間
這個也是非常重要的。我們不僅僅要關注時間長短,更要關心每個業務到底要調用多少個接口,其中css,js,png等是否根據網絡做了不同策略的調整,是否被壓縮了。時間數據僅僅是最終的一個展現。尼瑪,今天還有人和我說公司的東西我不敢放,老子就放了這次。類似於
流量
到流量了。也是幾種方式吧
第一種通過ddms,具體怎么用我就不教了,我不喜歡手把手。
第二種就是從OS中去獲取。和新方法如下:
String rcvPath = "/proc/uid_stat/" + uid + "/tcp_rcv"; String sndPath = "/proc/uid_stat/" + uid + "/tcp_snd";
當然未必一定要用java,可以用shell或者python等腳本直接拿出來都可以啦。
第三種就是通過直接去調原生的接口來獲取,其實和ddms是一樣的道理。
@SuppressLint("ShowToast") public void getAppTrafficList() { PackageManager pm = getPackageManager(); List<PackageInfo> pinfos = pm .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS); for (PackageInfo info : pinfos) { String[] premissions = info.requestedPermissions; if (premissions != null && premissions.length > 0) { for (String premission : premissions) { if ("android.permission.INTERNET".equals(premission)) { int uId = info.applicationInfo.uid; long rx = TrafficStats.getUidRxBytes(uId); long tx = TrafficStats.getUidTxBytes(uId); if (rx < 0 || tx < 0) { continue; } else { Log.e(info.packageName.toString() + "Traffic", (rx + tx) + "kb"); } } } } } }
第四種就是架代理了。抓包去獲取流量大小和網絡數據。
當然還有最后一種,也是很重要的一種,那就是tcpdump獲取數據和wireshark來分析。具體不在這里做教導了。
應用占用量
隨着現在應用使用頻率越來越高,應用發布時候的大小也許還看得過去,但是用戶用着用着就不堪入目了。所以應用占用量的增長也是我關注的點。
我編寫了一個應用來監控被測應用的三個數據的大小。核心代碼:
@SuppressLint("NewApi") public void queryPacakgeSize(String pkgName) throws Exception { if (pkgName != null) { PackageManager pm = getPackageManager(); try { Method getPackageSizeInfo = pm.getClass().getDeclaredMethod( "getPackageSizeInfo", String.class, int.class, IPackageStatsObserver.class); getPackageSizeInfo.invoke(pm, "<package name>", Process.myUid() / 100000, new PkgSizeObserver()); } catch (Exception ex) { Log.e(TAG, "NoSuchMethodException"); ex.printStackTrace(); throw ex; } } }
然后我們就可以看到一排一排的日志啦
電量
現在的確也有三種方式
1.功耗儀(安捷倫)
精准度最高,但費用消耗龐大,並且使用不方便。無法做自動化
2.結合cpu等各種數據最終計算出電量消耗,單位是mA
精准度不如功耗儀,這個公式我這里就不能給出了
3.通過消息的方式獲取。
精准度最低,單位是%。核心代碼
new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); int status = intent.getIntExtra("status", 0); // if (status == BatteryManager.BATTERY_STATUS_CHARGING) else } };
CPU
和啟動性能一樣,也有兩種。活動狀態和靜默狀態
內存泄漏
據不可靠消息,stackoverflow上面其實有人已經說新版本的Android OS monkey就算增加這個參數在/misc/data 下面也不會有了。這個我還在進一步驗證中
那么hprof文件主要來源就是ddms了。但是為什么一定要強調場景,是因為我們單獨去拿這個數據也沒有什么太大意義。MAT分析的時候一般都是做diff的對比。而這個diff的對比必須基於場景之上。
GPU過渡繪制
我們在測試之前總要知道標准吧。
同樣的,會根據不同場景進行測試和分析。一方面是和自己比,一方面是和競品去比。一旦有超出標准的,那么繼續跟到代碼去分析。
gfxinfo
同樣的需要去根據場景來分析。由於這個功能是動態的。所以我們需要去挑選場景。比如listview的場景,比如有tabbutton的場景,也就是說用戶在做滑動啊,滾動啊,切換界面等操作時候進行動態的數據的獲取。
systrace
基本上是被我放棄的東西了。由於限制比較多。不過效果還是很不錯的,也能夠發現比較深層次的問題
IO性能
這個需要自己做一定的二次開發了。IO性能對App整體性能提升會很大。最高可以達到30%之多。
Android主要使用的是Xposed,主要是hook被測應用中針對的方法,而iOS的話在越獄手機上去使用IOStringBuffer這樣的方法去監控IO的頭文件,進行分析。無詳細案例
iOS
iOS的話我其實也就不想多說了。其實就兩個圖
自動化
最后還是來說下自動化吧。其實最早我開源過一個python的,被吐槽的一塌糊塗。后來現在又重新造輪子造了一個java的。目錄如
其實方式很簡單,就是把我們所有能夠通過命令方式抓取的到的數據全部封裝起來,通過一個多線程的方式一邊在跑的時候一邊讀取數據一邊寫文件即可。
Monitor代碼如下:
public StartPerformaceMonitor(String filePath, String sn, String PackageName) { this.sn = sn; this.PackageName = PackageName; this.filePath = filePath; } public void stop() { this.isRunning = false; } @Override public void run() { cpu_index = CpuInfo.getCpuIndex(this.sn); vss_index = VssInfo.getVssIndex(this.sn); rss_index = RssInfo.getRssIndex(this.sn); String anyproxyData = ""; try { anyproxyData = AnyproxyInfo.StartCommand(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // TODO Auto-generated method stub while (this.isRunning) { try { try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // anyproxy anyproxyData = AnyproxyInfo.getALLData(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } resultList.put("anyproxyData", anyproxyData + ""); // traffic String traffic = TrafficInfo.getTrafficData(this.sn, this.PackageName); resultList.put("traffic", traffic); System.out.println("應用網絡消耗流量:" + traffic.substring(0, traffic.length() - 2) + "kb"); // totalsize = cachesize + datasize + codesize; String[] size = AppSizeInfo.getAppSizeData(this.sn, this.PackageName); resultList.put("cachesize", size[0]); resultList.put("datasize", size[1]); resultList.put("codesize", size[2]); System.out.println("應用緩存占用量:" + size[0] + "kb" + "應用數據占用量:" + size[1] + "kb" + "應用代碼占用量" + size[2] + "kb"); // cpu float cpu = CpuInfo .getCpuData(this.sn, this.PackageName, cpu_index); resultList.put("CPU", cpu + "%"); System.out.println("cpu占用率" + cpu + "%"); // mem float vss = VssInfo .getVssData(this.sn, this.PackageName, vss_index); resultList.put("VssMemory", vss + "kb"); System.out.println("vss內存占用率" + vss + "kb"); float rss = RssInfo .getRssData(this.sn, this.PackageName, rss_index); resultList.put("RssMemory", rss + "kb"); System.out.println("rss內存占用率" + rss + "kb"); try { Thread.sleep(4000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // write fileUtils.writeFile(this.filePath, JsonUtil.toJson(resultList) + "\n", true); } }
好吧,恆溫說這篇文章估計長到沒有人看了。其實還有很多要說的。以后繼續吧。專項是一個很深奧的事情,技術其實是很輔助的,場景和策略以及分析才是最重要的。
references