還是一如既往的android開發板子,需求是靜默升級。靜默升級顧名思義,就是後臺默默下載,在用戶無感知的情況,完成APP的升級,雖說現在有熱更新的技術,但是奈何需求與實際不相符合,並且必須得有用戶參與,纔有很大機率成功。所以還是硬着頭皮去研究一波靜默升級,好在android是root權限,基本的條件是有了。
現在基本思路是這樣,第一,在應用啓動的時候,獲取版本號,然後啓動下載,下載完成之後保存到指定的文件夾,這個想必對大多數搬磚工友來說,都不難,分享一個小的框架,簡單易用。Aria。示例代碼如下,
//開啓下載
Aria.download(this)
.load(bean.url) //讀取下載地址
.setFilePath(Constants.FILE_APK) //設置文件保存的完整路徑
.start(); //啓動下載
第二,在下載完成之後,思路就是如何才能調用命令安裝,一般的安裝代碼肯定是沒戲,但是shell命令可以試試,調用這個命令就好比。在應用外觸發了一個adb命令一般,命令格式是 pm install -r " + apkPath,有沒有一種熟悉的感覺,哈哈。下面分享一個封裝好的工具類,可以直接傳命令執行即可。
private static final String COMMAND_SU = "su";
private static final String COMMAND_SH = "sh";
private static final String COMMAND_EXIT = "exit\n";
private static final String COMMAND_LINE_END = "\n";
/**
* 執行shell命令
*/
public static CommandResult execCommand(String[] commands, boolean isRoot,
boolean isNeedResultMsg) {
int result = -1;
if (commands == null || commands.length == 0) {
return new CommandResult(result, null, null);
}
Process process = null;
BufferedReader successResult = null;
BufferedReader errorResult = null;
StringBuilder successMsg = null;
StringBuilder errorMsg = null;
DataOutputStream os = null;
StringBuilder cmd = new StringBuilder();
try {
process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH);
cmd.append(isRoot ? COMMAND_SU : COMMAND_SH);
cmd.append(COMMAND_LINE_END);
os = new DataOutputStream(process.getOutputStream());
for (String command : commands) {
if (command == null) {
continue;
}
os.writeBytes(command);
os.writeBytes(COMMAND_LINE_END);
cmd.append(command);
cmd.append(COMMAND_LINE_END);
os.flush();
}
os.writeBytes(COMMAND_EXIT);
cmd.append(COMMAND_EXIT);
os.flush();
result = process.waitFor();
// get command result
if (isNeedResultMsg) {
successMsg = new StringBuilder();
errorMsg = new StringBuilder();
successResult = new BufferedReader(new InputStreamReader(
process.getInputStream()));
errorResult = new BufferedReader(new InputStreamReader(
process.getErrorStream()));
String s;
while ((s = successResult.readLine()) != null) {
successMsg.append(s);
}
while ((s = errorResult.readLine()) != null) {
errorMsg.append(s);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (os != null) {
os.close();
}
if (successResult != null) {
successResult.close();
}
if (errorResult != null) {
errorResult.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (process != null) {
process.destroy();
}
}
/*Log.i(TAG, "execCommand: " + cmd.toString());
Log.i(TAG, "execCommand: result = " + result +
", successMsg = " + (successMsg == null ? null : successMsg.toString()) +
", errorMsg = " + (errorMsg == null ? null : errorMsg.toString()));*/
return new CommandResult(result,
successMsg == null ? null : successMsg.toString(),
errorMsg == null ? null : errorMsg.toString());
}
/**
* 運行結果
*/
public static class CommandResult {
//運行結果
private int result;
//運行成功結果
private String successMsg;
//運行失敗結果
private String errorMsg;
public CommandResult(int result) {
this.result = result;
}
public CommandResult(int result, String successMsg, String errorMsg) {
this.result = result;
this.successMsg = successMsg;
this.errorMsg = errorMsg;
}
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
public String getSuccessMsg() {
return successMsg;
}
public void setSuccessMsg(String successMsg) {
this.successMsg = successMsg;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
代碼的大概邏輯就是先判斷有沒有root權限,大家也知道,沒有這個,也沒辦法調用這個命令,如果有的話,就執行命令
意思爲覆蓋安裝。安裝完成之後會有結果,如果我們還想安裝完成自啓動,記着不是開機自啓動,而是,靜默升級的程序重啓,
這個時候需要找一個系統廣播,配合來啓動一波。系統在安裝包卸載重新安裝的時候,會去發送系統廣播,。至於想怎麼樣操
作,可以結合業務邏輯判斷action,來操作,同樣,這個廣播需要靜態註冊,否則,你懂得
public class UpdateReceiver extends BroadcastReceiver {
private static final String TAG = UpdateReceiver.class.getSimpleName();
@Override
public void onReceive(Context context, Intent intent) {
String packageName = intent.getDataString();
if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {//接收升級廣播
Logger.e(TAG, "onReceive:升級了一個安裝包,重新啓動此程序");
if (packageName.equals("package:" + SystemUtil.getPackageName())) {
SystemUtil.restartApp();//升級完自身app,重啓自身
}
} else if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {//接收安裝廣播
Logger.e(TAG, "onReceive:安裝了" + packageName);
if (packageName.equals("package:" + SystemUtil.getPackageName())) {
/*SystemUtil.reBootDevice();*/
}
} else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
//接收卸載廣播
Logger.e(TAG, "onReceive:卸載了" + packageName);
}
}
}
配置文件中來一波,靜態註冊
<receiver
android:name=".receiver.UpdateReceiver"
android:enabled="true">
<intent-filter android:priority="100">
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
走到這一步,然後程序run一把,然後讓服務器配合一波,基本就可以了。如果遇見問題,可以參照下面的踩坑記載,對了
記得在配置文件添加相關的權限。因爲我這個版本相對來說是6.0,稍微低一些,如果大家在高版本的板子上遇見問題,可以留言
大家一起討論一波,基本上都是適配權限的問題。還有就是系統簽名,一定要系統簽名,否則人家不認你。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
下面來說一下搞這個時候遇見幾個小坑。
1.首先設備是root的,這個就不用想了,不root這個功能基本不用想了。
2.第二個得啓用系統簽名,這樣系統才能認爲你是自己人,纔會讓你安裝,以及後來的自啓動,都離不開這個。
系統簽名有幾種方式,其中就是你找到系統的兩個文件夾,自己配置一波,然後生成簽名文件,配置到debug簽名下面
這樣你生成的就是系統簽名apk,這種查了資料,稍微麻煩一些,當然大家有時間的話,那種也挺方便,在這個推薦一種傻瓜方式。主要是節省時間,一把梭,但是得注意不要弄混了安裝包,
簽名命令,在當前文件夾執行如下命令,第一個app-debug.apk文件是待簽名之前的app,appNew.apk是簽名後的app,簽名成功保存在當前文件夾下, 示例命令,java -jar signapk.jar platform.x509.pem platform.pk8 app-debug.apk appNew.apk。
下面是一個朋友整理的資源,分享一波,裏面有簽名工具
鏈接:https://pan.baidu.com/s/18zQucxlvWHLkPNSVXlXKiw
提取碼:bo8c