Execute Shell Command in Android App
只要是Android開發者,一定都知道Android在SDK當中提供了一個好用的工具叫做ADB(Android Debug Bridge),它可以讓開發者透過電腦與裝置做溝通、安裝應用程式和除錯,也可以進入Unix Shell裡下其他指令。要使用ADB之前,必須先將Android裝置與電腦做連線,接著執行下列指令來確認裝置已經成功聯繫:
成功連接就會出現裝置序號(Serial Number),如下圖所示。

此時,可以開始使用指令來操作系統了,最常使用的莫過於讀取Log資訊,其他像是取得系統資訊、查看目錄、搜尋檔案等等,都是時常需要使用的,下面列出一些泰勞本身常用的指令。
當然也可以僅透過"adb shell"單純進入Android系統的Shell,接著你會看到終端機的開頭標示著"shell@xxxxx",通常會是裝置的某個代號。此時,不須再經由adb開頭的指令來操作了,一般Linux上常用的指令,只要Android有支援,都可以直接使用,例如:cd、cp、mv、ls、ls -la等等,如下圖所示。

簡單的講完前情提要之後,回到本篇文章的重點,要如何從應用程式的角度對系統下指令呢?其實並不難,只要使用Java提供的一些API並套入正確的指令就可以完成。有興趣想更深入研究的話,可以先詳閱以下參考資料喔!
參考資料:
Execute shell command from Android
Process - Oracle Help Center
Runtime - Oracle Help Center
廢話不多說,直接來看程式碼吧!以下範例為應用程式執行logcat指令並將包含關鍵字 "android.intent.action." 的Log紀錄篩選出來。
將Log蒐集回來之後,可以用readLine()這個方法依序將Log輸出,再看你想如何處理這些資訊。以下提供我的處理方式給大家參考。
如果有需要將Process回傳的資訊顯示在應用程式的畫面上,會建議將此功能寫在Service裡面,讓其與主執行序(Main Thread)分開,避免主畫面卡住。這部份可以參考泰勞放在GitHub上的完整程式碼,如下方連結所示。
GitHub:
https://github.com/Kuanlin-Chen/PIAndroid/blob/master/app/src/main/java/kkk/taiwan/kuanlin/piandroid/ServiceLog.java
$ adb devices
成功連接就會出現裝置序號(Serial Number),如下圖所示。

此時,可以開始使用指令來操作系統了,最常使用的莫過於讀取Log資訊,其他像是取得系統資訊、查看目錄、搜尋檔案等等,都是時常需要使用的,下面列出一些泰勞本身常用的指令。
$ adb shell dmesg --> 核心(Kernel)Log資訊
$ adb shell logcat --> 系統和應用程式的除錯(Debug)訊息
$ adb shell dumpsys --> 提供在Services、Memory或其他系統詳細的資訊
$ adb shell logcat --> 系統和應用程式的除錯(Debug)訊息
$ adb shell dumpsys --> 提供在Services、Memory或其他系統詳細的資訊
當然也可以僅透過"adb shell"單純進入Android系統的Shell,接著你會看到終端機的開頭標示著"shell@xxxxx",通常會是裝置的某個代號。此時,不須再經由adb開頭的指令來操作了,一般Linux上常用的指令,只要Android有支援,都可以直接使用,例如:cd、cp、mv、ls、ls -la等等,如下圖所示。

簡單的講完前情提要之後,回到本篇文章的重點,要如何從應用程式的角度對系統下指令呢?其實並不難,只要使用Java提供的一些API並套入正確的指令就可以完成。有興趣想更深入研究的話,可以先詳閱以下參考資料喔!
參考資料:
Execute shell command from Android
Process - Oracle Help Center
Runtime - Oracle Help Center
廢話不多說,直接來看程式碼吧!以下範例為應用程式執行logcat指令並將包含關鍵字 "android.intent.action." 的Log紀錄篩選出來。
try {
//在系統裡,一般應用程式受到UID的限制,僅能查看屬於自己的Log
//若想蒐集完整的Log必須先使用su指令切換至root身份
Process process = Runtime.getRuntime().exec("su");
//透過Outputstream將指令輸出至Shell執行
DataOutputStream dos = new DataOutputStream(process.getOutputStream());
//將欲執行的指令寫入OutPutStream
//write方法會先將資料暫存於Buffer(緩衝區)裡面
//"\n"是換行,相當於在Shell按下Enter讓指令執行
dos.writeBytes("logcat -v time -d |grep 'android.intent.action.'\n");
//透過getInputstream接收Shell回傳的資料,並放入BufferedReader
BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
//離開root身份
dos.writeBytes("exit\n");
//flush這個方法會強制將Stream的Buffer(緩衝區)裡暫存的資料發送出去
//在這裡的意思就是開始執行上述寫好的指令
dos.flush();
} catch (IOException e) {
//若沒有su時該做的事
............
}
//在系統裡,一般應用程式受到UID的限制,僅能查看屬於自己的Log
//若想蒐集完整的Log必須先使用su指令切換至root身份
Process process = Runtime.getRuntime().exec("su");
//透過Outputstream將指令輸出至Shell執行
DataOutputStream dos = new DataOutputStream(process.getOutputStream());
//將欲執行的指令寫入OutPutStream
//write方法會先將資料暫存於Buffer(緩衝區)裡面
//"\n"是換行,相當於在Shell按下Enter讓指令執行
dos.writeBytes("logcat -v time -d |grep 'android.intent.action.'\n");
//透過getInputstream接收Shell回傳的資料,並放入BufferedReader
BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
//離開root身份
dos.writeBytes("exit\n");
//flush這個方法會強制將Stream的Buffer(緩衝區)裡暫存的資料發送出去
//在這裡的意思就是開始執行上述寫好的指令
dos.flush();
} catch (IOException e) {
//若沒有su時該做的事
............
}
將Log蒐集回來之後,可以用readLine()這個方法依序將Log輸出,再看你想如何處理這些資訊。以下提供我的處理方式給大家參考。
StringBuilder mylog = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null){
log.append(line);
log.append("\n");
}
//轉成字串陣列
String[] splitlog;
splitlog = mylog.toString().split("\n");
String line;
while ((line = bufferedReader.readLine()) != null){
log.append(line);
log.append("\n");
}
//轉成字串陣列
String[] splitlog;
splitlog = mylog.toString().split("\n");
如果有需要將Process回傳的資訊顯示在應用程式的畫面上,會建議將此功能寫在Service裡面,讓其與主執行序(Main Thread)分開,避免主畫面卡住。這部份可以參考泰勞放在GitHub上的完整程式碼,如下方連結所示。
GitHub:
https://github.com/Kuanlin-Chen/PIAndroid/blob/master/app/src/main/java/kkk/taiwan/kuanlin/piandroid/ServiceLog.java
留言
張貼留言