Android/ target=_blank class=infotextkey>安卓更新方式,网上五花八门,但是真正实现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;
}
这种方式不能实现自动发包,但是可以完成自动更新。
adb connect 127.0.0.1:7555
adb devices
adb root
adb remount
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里面去查看包名)
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自动更新。
故事到这里就结束了。自动更新完成,喜欢的记得关注和转发。