Android/ target=_blank class=infotextkey>安卓更新方式,網上五花八門,但是真正實現apk自動更新無痕跡的方式,少之又少,畢竟不要錢的方式,穩定的方式才能讓開發者在困難中脫穎而出。
安卓程序如何做到自動更新?安卓程序如何實現無彈框更新?
1,安卓apk自動更新方式?
a,第三方平臺更新apk,灰度發布,用友等
b,系統更新方式有彈窗contenx,通過窗體上下文方式實現更新。
c,通過安卓程序系統服務實現命令更新,自啟等。
d,通過安卓反編譯修改安卓源碼包實現自動更新。
我們采用安卓系統apk方式實現apk重啟更新上圖:
整體流程:
1,我們Tomcat服務器掛載或者ftp服務器提供apkurl地址。
2,安卓程序定時拉取獲取監聽下載url地址,下載apk到本地獲取新的版本,對比當前的apk版本進行更新。
3,如果當前版本大于運行的apk程序版本進行apk更新。
4,通過我們JAVA程序執行cmd命令 adb 方式去讀取命令行執行方式返回信息進行程序更新。
5,通過adb命令把apk推送到系統程序里面,獲取apk包名實現程序重啟,即可完成安卓程序自動更新。
實現彈框的方式:
public boolean installApk(Context context) throws Exception {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(configPath + "App-release.apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//打開安裝應用界面
context.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
?
這種方式不能實現自動發包,但是可以完成自動更新。
通過cmd命令操作實現apk覆蓋自動重啟:
1,連接到adb
adb connect 127.0.0.1:7555
2,查看連接狀態
adb devices
?
3,通過adb root 命令權限。
adb root
?
4,獲取系統app權限
adb remount
5,把app上傳到系統里面
adb push D:app-release.apk /system/app/
6,查找包名重啟apk
adb shell pm list package -f
?
查看啟動Activity類名
adb shell
dumpsys package com.instwall.launch (進行系統shell里面去查看包名)
?
7,重啟程序(復制系統app下面的應用包名)
adb shell am start -n <應用包名>/<應用啟動Activity絕對名稱>
adb shell am start -n com.instwall.launch/.MainActivity
?
我們可以看到當前的程序已經啟動。但是命令行的方式還是需要手動去打命令自然不滿足我們自動更新的操作處理,至于這里為什么不用第三方平臺的問題,原因是因為我們的apk程序要支持離線更新,apk程序本身在專網里面,所以有很多限制。這里我們可以通過java 程序去代替我們執行adb 腳本命令從而實現apk自動更新,只不過步驟要寫很多。
貼幾個代碼執行腳本的代碼:
版本更新的核心處理類。
package com.instwall.launch.task;
import static com.instwall.launch.contans.Contans.configPath;
import static com.instwall.launch.utils.FileDirectoryUtils.dropVedio;
import static com.instwall.launch.utils.FileDirectoryUtils.getDownApkVersion;
import static com.instwall.launch.utils.FileDirectoryUtils.readAppInfo;
import static com.instwall.launch.utils.FileDirectoryUtils.writeAppInfo;
import static com.instwall.launch.utils.FileDirectoryUtils.writeLog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.instwall.launch.contans.Contans;
import com.instwall.launch.utils.CmdTerminalUtils;
import com.instwall.launch.utils.FileDirectoryUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
/**
* 版本更新
*/
public class UpdateVersionTask implements Runnable {
private final Context context;
public UpdateVersionTask(Context context) {
this.context = context;
}
private static void download(String mUrl, String mPath) throws MalformedURLException {
// 下載網絡文件
int byteread = 0;
int mDownloadSize = 0;
URL url = new URL(mUrl);
URLConnection conn = null;
InputStream is = null;
FileOutputStream os = null;
try {
conn = url.openConnection();
is = conn.getInputStream();
os = new FileOutputStream(mPath);
byte[] buffer = new byte[1204];
int length;
while ((byteread = is.read(buffer)) != -1) {
mDownloadSize += byteread;
os.write(buffer, 0, byteread);
}
Log.i("文件下載成功-------", mPath);
writeLog("apk down file success .......... " + mPath);
} catch (FileNotFoundException e) {
e.printStackTrace();
// dropFile(mPath);
writeLog("apk down file error .......... " + e.getMessage());
Log.e("文件下載失敗-------", e.getMessage());
} catch (IOException e) {
e.printStackTrace();
// dropFile(mPath);
Log.e("文件下載失敗---刪除當前下載文件----", e.getMessage());
writeLog("apk down file error .......... " + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
// dropFile(mPath);
Log.e("文件下載失敗---刪除當前下載文件----", e.getMessage());
writeLog("apk down file error .......... " + e.getMessage());
} finally {
// 關閉輸出流
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 關閉輸入流
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void run() {
try {
download(Contans.getApiDownUrl(), configPath + "app-release.apk");
//獲取下載的版本包
int verson = getDownApkVersion(context, configPath + "app-release.apk");
// writeLog("down apk version .......... " + verson);
int installVersion = readAppInfo();
if (installVersion == 0) {
installVersion = FileDirectoryUtils.getVersionCode(context);
}
writeLog("down now version .......... " + installVersion);
//如果下載的版本大于當前運行的apk版本直接安裝最新并且寫入文件
if (verson > installVersion) {
writeLog("down now version .......... " + installVersion);
//安裝apk
CmdTerminalUtils.startApk();
//寫入當前版本到文件夾
writeAppInfo(verson);
//刪除沒有下載完成的視頻
dropVedio();
}
} catch (Exception e) {
writeLog("ReportHear api pulish error .......... " + e.getMessage());
}
}
public boolean installApk(Context context) throws Exception {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(configPath + "app-release.apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//打開安裝應用界面
context.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
@RequiresApi(api = Build.VERSION_CODES.O)
private boolean isHasInstallPermissionWithO(Context context) {
if (context == null) {
return false;
}
return context.getPackageManager().canRequestPackageInstalls();
}
@RequiresApi(api = Build.VERSION_CODES.O)
private void startInstallPermissionSettingActivity(Context context) {
if (context == null) {
return;
}
Intent intent = new Intent();
//獲取當前apk包URI,并設置到intent中(這一步設置,可讓“未知應用權限設置界面”只顯示當前應用的設置項)
Uri packageURI = Uri.parse("package:" + context.getPackageName());
intent.setData(packageURI);
//設置不同版本跳轉未知應用的動作
if (Build.VERSION.SDK_INT >= 26) {
//intent = new Intent(android.provider.Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,packageURI);
intent.setAction(android.provider.Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
} else {
intent.setAction(android.provider.Settings.ACTION_SECURITY_SETTINGS);
}
context.startActivity(intent);
}
}
1,建立連接
/**
* 建立連接
*
* @return
*/
public static boolean firstCmd() throws IOException {
Process process = null;
String command = "D:\platform-tools\adb connect 127.0.0.1:7555"; //sdk所在位置
process = Runtime.getRuntime().exec(command);
InputStreamReader ir = new InputStreamReader(process.getInputStream());
try {
LineNumberReader input = new LineNumberReader(ir);
String line;
List<String> list = new ArrayList<>();
while ((line = input.readLine()) != null) {
list.add(line);
}
input.close();
for (String result : list) {
if (result.contains("device") || result.contains("already")) {
writeLog("adb: "+command );
writeLog("adb first cmd message: "+result );
return true;
}
System.out.println("message" + result);
}
} catch (IOException e) {
System.err.println("IOException" + e.getMessage());
} finally {
ir.close();
}
return false;
}
2,賦予權限:
/**
* 建立連接
*
* @return
*/
public static boolean twoCmd() throws IOException {
Process process = null;
String command = "D:\platform-tools\adb root"; //sdk所在位置
process = Runtime.getRuntime().exec(command);
InputStreamReader ir = new InputStreamReader(process.getInputStream());
try {
LineNumberReader input = new LineNumberReader(ir);
String line;
List<String> list = new ArrayList<>();
while ((line = input.readLine()) != null) {
list.add(line);
}
for (String result : list) {
if (result.equals("adbd is already running as root")) {
writeLog("adb: "+command );
writeLog("adb tow cmd message: "+result );
return true;
}
if (result.contains("device")) {
writeLog("adb: "+command );
writeLog("adb tow cmd message: "+result );
return true;
}
System.out.println("message" + result);
}
} catch (IOException e) {
System.err.println("IOException" + e.getMessage());
} finally {
ir.close();
}
return false;
}
3,后面的步驟同上,把不同腳本信息的命令通過cmd執行,獲取特定的返回messge信息判斷程序命令是否執行成功。
/**
* 通過命令啟動apk
*/
public static void startApk() {
//定義執行成功標識,如果執行不成功一直執行到成功
boolean flag = true;
while (flag)
try {
//執行命令
boolean first = firstCmd();
if (first) {
//執行權限
boolean two = twoCmd();
if (two) {
boolean three = threeCmd();
if (three) {
fourCmd();
fiveCmd();
flag = false;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
我們之間通過字符串包含的方式判斷命令是否執行完成,即可完成命令的執行,從而實現我們java程序去處理adb命令從而實現apk自動更新。
?
故事到這里就結束了。自動更新完成,喜歡的記得關注和轉發。