1.判断版本
public static void judgeVersion() {
ApiService.getAndroidAppVersion().subscribe(new CallBack<UpdateEntity>() {
@Override
protected void errorBody(String code, String message, String result) {
ToastUtils.showToast(message);
}
@Override
protected void requestBody(UpdateEntity data, String message) {
UpdateEntity.AvBean av = data.getAv();
if (av == null) {
return;
}
int myVersionCode = Integer.parseInt(av.getAndroidCode());
try {
int currentVersionCode = BaseApplication.getCurrentActivity().getPackageManager().getPackageInfo(BaseApplication.getCurrentActivity().getPackageName(), 0).versionCode;
if (currentVersionCode < myVersionCode) {
DialogUpdate dialog = DialogUpdate.newInstance(JsonUtils.ObjectToJson(av));
dialog.show(BaseApplication.getCurrentActivity().getFragmentManager(), "DialogUpdate");
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
});
}
2.更新弹窗
public class DialogUpdate extends DialogFragment {
@BindView(R.id.strUpgradeDialogVersionLabel)
TextView strUpgradeDialogVersionLabel;
@BindView(R.id.tv_content)
TextView tvContent;
@BindView(R.id.tv_update)
TextView tvUpdate;
@BindView(R.id.iv_close)
ImageView ivClose;
private UpdateEntity.AvBean avBean;
public static DialogUpdate newInstance(String value) {
DialogUpdate dialog = new DialogUpdate();
Bundle args = new Bundle();
args.putString("value", value);
dialog.setArguments(args);
return dialog;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String value = getArguments().getString("value");
avBean = JsonUtils.jsonToObject(value, UpdateEntity.AvBean.class);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
getDialog().setCanceledOnTouchOutside(false);
getDialog().setCancelable(false);
// 重新返回方法,禁止返回键
getDialog().setOnKeyListener(new DialogInterface.OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
return true;
} else {
return false;
}
}
});
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
View view = LayoutInflater.from(getActivity()).inflate(R.layout.upgrade_dialog, null);
ButterKnife.bind(this, view);
//设置值
setData();
builder.setView(view);
return builder.create();
}
/**
*
*/
private void setData() {
//是否强制更新
String androidStatus = avBean.getAndroidStatus();
if (androidStatus!=null&&"0".equals(androidStatus)) {
ivClose.setVisibility(View.VISIBLE);
}else {
ivClose.setVisibility(View.GONE);
}
//版本号
strUpgradeDialogVersionLabel.setText(avBean.getAndroidVersion());
//内容
if (avBean.getAndroidContent().contains("\\n")) {
String replace = avBean.getAndroidContent().replace("\\n", "\n");
tvContent.setText(replace);
} else {
tvContent.setText(avBean.getAndroidContent());
}
}
@OnClick({R.id.tv_update, R.id.iv_close})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.tv_update:
DownApkUptils.down(avBean.getAndroidDownUrl(), tvUpdate);
break;
case R.id.iv_close:
getDialog().dismiss();
break;
}
}
}
3.弹窗布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/bg_transparent_0fff"
android:gravity="center_horizontal"
android:orientation="vertical">
<RelativeLayout
android:layout_width="560mm"
android:layout_height="wrap_content">
<ImageView
android:layout_width="560mm"
android:layout_height="239mm"
android:scaleType="centerCrop"
android:src="@mipmap/upgrade" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="77mm"
android:layout_marginTop="90mm"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="全新升级"
android:textColor="@color/White"
android:textSize="@dimen/jl_36" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/strUpgradeDialogVersionLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1.0"
android:textColor="@color/White"
android:textSize="@dimen/font_40"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="版"
android:textColor="@color/White"
android:textSize="@dimen/jl_36" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
<LinearLayout
android:layout_width="560mm"
android:layout_height="wrap_content"
android:background="@color/White"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/jl_60"
android:paddingLeft="@dimen/jl_40"
android:paddingRight="@dimen/jl_40"
android:paddingTop="@dimen/jl_44">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="全新升级来啦"
android:textColor="@color/font_0"
android:textSize="@dimen/font_40"
android:textStyle="bold" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="@dimen/jl_200"
android:scrollbars="vertical"
android:layout_marginTop="@dimen/jl_50">
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="@dimen/jl_39"
android:textColor="@color/font_0"
android:textSize="@dimen/font_28" />
</ScrollView>
<TextView
android:id="@+id/tv_update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/jl_60"
android:background="@drawable/shape_btn_bg_f329"
android:gravity="center"
android:paddingBottom="@dimen/jl_28"
android:paddingTop="@dimen/jl_28"
android:text="立即升级"
android:textColor="@color/White"
android:textSize="@dimen/font_34"
android:textStyle="bold" />
</LinearLayout>
<ImageView
android:id="@+id/iv_close"
android:layout_width="@dimen/jl_80"
android:layout_height="@dimen/jl_80"
android:layout_marginTop="@dimen/jl_50"
android:src="@mipmap/update_close" />
</LinearLayout>
4.更新文件下载
public class DownApkUptils {
private static long downloadLength = 0;
private static long contentLength = 0;
// private static File file;
public static void down(final String downloadUrl, final TextView textView) {
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
downApk(downloadUrl, emitter);
}
}).subscribeOn(Schedulers.io())// 将被观察者切换到子线程
.observeOn(AndroidSchedulers.mainThread())// 将观察者切换到主线程
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Integer result) {
//设置ProgressDialog 进度条进度
// progressBar.setProgress(result);
textView.setText(result + "%");
}
@Override
public void onError(Throwable e) {
ToastUtils.showToast("网络异常!请重新下载");
textView.setEnabled(true);
}
@Override
public void onComplete() {
ToastUtils.showToast("服务器异常!请重新下载");
textView.setEnabled(true);
}
});
}
/**
* //下载apk
*
* @param downloadUrl
* @param emitter
*/
private static void downApk(final String downloadUrl, final ObservableEmitter<Integer> emitter) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(downloadUrl)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//下载失败
breakpoint(downloadUrl, emitter);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.body() == null) {
//下载失败
breakpoint(downloadUrl, emitter);
return;
}
InputStream is = null;
FileOutputStream fos = null;
byte[] buff = new byte[2048];
int len;
try {
is = response.body().byteStream();
File file = createFile();
fos = new FileOutputStream(file);
long total = response.body().contentLength();
contentLength = total;
long sum = 0;
while ((len = is.read(buff)) != -1) {
fos.write(buff, 0, len);
sum += len;
int progress = (int) (sum * 1.0f / total * 100);
//下载中,更新下载进度
emitter.onNext(progress);
downloadLength = sum;
}
fos.flush();
//4.下载完成,安装apk
installApk(BaseApplication.getCurrentActivity(), file);
} catch (Exception e) {
e.printStackTrace();
breakpoint(downloadUrl, emitter);
} finally {
try {
if (is != null)
is.close();
if (fos != null)
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
/**
* //断点续传
*
* @param downloadUrl
* @param emitter
*/
private static void breakpoint(final String downloadUrl, final ObservableEmitter<Integer> emitter) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(downloadUrl)
.addHeader("RANGE", "bytes=" + downloadLength + "-" + contentLength)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//下载失败
breakpoint(downloadUrl, emitter);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.body() == null) {
//下载失败
breakpoint(downloadUrl, emitter);
return;
}
InputStream is = null;
RandomAccessFile randomFile = null;
byte[] buff = new byte[2048];
int len;
try {
is = response.body().byteStream();
//文件路径是公共的DCIM目录下的/apk
String storagePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "apkname";
File storageDir = new File(storagePath);
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
storageDir.mkdirs();
File file = File.createTempFile(timeStamp, ".apk", storageDir);
randomFile = new RandomAccessFile(file, "rwd");
randomFile.seek(downloadLength);
long total = contentLength;
long sum = downloadLength;
while ((len = is.read(buff)) != -1) {
randomFile.write(buff, 0, len);
sum += len;
int progress = (int) (sum * 1.0f / total * 100);
//下载中,更新下载进度
emitter.onNext(progress);
downloadLength = sum;
}
//4.下载完成,安装apk
installApk(BaseApplication.getCurrentActivity(), file);
} catch (Exception e) {
e.printStackTrace();
breakpoint(downloadUrl, emitter);
} finally {
try {
if (is != null)
is.close();
if (randomFile != null)
randomFile.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
/**
* 路径为根目录
*/
private static File createFile() {
//文件路径是公共的DCIM目录下的/camerademo目录
String storagePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "apkname";
File storageDir = new File(storagePath);
storageDir.mkdirs();
File file = null;
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
try {
if (file != null && file.exists()) {
file.delete();
}
file = File.createTempFile(timeStamp, ".apk", storageDir);
return file;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* //安装apk,包含7.0
* @param context
* @param file
*/
private static void installApk(Context context, File file) {
if (context == null) {
return;
}
String authority = context.getApplicationContext().getPackageName() + ".fileprovider";
Uri apkUri = FileProvider.getUriForFile(context, authority, file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//判读版本是否在7.0以上
if (Build.VERSION.SDK_INT >= 24) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
context.startActivity(intent);
//弹出安装窗口把原程序关闭。
//避免安装完毕点击打开时没反应
new Timer().schedule(new TimerTask() {
@Override
public void run() {
Process.killProcess(android.os.Process.myPid());
}
},1000);
}
}
5.清单文件里的配置
<!-- 用于应用更新 -->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="包名.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/update_file_paths" />
</provider>
6.res文件下的xml文件provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- /storage/emulated/0/Download/${applicationId}/.beta/apk-->
<external-path name="beta_external_path" path="Download/"/>
<!--/storage/emulated/0/Android/data/${applicationId}/files/apk/-->
<external-path name="beta_external_files_path" path="Android/data/"/>
</paths>