自动更新功能对于一个APP来说是必备的功能,特别是对于未投放市场下载的APP,每次都让用户删掉原来的,再下载新的版本,肯定是不合适的。
实现思路:
- 后台提供接口,返回服务端版本号serviceVersion以及APK下载地址
- 前端对接接口,用拿到的serviceVersion和APK配置的localVersion比较,如果serviceVersion>localVersion则提示可以更新,通过获取的APK下载地址下载,然后通过api打开安装完成更新。
注意:
- localVersion笔者使用的是versionCode,可以再AndroidManifest中配置,通过java代码获取。笔者与后台约定了Code的规则,采用更新时间编辑,例如2018年8月2号,则versionCode=“180802”
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.mintu.dcdb"
android:versionCode="180802"
android:versionName="3.6">
/**
* 获取当前本地apk的版本号
* @param mContext
* @return
*/
public static int getVersionCode(Context mContext) {
int versionCode = 0;
try {
//获取软件版本号,对应AndroidManifest.xml下android:versionCode
versionCode = mContext.getPackageManager().
getPackageInfo(mContext.getPackageName(), 0).versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return versionCode;
}
- 对于Android7.0以上的手机,打开附件做了改变,无法使用以往的uri发布意图,详情可见笔者之前的一篇文章。Android7.0以上版本打开附件失败问题
- 本文的文件下载、附件打开方法使用的是笔者封装的OkHttp3工具类,使用者可以自己随意替换。只要将APK从url上下载下来,用API打开即可。
核心代码:
/***
* 检查是否更新版本
*/
private void checkVersion() {
if (Integer.parseInt((String) sharedPreferencesUtil.getData(Constant.VERSION_CODE_LOCAL,"")) < Integer
.parseInt(CommonUtil.getInstance().isNull(sharedPreferencesUtil.getData(Constant.VERSION_CODE,"")) ? "0"
: (String) sharedPreferencesUtil.getData(Constant.VERSION_CODE,""))) {
showDialog(new DownLoadBroadCastReceiver());
}else{
if(!isAutoUpdate) Toast.makeText(activity, "未检查到新版本", Toast.LENGTH_SHORT).show();
}
}
/***
* 开线程下载
*/
public void createThread(final String downUrl) {
final Message message = new Message();
if (SystemUtils.isNetworkAvailable(getApplicationContext())) {
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpUtil.getInstance().download(downUrl, newApkUrl, file_name, new OkHttpUtil.OnDownloadListener() {
@Override
public void onDownloadSuccess(File downfile, File file) {
hand();
stopSelf();
}
@Override
public void onDownloading(int progress, File file) {
LogUtil.i("update progress---"+progress);
broadCast.putExtra("download", progress);
sendBroadcast(broadCast);
}
@Override
public void onDownloadFailed(String error) {
}
});
} catch (Exception e) {
}
}
}).start();
} else {
Toast.makeText(getApplicationContext(), "网络无连接,请稍后下载!",
Toast.LENGTH_SHORT).show();
}
}
/**
* 打开附件的方法
* @param f
* @param context
*/
public void openFile(File f, Context context) {
Log.i(LOGTAG, "正在打开附件打--------" + f.getName()+"。附件大小为"+f.length());
try {
String end = f.getName().substring(f.getName().lastIndexOf(".")
+ 1, f.getName().length()).toLowerCase();
if(end.equals("amr")){
AudioRecoderUtils.getInstance().playerStart(f.getAbsolutePath());
}else {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.addCategory("android.intent.category.DEFAULT");
/* 调用getMIMEType()来取得MimeType */
String type = getMIMEType(f);
/* 设置intent的file与MimeType */
if(Build.VERSION.SDK_INT>=24){
Uri contenturi=FileProvider.getUriForFile(context, "com.mintu.dcdb.fileprovider",f);
intent.setDataAndType(contenturi,type);
intent.putExtra(MediaStore.EXTRA_OUTPUT, contenturi);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}else{
intent.setDataAndType(Uri.fromFile(f), type);
}
context.startActivity(intent);
}
} catch (Exception e) {
// Toast.makeText(context,"打开附件---"+f.getName()+",发生了错误",Toast.LENGTH_SHORT).show();
Log.e(LOGTAG, "打开附件" + f.getName() + "报错了,错误是-----" + e.getMessage());
}
}
项目中全量更新源码:
package com.mintu.dcdb.util.updateAppUtil;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.text.Html;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.google.gson.Gson;
import com.mintu.dcdb.R;
import com.mintu.dcdb.config.Constant;
import com.mintu.dcdb.config.RequestUrl;
import com.mintu.dcdb.main.bean.UpdateBean;
import com.mintu.dcdb.main.view.UpdateDialog;
import com.mintu.dcdb.util.LogUtil;
import com.wusy.wusylibrary.base.BaseActivity;
import com.wusy.wusylibrary.util.CommonUtil;
import com.wusy.wusylibrary.util.OkHttpUtil;
import com.wusy.wusylibrary.util.SharedPreferencesUtil;
import java.io.IOException;
import java.util.ArrayList;
import okhttp3.Call;
/**
* Created by XIAO RONG on 2018/7/19.
*/
public class UpdateAppUtil {
private SharedPreferencesUtil sharedPreferencesUtil;
private BaseActivity activity;
private ProgressDialog m_pDialog;
private boolean isAutoUpdate=false;
public UpdateAppUtil(BaseActivity activity,boolean isAutoUpdate){
sharedPreferencesUtil=SharedPreferencesUtil.getInstance(activity);
this.activity=activity;
this.isAutoUpdate=isAutoUpdate;
}
public void updateApp(){
String url = RequestUrl.getInstance().getUpdateUrl((String) SharedPreferencesUtil.getInstance(activity).getData(Constant.VERSION_CODE, ""));
OkHttpUtil.getInstance().asynGet(url, new OkHttpUtil.ResultCallBack() {
@Override
public void successListener(Call call, String responseStr) {
LogUtil.i("update result str--"+responseStr);
Gson gson = new Gson();
UpdateBean bean = gson.fromJson(responseStr, UpdateBean.class);
sharedPreferencesUtil.saveData(Constant.VERSION_CODE,bean.getVersion());
sharedPreferencesUtil.saveData(Constant.VERSION_URL,bean.getUrl());
sharedPreferencesUtil.saveData(Constant.VERSION_CONTENT,bean.getContent());
sharedPreferencesUtil.saveData(Constant.VERSION_PATCH_FILE_PATH,bean.getPatchFilePath());
sharedPreferencesUtil.saveData(Constant.VERSION_IS_PATCH_FILE_PATH,bean.getIsPatchFilePath());
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
checkVersion();
}
});
}
@Override
public void failListener(Call call, IOException e) {
}
});
}
/***
* 检查是否更新版本
*/
private void checkVersion() {
if (Integer.parseInt((String) sharedPreferencesUtil.getData(Constant.VERSION_CODE_LOCAL,"")) < Integer
.parseInt(CommonUtil.getInstance().isNull(sharedPreferencesUtil.getData(Constant.VERSION_CODE,"")) ? "0"
: (String) sharedPreferencesUtil.getData(Constant.VERSION_CODE,""))) {
showDialog(new DownLoadBroadCastReceiver());
}else{
if(!isAutoUpdate) Toast.makeText(activity, "未检查到新版本", Toast.LENGTH_SHORT).show();
}
}
private void showDialog(final BroadcastReceiver receiver) {
final Dialog alert = new UpdateDialog(activity, R.style.MyDialogStyle);
alert.setContentView(R.layout.upgrade_dialog);
TextView tvView = (TextView) alert.findViewById(R.id.upgradeText);
if (!CommonUtil.getInstance().isNull(sharedPreferencesUtil.getData(Constant.VERSION_CONTENT, ""))) {
tvView.setText(Html.fromHtml((String) sharedPreferencesUtil.getData(Constant.VERSION_CONTENT, "")));
}
tvView.setWidth(activity.getWindowManager().getDefaultDisplay().getWidth() * 3 / 4);
Button sureBtn = (Button) alert.findViewById(R.id.btn_sure);
Button cancleBtn = (Button) alert.findViewById(R.id.btn_cancle);
sureBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ArrayList<String> actionList = new ArrayList<>();
actionList.add(Constant.DOWNLOAD_ACTION);
activity.addBroadcastAction(actionList, receiver);
Intent updateIntent = new Intent(activity,
UpdateService.class);
updateIntent.putExtra("app_name", activity.getResources()
.getString(R.string.app));
activity.startService(updateIntent);
alert.dismiss();
}
});
cancleBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
alert.dismiss();
}
});
alert.show();
}
class DownLoadBroadCastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case Constant.DOWNLOAD_ACTION:
int progress = intent.getIntExtra("download", 0);
showProgress(progress);
break;
default:
break;
}
}
}
private void showProgress(Integer progress) {
if (m_pDialog == null) {
createProgressDialog("正在更新请稍后...");
}
m_pDialog.setProgress(progress);
if (progress == 100) {
m_pDialog.dismiss();
}
}
private void createProgressDialog(String title) {
// 创建ProgressDialog对象
m_pDialog = new ProgressDialog(activity);
// 设置进度条风格,风格为长形
m_pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
// 设置ProgressDialog 标题
m_pDialog.setTitle(title);
// 设置ProgressDialog 进度条进度
m_pDialog.setProgress(100);
// 设置ProgressDialog 的进度条是否不明确
m_pDialog.setIndeterminate(false);
// 设置ProgressDialog 是否可以按退回按键取消
m_pDialog.setCancelable(true);
// 设置点击进度对话框外的区域对话框不消失
m_pDialog.setCanceledOnTouchOutside(false);
// 让ProgressDialog显示
m_pDialog.show();
}
}
package com.mintu.dcdb.util.updateAppUtil;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;
import com.mintu.dcdb.config.Constant;
import com.mintu.dcdb.util.LogUtil;
import com.mintu.dcdb.util.SystemUtils;
import com.wusy.wusylibrary.util.CommonUtil;
import com.wusy.wusylibrary.util.OkHttpUtil;
import com.wusy.wusylibrary.util.SharedPreferencesUtil;
import java.io.File;
/**
*
* Filename: UpdateService.java Description: 今天修改了当增量包合成失败的时候,重新下载整个最新的apk
* Company: minto
*
* @author: chjr
* @version: 2.8.0 Create at: 2016年5月20日 下午3:25:52
*
*/
public class UpdateService extends Service {
private final int TIMEOUT = 10 * 1000;// 超时
private final int DOWN_OK = 1;
private final int DOWN_ERROR = 0;
private String app_name;
private String file_name;
private NotificationManager notificationManager;
private Notification notification;
private Intent updateIntent;
private PendingIntent pendingIntent;
private int notification_id = 0;
ProgressDialog m_pDialog;
private File updateDir = new File(Constant.FILEDIR);
File updateFile;
SharedPreferencesUtil vsPreference;
String packageName = "com.minto.workhi";
String patchUrl = Constant.FILEDIR;
String newApkUrl = Constant.FILEDIR;
private String downUrl = "";
private int flag = 2;
private Intent broadCast;
@Override
public IBinder onBind(Intent arg0) {
stopSelf();
return null;
}
@Override
public void onCreate() {
super.onCreate();
vsPreference = SharedPreferencesUtil.getInstance(getApplicationContext());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (CommonUtil.getInstance().isNull(intent)) {
stopSelf();
Log.d("UpService", intent + "--------------------");
} else {
app_name = intent.getStringExtra("app_name");
file_name = app_name + ".apk";
patchUrl = patchUrl + app_name + ".patch";
// 创建文件
CommonUtil.getInstance().createFile(app_name + ".patch");
CommonUtil.getInstance().createFile(file_name);
updateFile = new File(updateDir + "/" + app_name + ".patch");
if(flag==1)flag=2;
broadCast = new Intent();
broadCast.setAction(Constant.DOWNLOAD_ACTION);
broadCast.putExtra("download", 0);
sendBroadcast(broadCast);
downFile();
}
return super.onStartCommand(intent, flags, startId);
}
/***
* 开线程下载
*/
public void createThread(final String downUrl) {
final Message message = new Message();
if (SystemUtils.isNetworkAvailable(getApplicationContext())) {
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpUtil.getInstance().download(downUrl, newApkUrl, file_name, new OkHttpUtil.OnDownloadListener() {
@Override
public void onDownloadSuccess(File downfile, File file) {
hand();
stopSelf();
}
@Override
public void onDownloading(int progress, File file) {
LogUtil.i("update progress---"+progress);
broadCast.putExtra("download", progress);
sendBroadcast(broadCast);
}
@Override
public void onDownloadFailed(String error) {
}
});
} catch (Exception e) {
}
}
}).start();
} else {
Toast.makeText(getApplicationContext(), "网络无连接,请稍后下载!",
Toast.LENGTH_SHORT).show();
}
}
/**
* 根据类型下载不同的文件
*
* @exception
* @since 1.0.0
*/
protected void downFile() {
// 下载增量包路径
if (1 == flag) {
downUrl = (String) vsPreference.getData(Constant.VERSION_PATCH_FILE_PATH,"");
} else if (2 == flag) {
downUrl = (String) vsPreference.getData(Constant.VERSION_URL,"");
}
createThread(downUrl);
}
/**
* 根据不同的包安装
*
* @exception
* @since 1.0.0
*/
protected void hand() {
// 增量合成方法
if (1 == flag) {
// ApkUpdate update = new ApkUpdate(getApplicationContext(),
// packageName, patchUrl, newApkUrl);
// // 检查低版本的apk 是否存在
// if (update.isOldApkExist()) {
// // 判断是否成功合成新的apk
// if (update.newApkGet()) {
// update.installApk();
// delData();
// } else {
// // Toast.makeText(getApplicationContext(),
// // "下载失败!:" + newApkUrl + "增量包地址:" + patchUrl,
// // Toast.LENGTH_SHORT).show();
// flag = 2;
// downFile();
// }
// }
}
// 整包安装方法
else if (2 == flag) {
File apkFile = new File(newApkUrl, file_name);
OkHttpUtil.getInstance().openFile(apkFile,this);
}
}
}
网友评论