admin管理员组文章数量:1532343
2023年12月14日发(作者:)
App的版本迭代更新
一、简介:
在APP开发中,应用上线后公司肯定后期会对应用进行维护对一些Bug修复,这种情况就需要版本迭代了。检测到服务器版本比本地手机版本高
的时候,手机会询问用户是否要下载最新的app,然后下载apk下来,然后进行安装。
备注:
也可以用第三方服务,比如腾讯的Bugly、Bmob云服务等,也挺方便的,不过apk要上传到第三方的平台上,如果公司要求在自己平台上,就只
能自己写了。
二、实现步骤
上一张开发中的版本迭代的流程图
具体来说大概是如下几步:
1、每次启动应用我们就获取放在服务器上的版本信息,我们获取到版本号与当前应用的版本好进行对比,这样我们就可以知道应用是否更新了,
版本信息一般包含如下内容:
{
"versionCode": "2", //版本号
"versionName": "2.0", //版本名称
//服务器上最新版本的app的下载地址
"apkUrl": "/",
"updateTitle": "更新提示" ,
"changeLog":"1.修复xxx Bug;2.更新了某某UI界面."
}
备注:
versionCode 2 //对用户不可见,仅用于应用市场、程序内部识别版本,判断新旧等用途。
versionName "2.0"//展示给用户,让用户会知道自己安装的当前版本.
//versionCode的值,必须是int
2、获取用户当前使用的APP的versionCode(版本号)
/**
* 获取当前APP版本号
* @param context
* @return
*/
public static int getPackageVersionCode(Context context){
PackageManager manager = kageManager();
PackageInfo packageInfo = null;
try {
packageInfo = kageInfo(kageName(),0);
} catch (tFoundException e) {
tackTrace();
}
if(packageInfo != null){
return nCode;
}else{
return 1;
}
}
3、拿到本地的版本号后,与获取到的服务器的最新的版本号做对比,如果比我们本地获取的APP的versionCode 高,则就进行下一步
//如果当前版本小于新版本,则更新
//获取当前app版本
int currVersionCode = kageVersionCode();
//newVersionCode自己通过网络框架访问服务器,解析数据得到
if(currVersionCode < newVersionCode){
Log.i("tag", "有新版本需要更新");
showHintDialog(); //弹出对话框,提示用户更新APP
}
4、如果服务器有新的高版本,则弹出对话框提示用户更新
//显示询问用户是否更新APP的dialog
private void showHintDialog() {
r builder = new r(this);
n(_launcher)
.setMessage("检测到当前有新版本,是否更新?")
.setNegativeButton("取消", new kListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//取消更新,则跳转到旧版本的APP的页面
xt(, "暂时不更新app", _SHORT).show();
}
})
.setPositiveButton("确定", new kListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//6.0以下系统,不需要请求权限,直接下载新版本的app
if (_INT < N_CODES.M) {
downloadApk();
} else {
//6.0以上,先检查,申请权限,再下载
checkPermission();
}
}
}).create().show();
}
5、如果用户选择了更新APP,则对手机系统版本进行判断
6.0以下系统,不需要请求权限,直接下载新版本的app
6.0以上,先检查,申请权限,再下载
顺便给出版本迭代需要的2个主要权限
<--网络权限-->
<--读写sdcard的权限-->
<--访问网络状态的权限-->
//检查权限
private void checkPermission() {
//app更新所需的权限
String[] permissions = {_EXTERNAL_STORAGE, ET};
if (missions(this, permissions)) {
// Already have permission, do the thing
// ...
downloadApk();
} else {
// Do not have permissions, request them now(请求权限)
tPermissions(this, "app更新需要读写sdcard的权限",
REQUEST_CODE_WRITE, permissions);
}
}
授权结果的回调:
//授权的结果的回调方法
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
estPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == REQUEST_CODE_WRITE){
if( > 0 && grantResults[0] == SION_GRANTED){
downloadApk();
}
}
}
备注:
ET完全可以不用写,但是,我之前在比较复杂的测试中,遇到过问题,故此处就加上了。
权限申请,有时,用户拒绝了授权,并且勾选了不再提示的选项,那么用户会因为没有授权而不能使用一些功能,这样的用户体验是非常糟糕的,
为了解决这个问题,我们可以通过弹出自定义的Dialog来让用户打开APP设置界面去手动开启相应的权限,这样才能完整的使用app,所以还需要
实现sionCallbacks接口,重写如下方法
/**
* 用户同意授权了
*
* @param requestCode
* @param perms
*/
@Override
public void onPermissionsGranted(int requestCode, List
downloadApk();
Log.i("tag","--------->同意授权");
}
/**
* 用户拒绝了授权,则通过弹出对话框让用户打开app设置界面,
* 手动授权,然后返回app进行版本更新
*
* @param requestCode
* @param perms
*/
@Override
public void onPermissionsDenied(int requestCode, List
xt(this, "没有同意授权", _SHORT).show();
if (rmissionPermanentlyDenied(this, perms)) {
new r(this, "请设置权限")
.setTitle("设置对话框")
.setPositiveButton("设置")
.setNegativeButton("取消", null /* click listener */)
.setRequestCode(RC_SETTINGS_SCREEN)
.build()
.show();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
vityResult(requestCode, resultCode, data);
if (requestCode == RC_SETTINGS_SCREEN) {
// Do something after user returned from app settings screen, like showing a Toast.
xt(this, "从app设置返回应用界面", _SHORT)
.show();
downloadApk();
}
}
----------------------至此我们也已经把下载APK的的权限也搞定了-----------------
7、接下来只需要进行下载安装即可,我们这时候就要判断,是否处于WiFi状态下,如果是WiFi情况下就直接进行更新,如果不是,再创建对话
框,然后询问用户,是否确定需要通过流量来进行下载:(因为一般下载都是在后台,所以都是放在Service中进行操作的。通过
startService(new Intent(, ));来启动服务进行下载)
判断是否处于WiFi状态
/**
* 判断是否处于WiFi状态
* getActiveNetworkInfo 是可用的网络,不一定是链接的,getNetworkInfo 是链接的。
*/
public static boolean isWifi(Context context) {
ConnectivityManager manager = (ConnectivityManager)context. getSystemService(CONNECTIVITY_SERVICE);
//NetworkInfo info = workInfo(_WIFI);
NetworkInfo networkInfo = iveNetworkInfo();
//处于WiFi连接状态
if (networkInfo != null && e() == _WIFI) {
return true;
}
return false;
}
新版app下载
//下载最新版的app
private void downloadApk() {
boolean isWifi = (this); //是否处于WiFi状态
if (isWifi) {
startService(new Intent(, ));
xt(, "开始下载。", _LONG).show();
} else {
//弹出对话框,提示是否用流量下载
r builder = new r(this);
le("提示");
sage("是否要用流量进行下载更新");
ativeButton("取消", new kListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
s();
xt(, "取消更新。", _LONG).show();
}
});
itiveButton("确定", new kListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
startService(new Intent(, ));
xt(, "开始下载。", _LONG).show();
}
});
celable(false);
AlertDialog dialog = ();
//设置不可取消对话框
celable(false);
celedOnTouchOutside(false);
();
}
}
备注:
如果对service不是很理解的童鞋,可以看看这篇文章深入理解Service
8、Service进行下载
这里是用DownloadManager进行下载的,下载完成后,点击通知的图标,可以自动安装。
这里顺便给出一个DownloadManager的链接,有需要的,可以自行阅读Android系统下载管理DownloadManager
1)通过DownLoadManager来进行APK的下载,代码如下:
//开始下载最新版本的apk文件
DownloadManager downloadManager = (DownloadManager)temService(DOWNLOAD_SERVICE);
//DownloadManager实现下载
t request = new t((_VERSION_APP_URL));
le("文件下载")
.setDestinationInExternalPublicDir(ORY_DOWNLOADS,_VERSION_APK_NAME)
//设置通知在下载中和下载完成都会显示
//.setNotificationVisibility(LITY_VISIBLE_NOTIFY_COMPLETED)
//设置通知只在下载过程中显示,下载完成后不再显示
.setNotificationVisibility(LITY_VISIBLE);
e(request);
2)下载完毕,自动安装的实现
当DownLoadManager下载完成后,会发送一个_DOWNLOAD_COMPLETE的广播,所以我们只要刚开始在启
动Service的时候,注册一个广播,监听
_DOWNLOAD_COMPLETE,然后当下载完成后,在BroadcastReceiver中调用安装APK的方法即可。
//广播接收的注册
public void receiverRegist() {
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//安装apk
lApk(context);
stopSelf(); //停止下载的Service
}
};
IntentFilter filter = new IntentFilter(_DOWNLOAD_COMPLETE);
registerReceiver(receiver, filter); //注册广播
}
3)通过隐式意图安装apk
/**Apk的安装
*
* @param context
*/
public static void installApk(Context context) {
Intent intent = new Intent();
ion(_VIEW);
gs(_ACTIVITY_NEW_TASK); //这个必须有
aAndType(
le(new File(ernalStoragePublicDirectory(
ORY_DOWNLOADS), _VERSION_APK_NAME)),
"application/e-archive");
ctivity(intent);
}
Service中的完整代码
public class UpdateService extends Service {
public static final int NOTIFICATION_ID = 100;
private static final int REQUEST_CODE = 10; //PendingIntent中的请求码
//下载的新版本的apk的存放路径
public static final String destPath = ernalStoragePublicDirectory(ORY_DOWNLOADS) + tor + "";
private Context mContext = this;
private Notification mNotification;
private NotificationManager manager;
private r builder;
private RemoteViews remoteViews;
private BroadcastReceiver receiver;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
receiverRegist();
//下载apk文件
adApkByDownloadManager(this);
return _STICKY;
}
@Override
public void onDestroy() {
roy();
//解除注册
unregisterReceiver(receiver);
}
//广播接收的注册
public void receiverRegist() {
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//安装apk
lApk(context);
stopSelf(); //停止下载的Service
}
};
IntentFilter filter = new IntentFilter(_DOWNLOAD_COMPLETE);
registerReceiver(receiver, filter); //注册广播
}
}
下面这段代码是自己封装的下载apk并实现自动安装的功能,如有不妥之处,敬请 指出
public class UpdateService extends IntentService {
public static final int NOTIFICATION_ID = 100;
private static final int REQUEST_CODE = 10; //PendingIntent中的请求码
public static final String destPath = ernalStoragePublicDirectory(ORY_DOWNLOADS) + tor + "";
private Context mContext = this;
private Notification mNotification;
private NotificationManager manager;
private r builder;
private RemoteViews remoteViews;
public UpdateService() {
super("UpdateService");
}
@Override
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
//开始下载最新版本的apk文件
initNotification();
download(_VERSION_APP_URL);
}
}
private void download(String newVersionApkUrl) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
URL url = new URL(newVersionApkUrl);
HttpURLConnection conn = (HttpURLConnection) nnection();
//设置连接的属性
nectTimeout(5000);
dTimeout(5000);
//如果响应码为200
if (ponseCode() == 200) {
bis = new BufferedInputStream(utStream());
bos = new BufferedOutputStream(new FileOutputStream(destPath));
int totalSize;
int count = 0; //读取到的字节数的计数器
int progress; //当前进度
byte[] data = new byte[1024 * 1024];
int len;
//文件总的大小
totalSize = tentLength();
while ((len = (data)) != -1) {
count += len; //读取当前总的字节数
(data, 0, len);
();
progress = (int) ((count / (float) totalSize) * 100);
//progress = (count * 100) / totalSize; //当前下载的进度
//重新设置自定义通知的进度条的进度
gressBar(ssBar, 100, progress, false);
tViewText(_progress, "已经下载了:" + progress + "%");
//发送通知
(NOTIFICATION_ID, mNotification);
}
}
} catch (IOException e) {
tackTrace();
} finally {
if (bis != null) {
try {
();
} catch (IOException e) {
tackTrace();
}
}
if (bos != null) {
try {
();
} catch (IOException e) {
tackTrace();
}
}
}
//下载文件完成以后,执行以下操作
Intent installIntent = new Intent();
/**启动系统服务的Activity,用于显示用户的数据。
比较通用,会根据用户的数据类型打开相应的Activity。
*/
ion(_VIEW);
aAndType(le(new File(destPath)), "application/e-archive");
//实例化延时的Activity
//实例化延时的Activity
PendingIntent pendingIntent = ivity(mContext, REQUEST_CODE, installIntent, _ONE_SHOT);
tentTitle("文件下载完毕!")
.setSmallIcon(_sys_download_done)
.setContentText("已下载100%")
.setContentIntent(pendingIntent);
//点击通知图标,自动消失
Notification notification = ();
|= _AUTO_CANCEL;
(NOTIFICATION_ID, notification);
}
//初始化通知
private void initNotification() {
builder = new r(mContext);
//自定义的Notification
remoteViews = new RemoteViews(getPackageName(), _main_notification);
Bitmap largeIcon = Resource(getResources(), _sys_download_anim0);
ker("开始下载apk文件")
.setSmallIcon(_sys_download_anim5)
.setLargeIcon(largeIcon)
.setContent(remoteViews);
//实例化通知对象
mNotification = ();
//获取通知的管理器
manager = (NotificationManager) getSystemService(CATION_SERVICE);
}
}
版权声明:本文标题:App的版本迭代更新 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1702511442a10848.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论