简单做一个APP检测更新的小工具,有点粗糙。不能断点续传,只用为个人觉得没有必要,自己可根据大家的想法添加更多的功能,这里只是为了想我一样的初学者和比较简约的人所提供。
效果如下:
基本思路先理一理,以我的实际开发为例:首先当然要一个网络去请求我们的服务器,获得仓库中的apk版本信息和下载路径,在和自己当前的版本号进行比较,当自己的版本号小于仓库中的版本号,就提示用户下载,在根据更新等级进行下载强度操作(是否强制下载还是什么的)。在下的时候监听文件下载的Progress 来更新 notification中的进度条和进度信息等待,当下载完成后,可让用户点击(关闭通知就行)进入到安装apk界面,这样就算完成了。
本次使用了okhttp (okgo)网络框架进行下载文件,这里确定下载的是apk就不多过滤信息了,直接下载安装。
首先想到的是要用一个单例来做下载管理,我们需要传入一个context 。
Context context;
private static final AppUpdateService INSTANCE = new AppUpdateService();
private static class LazyHolder {
public static AppUpdateService getThis(Context context) {
INSTANCE.context = context;
return INSTANCE;
}
}
public static AppUpdateService getInstance(Context context) {
return LazyHolder.getThis(context);
}
初始化Notification 、notificationCompat.Builder和notificationManager
//初始化通知
private void initNotification() {
notificationManager = (NotificationManager) MyApplication.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
builder = new NotificationCompat.Builder(MyApplication.getInstance());
builder.setContentTitle("正在更新...") //设置通知标题
.setSmallIcon(R.mipmap.ic_launcher_round)
.setLargeIcon(BitmapFactory.decodeResource(MyApplication.getInstance().getResources(), R.mipmap.ic_launcher_round)) //设置通知的大图标
.setDefaults(Notification.DEFAULT_LIGHTS) //设置通知的提醒方式: 呼吸灯
.setPriority(NotificationCompat.PRIORITY_MAX) //设置通知的优先级:最大
.setAutoCancel(false)//设置通知被点击一次是否自动取消
.setContentText("下载进度:" + "0%")
.setProgress(100, 0, false);
notification = builder.build();//构建通知对象
}
下载更新通知核心代码,可更新直接的选择是否让用户点击后安装,还是直接安装。
public void download(String url, String filePath) {
MyApplication.getInstance().okGo.post(url).execute(new FileCallback() {
@Override
public void onSuccess(Response response) {
File file = response.body();
Log.e("update", "onSuccess: 下载完成" + file.getPath() + file.getName());
builder.setContentTitle("下载完成")
.setContentText("点击安装")
.setAutoCancel(true);//设置通知被点击一次是否自动取消
//点击安装代码块
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.parse("file://" + file.toString()), "application/vnd.android.package-archive");
PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 0);
notification = builder.setContentIntent(pi).build();
notificationManager.notify(1, notification);
//自动安装
// installApk(file);
}
//下载进度
@Override
public void downloadProgress(Progress progress) {
super.downloadProgress(progress);
Log.e("update", "downloadProgress: " + progress.fraction);
builder.setProgress(100, (int) (progress.fraction * 100), false);
builder.setContentText("下载进度:" + (int) (progress.fraction * 100) + "%");
notification = builder.build();
notificationManager.notify(1, notification);
}
@Override
public void onStart(Request request) {
super.onStart(request);
initNotification();
}
@Override
public void onError(Response response) {
super.onError(response);
Toast.makeText(context, "下载错误", Toast.LENGTH_SHORT).show();
}
});
}
安装apk
private void installApk(File file) {
//新下载apk文件存储地址
File apkFile = file;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.parse("file://" + apkFile.toString()), "application/vnd.android.package-archive");
context.startActivity(intent);
notificationManager.cancel(1);//取消通知
}
获取本地版本号
public int getVersionCode() {
PackageManager packageManager = context.getPackageManager();
int versionCode = 0;
try {
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
versionCode = packageInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return versionCode;
}
获取更新信息,自己根据自己的网络框架修改。
public void getUpdate() {
MyApplication.getInstance().okGo.post("*****").execute(new StringCallback() {
@Override
public void onSuccess(Response response) {
ApkSer apkSer = new ApkSer();
apkSer = new Gson().fromJson(response.body(), ApkSer.class);
CustomDialog("有新版本需要更新", 3, apkSer.getUpdatePath());
}
});
}
提示框信息提示用户。
public void CustomDialog(final String cominit, int updatelv, final String url) {
CustomDialog.Builder dialog = new CustomDialog.Builder(context);
dialog.setTitle("版本更新")
.setMessage(cominit);
dialog.setCancelable(false);
//更新等级 0:普通 1:重点bug修复更新 2:新功能更新 3:配置更新 4:特殊更新,强制用户更新
if (updatelv == 0 || updatelv == 2) {
dialog.setCancelBtn("下次提醒", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
dialog.dismiss();
}
});
} else {
dialog.setCanceledOnTouchOutside(false);
}
dialog.setCancelBtn("重点更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
Toast.makeText(context,"已添加到下载任务",Toast.LENGTH_SHORT).show();
dialog.dismiss();
download(url, "path");
}
});
dialog.create().show();
}
版本信息对象字段
public class ApkSer { public String type; //更新内容类型,apk安装包 res资源 public String apkName; public String packageName; //apk包名 public Long versionCode; //库版本号 public String versionName; //库版本名称 public int important; //更新等级 0:普通 1:重点bug修复更新 2:新功能更新 3:配置更新 4:特殊更新,强制用户更新 public String commit; //更新内容 public String updatePath; //更新下载地址 public String updateTime; //更新时间 }
完整代码如下,欢迎大家点评。
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
import com.google.gson.Gson;
import com.hx.view.widget.CustomDialog;
import com.lzy.okgo.callback.FileCallback;
import com.lzy.okgo.callback.StringCallback;
import com.lzy.okgo.model.Progress;
import com.lzy.okgo.model.Response;
import com.lzy.okgo.request.base.Request;
import com.pay.library.config.AppConfig;
import com.yhx.loan.R;
import com.yhx.loan.base.MyApplication;
import java.io.File;
public class AppUpdateService {
private NotificationManager notificationManager;
private Notification notification; //下载通知进度提示
private NotificationCompat.Builder builder;
private boolean flag = false; //进度框消失标示 之后发送通知
public static boolean isUpdate = false; //是否正在更新
Context context;
private static final AppUpdateService INSTANCE = new AppUpdateService();
private static class LazyHolder {
public static AppUpdateService getThis(Context context) {
INSTANCE.context = context;
return INSTANCE;
}
}
public static AppUpdateService getInstance(Context context) {
return LazyHolder.getThis(context);
}
private AppUpdateService() {
}
public void getUpdate() {
MyApplication.getInstance().okGo.post(AppConfig.updateAPP_url)
.upJson("{'package':'包名'}")
.execute(new StringCallback() {
@Override
public void onSuccess(Response response) {
ApkSer apkSer = new ApkSer();
apkSer = new Gson().fromJson(response.body(), ApkSer.class);
CustomDialog("有新版本需要更新", 3, apkSer.getUpdatePath());
}
});
}
public void CustomDialog(final String cominit, int updatelv, final String url) {
CustomDialog.Builder dialog = new CustomDialog.Builder(context);
dialog.setTitle("版本更新")
.setMessage(cominit);
dialog.setCancelable(false);
//更新等级 0:普通 1:重点bug修复更新 2:新功能更新 3:配置更新 4:特殊更新,强制用户更新
if (updatelv == 0 || updatelv == 2) {
dialog.setCancelBtn("下次提醒", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
dialog.dismiss();
}
});
} else {
dialog.setCanceledOnTouchOutside(false);
}
dialog.setCancelBtn("重点更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
Toast.makeText(context,"已添加到下载任务",Toast.LENGTH_SHORT).show();
dialog.dismiss();
download(url, "path");
}
});
dialog.create().show();
}
//初始化通知
private void initNotification() {
notificationManager = (NotificationManager) MyApplication.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
builder = new NotificationCompat.Builder(MyApplication.getInstance());
builder.setContentTitle("正在更新...") //设置通知标题
.setSmallIcon(R.mipmap.ic_launcher_round)
.setLargeIcon(BitmapFactory.decodeResource(MyApplication.getInstance().getResources(), R.mipmap.ic_launcher_round)) //设置通知的大图标
.setDefaults(Notification.DEFAULT_LIGHTS) //设置通知的提醒方式: 呼吸灯
.setPriority(NotificationCompat.PRIORITY_MAX) //设置通知的优先级:最大
.setAutoCancel(false)//设置通知被点击一次是否自动取消
.setContentText("下载进度:" + "0%")
.setProgress(100, 0, false);
notification = builder.build();//构建通知对象
}
public void download(String url, String filePath) {
MyApplication.getInstance().okGo.post(url).execute(new FileCallback() {
@Override
public void onSuccess(Response response) {
File file = response.body();
Log.e("update", "onSuccess: 下载完成" + file.getPath() + file.getName());
builder.setContentTitle("下载完成")
.setContentText("点击安装")
.setAutoCancel(true);//设置通知被点击一次是否自动取消
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.parse("file://" + file.toString()), "application/vnd.android.package-archive");
PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 0);
notification = builder.setContentIntent(pi).build();
notificationManager.notify(1, notification);
//自动安装
// installApk(file);
}
//下载进度
@Override
public void downloadProgress(Progress progress) {
super.downloadProgress(progress);
Log.e("update", "downloadProgress: " + progress.fraction);
builder.setProgress(100, (int) (progress.fraction * 100), false);
builder.setContentText("下载进度:" + (int) (progress.fraction * 100) + "%");
notification = builder.build();
notificationManager.notify(1, notification);
}
@Override
public void onStart(Request request) {
super.onStart(request);
initNotification();
}
@Override
public void onError(Response response) {
super.onError(response);
Toast.makeText(context, "下载错误", Toast.LENGTH_SHORT).show();
}
});
}
/**
* 安装apk
*/
private void installApk(File file) {
//新下载apk文件存储地址
File apkFile = file;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.parse("file://" + apkFile.toString()), "application/vnd.android.package-archive");
context.startActivity(intent);
notificationManager.cancel(1);//取消通知
}
/**
* 获取本地版本号
*
* @return
*/
public int getVersionCode() {
PackageManager packageManager = context.getPackageManager();
int versionCode = 0;
try {
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
versionCode = packageInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return versionCode;
}
}
https://blog.csdn.net/xiaoyu940601/article/details/54406725
最后发现对于file:///storage/emulated/0/xxx/...的文件来说。我们的filepaths.xml中的path要有xxx路径,这样才能找到文件不报错。也不是太懂。反正最后是解决报错的问题了。