編輯:關於android開發
由於項目需要root安裝軟件,並且希望在合適的時候引導用戶去開啟root安裝,故需要檢測手機是否root。
最基本的判斷如下,直接運行一個底層命令。(參考https://github.com/Trinea/android-common/blob/master/src/cn/trinea/android/common/util/ShellUtils.java)
也可參考csdnhttp://blog.csdn.net/fm9333/article/details/12752415
1 /** 2 * check whether has root permission 3 * 4 * @return 5 */ 6 public static boolean checkRootPermission() { 7 return execCommand("echo root", true, false).result == 0; 8 } 9 10 11 /** 12 * execute shell commands 13 * 14 * @param commands 15 * command array 16 * @param isRoot 17 * whether need to run with root 18 * @param isNeedResultMsg 19 * whether need result msg 20 * @return <ul> 21 * <li>if isNeedResultMsg is false, {@link CommandResult#successMsg} 22 * is null and {@link CommandResult#errorMsg} is null.</li> 23 * <li>if {@link CommandResult#result} is -1, there maybe some 24 * excepiton.</li> 25 * </ul> 26 */ 27 public static CommandResult execCommand(String[] commands, boolean isRoot, 28 boolean isNeedResultMsg) { 29 int result = -1; 30 if (commands == null || commands.length == 0) { 31 return new CommandResult(result, null, null); 32 } 33 34 Process process = null; 35 BufferedReader successResult = null; 36 BufferedReader errorResult = null; 37 StringBuilder successMsg = null; 38 StringBuilder errorMsg = null; 39 40 DataOutputStream os = null; 41 try { 42 process = Runtime.getRuntime().exec( 43 isRoot ? COMMAND_SU : COMMAND_SH); 44 os = new DataOutputStream(process.getOutputStream()); 45 for (String command : commands) { 46 if (command == null) { 47 continue; 48 } 49 50 // donnot use os.writeBytes(commmand), avoid chinese charset 51 // error 52 os.write(command.getBytes()); 53 os.writeBytes(COMMAND_LINE_END); 54 os.flush(); 55 } 56 os.writeBytes(COMMAND_EXIT); 57 os.flush(); 58 59 result = process.waitFor(); 60 // get command result 61 if (isNeedResultMsg) { 62 successMsg = new StringBuilder(); 63 errorMsg = new StringBuilder(); 64 successResult = new BufferedReader(new InputStreamReader( 65 process.getInputStream())); 66 errorResult = new BufferedReader(new InputStreamReader( 67 process.getErrorStream())); 68 String s; 69 while ((s = successResult.readLine()) != null) { 70 successMsg.append(s); 71 } 72 while ((s = errorResult.readLine()) != null) { 73 errorMsg.append(s); 74 } 75 } 76 } catch (IOException e) { 77 e.printStackTrace(); 78 } catch (Exception e) { 79 e.printStackTrace(); 80 } finally { 81 try { 82 if (os != null) { 83 os.close(); 84 } 85 if (successResult != null) { 86 successResult.close(); 87 } 88 if (errorResult != null) { 89 errorResult.close(); 90 } 91 } catch (IOException e) { 92 e.printStackTrace(); 93 } 94 95 if (process != null) { 96 process.destroy(); 97 } 98 } 99 return new CommandResult(result, successMsg == null ? null 100 : successMsg.toString(), errorMsg == null ? null 101 : errorMsg.toString()); 102 } 103 104 /** 105 * result of command, 106 * <ul> 107 * <li>{@link CommandResult#result} means result of command, 0 means normal, 108 * else means error, same to excute in linux shell</li> 109 * <li>{@link CommandResult#successMsg} means success message of command 110 * result</li> 111 * <li>{@link CommandResult#errorMsg} means error message of command result</li> 112 * </ul> 113 * 114 * @author Trinea 2013-5-16 115 */ 116 public static class CommandResult { 117 118 /** result of command **/ 119 public int result; 120 /** success message of command result **/ 121 public String successMsg; 122 /** error message of command result **/ 123 public String errorMsg; 124 125 public CommandResult(int result) { 126 this.result = result; 127 } 128 129 public CommandResult(int result, String successMsg, String errorMsg) { 130 this.result = result; 131 this.successMsg = successMsg; 132 this.errorMsg = errorMsg; 133 } 134 } /** 135 * execute shell command, default return result msg 136 * 137 * @param command 138 * command 139 * @param isRoot 140 * whether need to run with root 141 * @return 142 * @see ShellUtils#execCommand(String[], boolean, boolean) 143 */ 144 public static CommandResult execCommand(String command, boolean isRoot) { 145 return execCommand(new String[] { command }, isRoot, true); 146 }
但是這會帶來一個問題,每次判斷是否root都會彈出一個root請求框。這是十分不友好的一種交互方式,而且,用戶如果選擇取消,有部分手機是判斷為非root的。
這是方法一。交互不友好,而且有誤判。
在這個情況下,為了不彈出確認框,考慮到一般root手機都會有一些的特殊文件夾,比如/system/bin/su,/system/xbin/su,裡面存放有相關的權限控制文件。
因此只要手機中有一個文件夾存在就判斷這個手機root了。
然後經過測試,這種方法在大部分手機都可行。
代碼如下:
1 /** 判斷是否具有ROOT權限 ,此方法對有些手機無效,比如小米系列 */ 2 public static boolean isRoot() { 3 4 boolean res = false; 5 6 try { 7 if ((!new File("/system/bin/su").exists()) 8 && (!new File("/system/xbin/su").exists())) { 9 res = false; 10 } else { 11 res = true; 12 } 13 ; 14 } catch (Exception e) { 15 res = false; 16 } 17 return res; 18 }
這是方法二。交互友好,但是有誤判。
後來測試的過程中發現部分國產,比如小米系列,有這個文件夾,但是系統是未root的,判斷成了已root。經過分析,這是由於小米有自身的權限控制系統而導致。
考慮到小米手機有大量的用戶群,這個問題必須解決,所以不得不尋找第三種方案。
從原理著手,小米手機無論是否root,應該都是具有相關文件的。但是無效的原因應該是,文件設置了相關的權限。導致用戶組無法執行相關文件。
從這個角度看,就可以從判斷文件的權限入手。
先看下linux的文件權限吧。
linux文件權限詳細可參考《鳥叔的linux私房菜》http://vbird.dic.ksu.edu.tw/linux_basic/0210filepermission.php#filepermission_perm
只需要在第二種方法的基礎上,再另外判斷文件擁有者對這個文件是否具有可執行權限(第4個字符的狀態),就基本可以確定手機是否root了。
在已root手機上(三星i9100 android 4.4),文件權限(x或者s,s權限,可參考http://blog.chinaunix.net/uid-20809581-id-3141879.html)如下
未root手機,大部分手機沒有這兩個文件夾,小米手機有這個文件夾。未root小米手機權限如下(由於手頭暫時沒有小米手機,過幾天補上,或者有同學幫忙補上,那真是感激不盡)。
【等待補充圖片】
代碼如下:
1 /** 判斷手機是否root,不彈出root請求框<br/> */ 2 public static boolean isRoot() { 3 String binPath = "/system/bin/su"; 4 String xBinPath = "/system/xbin/su"; 5 if (new File(binPath).exists() && isExecutable(binPath)) 6 return true; 7 if (new File(xBinPath).exists() && isExecutable(xBinPath)) 8 return true; 9 return false; 10 } 11 12 private static boolean isExecutable(String filePath) { 13 Process p = null; 14 try { 15 p = Runtime.getRuntime().exec("ls -l " + filePath); 16 // 獲取返回內容 17 BufferedReader in = new BufferedReader(new InputStreamReader( 18 p.getInputStream())); 19 String str = in.readLine(); 20 Log.i(TAG, str); 21 if (str != null && str.length() >= 4) { 22 char flag = str.charAt(3); 23 if (flag == 's' || flag == 'x') 24 return true; 25 } 26 } catch (IOException e) { 27 e.printStackTrace(); 28 }finally{ 29 if(p!=null){ 30 p.destroy(); 31 } 32 } 33 return false; 34 }
這種方法基本可以判斷所有的手機,而且不彈出root請求框。這才是我們需要的,perfect。
方法三,交互友好,基本沒有誤判。
以下是apk以及相關源代碼,大家可以下載apk看下運行效果
ROOT檢測APK下載地址:http://good.gd/3091610.htm
ROOT檢測代碼下載:http://good.gd/3091609.htm或者http://download.csdn.net/detail/waylife/7639017
如果有手機使用方法三無法判斷,歡迎提出。
也歡迎大家提出其他的更好的辦法。
問啊-定制化IT教育平台,牛人一對一服務,有問必答,開發編程社交頭條 官方網站:www.wenaaa.com
QQ群290551701 聚集很多互聯網精英,技術總監,架構師,項目經理!開源技術研究,歡迎業內人士,大牛及新手有志於從事IT行業人員進入!
Android studio圖片ERROR: 9-patch image xx .9.png malformed Android studio 圖片錯誤 9-patc
Android studio ButterKnife8.1.0空指針/相關配置及其簡單使用,butterknife空指針ButterKnife 是這兩年比較流行的開源庫,
ELF Format 筆記(十三)—— 段權限,elfformatilocker:關注 Android 安全(新手) QQ: 2597294287 一個可被系統加載的程序
android的布局-----RelativeLayout(相對布局),relativelayout布局學習導圖 注:父容器定位的屬性值只能是Boolean ,兄弟組件