<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!--未知来源安装权限 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
/**
* 我这里用了别人的请求权限框架 AndPemission
* Github地址:https://github.com/yanzhenjie/AndPermission
*/
if (AndPermission.hasPermissions(this, Permission.Group.STORAGE)) {
// 有存储权限, 开启服务下载
startService();
} else {
// 无存储权限, 则请求权限
AndPermission.with(this).runtime().permission(Permission.Group.STORAGE).onGranted(data -> {
if (CollectionUtils.isEmpty(data)) {
return;
}
// 用户允许了权限, 开启服务下载
startService();
}).start();
}
Intent intent = new Intent(this, ApkDownloadService.class);
// 给Service传值, 我这里直接把整个Bean传过去了, 其实只需要个下载地址和当前下载的版本号
intent.putExtra(IntentKey.BEAN, newVersionBean);
if (Build.VERSION.SDK_INT >= 26) {
// Android8.0适配
startForegroundService(intent);
} else {
startService(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// android8.0适配: 被启动的Service创建服务后的五秒内要调用startForground(0, new Notification())
// 如果不调用或调用时间超过5秒会抛出一个ANR
// 调用startForground用到了通知,android8.0通知又必须要设置通知渠道
// 创建通知渠道并运行服务到前台
createNotificationChannel();
// 获取Intent传值信息
mVersionInfo = (QueryVersionsVo) intent.getSerializableExtra(IntentKey.BEAN);
// 开始异步任务下载Apk
downloadApk();
return super.onStartCommand(intent, flags, START_STICKY);
}
private void createNotificationChannel() {
// 这里的id输入自己的项目的包名
String ID = "com.***.***";
String NAME = "Channel One";
Intent intent = new Intent(this, HomeActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
// 创建服务对象
NotificationCompat.Builder notification;
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Android8.0要求必须创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(ID, NAME, NotificationManager.IMPORTANCE_HIGH);
channel.enableLights(false);
channel.setShowBadge(false);
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
manager.createNotificationChannel(channel);
}
notification = new NotificationCompat.Builder(this, ID);
notification.setContentTitle("更新提示")
.setContentText("正在下载最新版本..")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher_esp)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_esp))
.setContentIntent(pendingIntent)
.build();
Notification notification1 = notification.build();
startForeground(1, notification1);
}
public void downloadApk() {
// 设置最新安装包名称
StringBuilder builder = new StringBuilder("your_app_name-");
if (!TextUtils.isEmpty(mVersionInfo.getPushVersions())) {
builder.append(mVersionInfo.getPushVersions());
} else {
builder.append(System.currentTimeMillis());
}
builder.append(".apk");
// 设置apk所在目录
File baseFile = ContextHolder.getContext().getExternalFilesDir("yc_team");
if (!baseFile.exists()) {
baseFile.mkdirs();
}
mApkFile = new File(baseFile.getPath(), builder.toString());
// 最终apk目录 文件管理-手机存储-Android-data-应用包名-yc_team-***.apk
if (mApkFile.exists()) {
mApkFile.delete();
}
// 开始异步下载
mAsyncTask = new DownApkAsyncTask();
mAsyncTask.execute();
}
@SuppressLint("StaticFieldLeak")
private class DownApkAsyncTask extends AsyncTask<Void, Long, Void> {
@Override
protected Void doInBackground(Void... voids) {
HttpURLConnection httpConnection = null;
InputStream is = null;
FileOutputStream fos = null;
int updateTotalSize;
URL url;
try {
url = new URL(mVersionInfo.getUrl());
httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.setConnectTimeout(60000);
httpConnection.setReadTimeout(60000);
if (httpConnection.getResponseCode() != 200) {
return null;
}
updateTotalSize = httpConnection.getContentLength();
// mApkFile.createNewFile();
is = httpConnection.getInputStream();
fos = new FileOutputStream(mApkFile, false);
byte[] buffer = new byte[4096];
int readSize;
int currentSize = 0;
while ((readSize = is.read(buffer)) > 0) {
fos.write(buffer, 0, readSize);
currentSize += readSize;
int finalCurrentSize = currentSize;
int finalUpdateTotalSize = updateTotalSize;
// 这里可以发消息实时通知进度
handler.post(() -> {
// 用finalCurrentSize和finalUpdateTotalSize计算出进度
});
}
// 下载完成, 通知安装
installApk();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (httpConnection != null) {
httpConnection.disconnect();
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
/**
* 我这里是发了个EventBus,通知Activity可以安装Apk了
*/
private void installApk() {
handler.post(() -> {
EventBusMode mode = new EventBusMode(EventBusType.ESP_DOWNLOAD_APK_SUCCESS);
mode.setTempStr(mApkFile.getAbsolutePath());
EventBus.getDefault().post(mode);
// 下载完成,关闭当前服务
stopSelf();
});
}
/**
* 在Service结束时, 停止异步下载
*/
@Override
public void onDestroy() {
if (mAsyncTask != null) {
mAsyncTask.cancel(true);
}
super.onDestroy();
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// Android8.0之前,直接安装Apk
installApk();
return;
}
boolean haveInstallPermission = getPackageManager().canRequestPackageInstalls();
if (!haveInstallPermission) {
// 权限没有打开则提示用户去手动打开
Uri packageURI = Uri.parse("package:" + getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
startActivityForResult(intent, 1001);
}
/**
* 未知来源安装权限申请回调
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_OK) {
return;
}
if (requestCode == 1001 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 未知来源安装应用权限开启
boolean haveInstallPermission = getPackageManager().canRequestPackageInstalls();
if (haveInstallPermission) {
installApk();
}
}
}
/**
* 安装最新Apk
*/
private void installApk() {
// Service发的通知中的文件绝对路径
File file = new File(mNewApkFilePath);
try {
// 这里有文件流的读写,需要处理一下异常
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//如果SDK版本>=24,即:Build.VERSION.SDK_INT >= 24
String packageName = context.getApplicationContext().getPackageName();
String authority = new StringBuilder(packageName).append(".provider").toString();
Uri uri = FileProvider.getUriForFile(context, authority, file);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
} else {
Uri uri = Uri.fromFile(file);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
}
context.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
// 安装的时候会进行版本自动检测,更新版本小于已有版本,是会走当前异常的,注意!
}
}