1. 程式人生 > >Android自動dump hprof檔案的功能實現

Android自動dump hprof檔案的功能實現

要實現這個功能,必須提升許可權,必須滿足以下兩個條件之一

1、在root的裝置上執行

2、如果機子沒root,需要在app的manifest檔案中新增sharedUid,但是使用了這個的話,需要對apk檔案進行系統簽名

android:sharedUserId="android.uid.shell"


【步驟1】先準備一個工具類,用於獲取程序的記憶體,dump hrpof檔案等操作,程式碼如下:

**
 * 系統級的操作的工具類
 *
 */
public class SystemOper {
	private static String TAG = "SystemOper";

	
	/**
	 * 獲取應用所佔用的記憶體大小
	 *
	 * @param pkgName 應用的包名
	 * @return 佔用記憶體的大小(kB),包括native heap 和 dalvik heap等,為總記憶體大小
	 */
	public static int getProcessMemory(String pkgName) {
		int memoryUsed = 0;
		String getMemory = "dumpsys meminfo | grep " + pkgName;
		ShellUtils.CommandResult getMemoryResult = ShellUtils.execCommand(
				getMemory, false);
		if (getMemoryResult.successMsg.trim().length() > 0) {
			String str = getMemoryResult.successMsg;
			int end = str.indexOf(" kB:");
			memoryUsed = Integer.parseInt(str.substring(0, end).trim());
			Log.i(TAG, "getProcessMemory: " + memoryUsed);
		}
		return memoryUsed;
	}

	/**
	 * dump應用的hprof檔案,為了保證資料的完整性,此步驟將耗時1分鐘
	 * hprof檔案存放在/sdcard/autotest/hprof路徑下
	 * @param pkgName 程序名稱
	 */
	public static void getHprof(final String pkgName) {

		Thread dumpThread = new Thread(new Runnable() {

			@Override
			public void run() {
				// 1、得到程序號
				String findPID = getPidByName(pkgName);
				if (findPID == null) {
					return;
				}
				File srcFile = new File("/data/local/tmp/" + pkgName + ".hprof");
				Date date = new Date();
				SimpleDateFormat format = new SimpleDateFormat(
						"yyyyMMdd_HH:mm:ss");
				String time = format.format(date);
				File hprofFile_sdcard = new File("/sdcard/autotest/hprof/"
						+ pkgName + "_" + time + ".hprof");
				if (!hprofFile_sdcard.getParentFile().exists())
					hprofFile_sdcard.getParentFile().mkdirs();
				if (!hprofFile_sdcard.exists()) {
					try {
						hprofFile_sdcard.createNewFile();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				// 2、根據程序號dumpheap
				String dumpHeap = "am dumpheap " + findPID + " "
						+ srcFile.getAbsolutePath();
				ShellUtils.CommandResult dumpHeapResult = ShellUtils
						.execCommand(dumpHeap, false);

				// 3、等待dump檔案的生成
				FileOperator.waitForCompleted(srcFile);

				// 4、將檔案拷貝到sdcard下
				String cpHprof = "cp " + srcFile.getAbsolutePath() + " "
						+ hprofFile_sdcard.getAbsolutePath();
				ShellUtils.CommandResult cpHprofResult = ShellUtils
						.execCommand(cpHprof, false);
				TestReport.i(TAG, "DUMP完畢!" + cpHprofResult.errorMsg + "---"
						+ cpHprofResult.successMsg);
				// 5、刪除原始檔
				srcFile.delete();

			}
		});
		dumpThread.start();
	}

	/**
	 * 通過包名查詢pid,如果是system許可權,該方法將失效,如果程序不在,則返回null
	 *
	 * @param pkgName 包名
	 * @return pid
	 */
	public static String getPidByName(String pkgName) {
		String findPID = "ps | grep " + pkgName;
		ShellUtils.CommandResult result = ShellUtils
				.execCommand(findPID, false);
		if (result.successMsg.trim().length() > 0) {
			String[] strs = result.successMsg.split(" ");
			ArrayList<String> list = new ArrayList<String>();
			for (int i = 0; i < strs.length; i++) {
				if (strs[i].trim().length() > 0) {
					list.add(strs[i]);
				}
			}
			return list.get(1);
		} else {
			TestReport.i(TAG, pkgName + "程序不存在!");
			return null;
		}

	}
}
這個裡面的ShellUtils類就不給了,就是一個自己封裝的方法,核心程式碼就是Runtime.getRuntime().exec(String command)

然後dump hprof檔案的命令需要解釋下,格式為

am dumpheap pid /data/local/tmp/pkgName.hprof

這個檔案最好還是先放到tmp目錄下,不同的機子許可權不一樣,雖然理論上這個命令是可以將hprof檔案直接放到/sdcard下的,但是發現有些機子是不行的,所以為了安全起見還是放到tmp下,再拷貝出去,拷之前要確保hprof檔案已經生成完畢,

FileOperator.waitForCompleted(srcFile);

這個方法參照上一篇文章

【步驟2】自己開啟一個執行緒,不斷判斷SystemOper.getProcessMemory(youpkg)的值,如果大於你設的閾值,就呼叫SystemOper.getHprof(youpkg)