-
记录一次最近用Retrofit+RxJava实现的版本更新
-
点击更新,状态栏带有下载进度
一、先看一下实现之后的具体使用方式:
1.检测是否有更新:
/**
* 版本更新检测
*/
private void checkAppUpdate() {
UpdateCheckUtil.getInstance().checkUpdate(new UpdateCheckUtil.AppUpdateCallback() {
@Override
public void result(boolean update, AppUpdateResponseBean bean) {
if (null != tvCurrentVersion) {
tvCurrentVersion.setText(update ? "有新版本,点击更新" : String.format(getString(R.string.str_current_version), AppUtils.getAppVersionName()));
}
hasUpdate = update;
if (update) {
mUpdateBean = bean;
}
}
});
}
2.有新版本,点击更新:
case R.id.check_update_ll:
if (hasUpdate) {
if (UpdateManager.isUpdateIng) {
ToastUtils.showShort("正在下载中");
return;
} else {
if (AppInstallUtil.isInstallApkIsExists(this)) {
AppInstallUtil.installApk(this);
return;
}
}
if (!TextUtils.isEmpty(mUpdateBean.DownloadUrl)) {
UpdateService.startUpdateService(MineActivity.this, mUpdateBean.DownloadUrl, AppInstallUtil.getInstallPath(MineActivity.this));
ToastUtils.showShort("开始下载");
return;
}
}
ToastUtils.showShort(getString(R.string.str_current_is_new_version));
break;
二、上面的代码中涉及到的类如下:
1.UpdateCheckUtil:
/**
* Des: 版本更新检测
* Created by kele on 2020/8/5.
* E-mail:[email protected]
*/
public class UpdateCheckUtil {
private final Service mService;
private UpdateCheckUtil() {
mService = RetrofitManager.getInstance().createService(Service.class);
}
private static class UpdateUtilHolder {
private static final UpdateCheckUtil INSTANCE = new UpdateCheckUtil();
}
public static UpdateCheckUtil getInstance() {
return UpdateUtilHolder.INSTANCE;
}
/**
* 检测新版本
*
* @param cb
*/
public void checkUpdate(final AppUpdateCallback cb) {
if (null == cb) {
return;
}
final AppUpdateRequestBean b = RequestBeanFactory.getInstance().getAppUpdateRB();
Observable> update = mService.update(b);
update
.compose(ResponseTransformer.handleResult())
.compose(SchedulerProvider.getInstance().applySchedulers())
.subscribe(new Consumer() {
@Override
public void accept(AppUpdateResponseBean bean) throws Exception {
if (null == cb) {
return;
}
if (null != bean) {
int vCode = AppUtils.getAppVersionCode();
boolean update = bean.VersionCode > vCode;
cb.result(update, bean);
return;
}
cb.result(false, null);
}
}, new Consumer() {
@Override
public void accept(Throwable throwable) throws Exception {
if (null == cb) {
return;
}
cb.result(false, null);
}
});
}
/**
* 版本更新回调
*/
public interface AppUpdateCallback {
/**
* 检测结果
*
* @param update 是否有新版本
* @param bean 新版本实体
*/
void result(boolean update, AppUpdateResponseBean bean);
}
/**
* 版本更新检测接口
*/
private interface Service {
/**
* 检测版本更新
*
* @param rBean 请求实体
* @return
*/
@POST(UrlUtil.AppUpdate)
Observable> update(
@Body AppUpdateRequestBean rBean
);
}
}
2.UpdateService:
/**
* Des: 版本更新服务
* Created by kele on 2020/7/30.
* E-mail:[email protected]
*/
public class UpdateService extends IntentService {
private static final String TAG = UpdateService.class.getSimpleName();
private static final int NOTIFICATION_ID = 1001;
private static final String ACTION_DOWNLOAD = "intent_service.action.download";
private static final String DOWNLOAD_URL = "downloadUrl";
private static final String APK_PATH = "apkPath";
private CompositeDisposable cd = new CompositeDisposable();
private NotificationCompat.Builder builder;
private NotificationManager notificationManager;
public UpdateService() {
super(TAG);
}
/**
* 开启下载服务
*
* @param context 上下文
* @param url 下载地址
* @param apkPath apk地址
*/
public static void startUpdateService(Context context, String url, String apkPath) {
if (null == context) {
return;
}
if (TextUtils.isEmpty(url) || TextUtils.isEmpty(apkPath)) {
return;
}
Intent intent = new Intent(context, UpdateService.class);
intent.setAction(ACTION_DOWNLOAD);
intent.putExtra(DOWNLOAD_URL, url);
intent.putExtra(APK_PATH, apkPath);
context.startService(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
LogUtils.e(TAG, "---onHandleIntent");
if (intent != null) {
String action = intent.getAction();
if (ACTION_DOWNLOAD.equals(action)) {
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
builder = new NotificationCompat.Builder(this, CommonApp.CHANNEL_ID)
.setContentTitle("开始下载")
.setContentText("版本更新")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.logo)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.logo))
.setAutoCancel(true);
notificationManager.notify(NOTIFICATION_ID, builder.build());
String url = intent.getStringExtra(DOWNLOAD_URL);
String apkPath = intent.getStringExtra(APK_PATH);
handleUpdate(url, apkPath);
}
}
}
/**
* 开始下载
*
* @param url
* @param apkPath
*/
private void handleUpdate(String url, String apkPath) {
//订阅下载进度
subscribeEvent();
UpdateManager.getInstance().downloadApk(this, url, apkPath, cd);
}
/**
* 下载进度订阅
*/
private void subscribeEvent() {
RxBus.getDefault().toObservable(UpdateBean.class)
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
cd.add(d);
}
@Override
public void onNext(UpdateBean updateBean) {
int progress = (int) Math.round(updateBean.getBytesReaded() / (double) updateBean.getTotal() * 100);
builder
.setContentInfo(String.valueOf(progress) + "%")
.setProgress(100, progress, false);
LogUtils.e(TAG, "---progress=" + progress);
notificationManager.notify(NOTIFICATION_ID, builder.build());
if (updateBean.getBytesReaded() >= updateBean.getTotal()) {
notificationManager.cancel(NOTIFICATION_ID);
}
}
@Override
public void onError(Throwable e) {
subscribeEvent();
}
@Override
public void onComplete() {
subscribeEvent();
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
LogUtils.e(TAG, "---onDestroy");
}
}
不要忘记manifest中注册Service:
3.UpdateManager:
/**
* Des: 版本更新管理
* Created by kele on 2020/7/30.
* E-mail:[email protected]
*/
public class UpdateManager {
private UpdateApiService mApi;
private UpdateManager() {
init();
}
public static class UpDateManagerHolder {
public static UpdateManager INSTANCE = new UpdateManager();
}
public static UpdateManager getInstance() {
return UpDateManagerHolder.INSTANCE;
}
/**
* 是否正在更新中
*/
public static boolean isUpdateIng;
private void init() {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.addNetworkInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());//对结果重新处理
return originalResponse
.newBuilder()
.body(new FileResponseBody(originalResponse))//将自定义的ResposeBody设置给它
.build();
}
})
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(UrlUtil.getBaseUrl())
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
mApi = retrofit.create(UpdateApiService.class);
}
/**
* 开始下载
*
* @param url
* @return
*/
public Observable down(String url) {
return mApi.down(url);
}
/**
* 是否需要更新,需要则下载
*
* @param context 上下文
* @param url 新版本地址
* @param apkPath 本地apk保存路径
* @param cd 订阅关系集合,在数据传输完毕时解除订阅
*/
public void downloadApk(final Context context, final String url, final String apkPath, final CompositeDisposable cd) {
down(url)
.map(new Function() {
@Override
public BufferedSource apply(ResponseBody responseBody) throws Exception {
return responseBody.source();//获取数据缓冲源
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
cd.add(d);
isUpdateIng = true;
}
@Override
public void onNext(BufferedSource bufferedSource) {
try {
writeFile(bufferedSource, new File(apkPath));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable e) {
unSubscribe(cd);
isUpdateIng = false;
}
@Override
public void onComplete() {
//安装apk
// Intent intent = new Intent(Intent.ACTION_VIEW);
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// intent.setDataAndType(Uri.fromFile(new File(apkPath)), "application/vnd.android.package-archive");
// context.startActivity(intent);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
Uri uri = FileProvider.getUriForFile(context, "你的包名.fileprovider", new File(apkPath));
AppUtils.installApp(uri);
} else {
AppUtils.installApp(apkPath);
}
unSubscribe(cd);
isUpdateIng = false;
}
});
}
/**
* 写入文件
*/
private void writeFile(BufferedSource source, File file) throws IOException {
if (!file.getParentFile().exists())
file.getParentFile().mkdirs();
if (file.exists())
file.delete();
BufferedSink bufferedSink = Okio.buffer(Okio.sink(file));
bufferedSink.writeAll(source);
bufferedSink.close();
source.close();
}
/**
* 解除订阅
*
* @param cd 订阅关系集合
*/
private void unSubscribe(CompositeDisposable cd) {
if (cd != null && !cd.isDisposed())
cd.dispose();
}
}
4.FileResponseBody:
/**
* 下载文件
* Created by lxf on 2017/3/3.
*/
public class FileResponseBody extends ResponseBody {
private Response originalResponse;//原结果
public FileResponseBody(Response originalResponse) {
this.originalResponse = originalResponse;
}
//返回内容类型
@Override
public MediaType contentType() {
return originalResponse.body().contentType();
}
//返回内容长度,没有则返回-1
@Override
public long contentLength() {
return originalResponse.body().contentLength();
}
//返回缓存源,类似于io中的BufferedReader
@Override
public BufferedSource source() {
return Okio.buffer(new ForwardingSource(originalResponse.body().source()) {
long bytesReaded = 0;
//返回读取到的长度
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
bytesReaded += bytesRead == -1 ? 0 : bytesRead;
// 通过RxBus发布进度信息
RxBus.getDefault().send(new UpdateBean(contentLength(), bytesReaded));
return bytesRead;
}
});
}
}
5.RxBus:
/**
* Retrofit实现的RXBus
*/
public class RxBus {
private static volatile RxBus mDefaultInstance;
private RxBus() {
}
public static RxBus getDefault() {
if (mDefaultInstance == null) {
synchronized (RxBus.class) {
if (mDefaultInstance == null) {
mDefaultInstance = new RxBus();
}
}
}
return mDefaultInstance;
}
private final Subject
6.AppInstallUtil:
/**
* Des: APP安装工具
* Created by kele on 2020/7/30.
* E-mail:[email protected]
*/
public class AppInstallUtil {
private static final String TAG = AppInstallUtil.class.getSimpleName();
/**
* 获取安装包文件
*
* @param context
* @return
*/
public static File getInstallApkFile(Context context) {
File file = new File(context.getExternalFilesDir("apk"), "***.apk");
return file;
}
/**
* 获取安装包文件地址
*
* @param context
* @return
*/
public static String getInstallPath(Context context) {
File installApkFile = getInstallApkFile(context);
if (null != installApkFile) {
LogUtils.e(TAG, "---getInstallPath=" + installApkFile.getAbsolutePath());
return installApkFile.getAbsolutePath();
}
return "";
}
/**
* 安装包文件是否存在
*
* @param context
* @return
*/
public static boolean isInstallApkIsExists(Context context) {
File installApkFile = getInstallApkFile(context);
if (null != installApkFile) {
if (installApkFile.isFile()) {
return installApkFile.exists();
}
}
return false;
}
/**
* 清除安装包文件
*
* @param context
* @return
*/
public static boolean clearInstallApkFile(Context context) {
File installApkFile = getInstallApkFile(context);
if (null != installApkFile) {
if (installApkFile.isFile()) {
if (installApkFile.exists()) {
boolean delete = installApkFile.delete();
LogUtils.e(TAG, "---delete=" + delete);
return delete;
}
}
}
return false;
}
/**
* 安装apk
*
* @param context
*/
public static void installApk(Context context) {
installApk(context, getInstallPath(context));
}
/**
* 安装apk
*
* @param context
* @param apkPath
*/
public static void installApk(Context context, String apkPath) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
Uri uri = FileProvider.getUriForFile(context, "你的包名.fileprovider", new File(apkPath));
AppUtils.installApp(uri);
} else {
AppUtils.installApp(apkPath);
}
}
}
上面用到的依赖库:
implementation "io.reactivex.rxjava2:rxjava:2.1.0"
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
补充-200922
版本更新需要一个重要的权限,上面没有提到,在这里补充下:
今天在给新项目接更新的时候,包下载完死活安装不了,就是没加这个权限的原因!