通用版本升级功能,拿去就能用!
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import androidx.fragment.app.DialogFragment;
import com.htnova.fly.R;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| public class HtUpdateDialogFragment extends DialogFragment { private ProgressBar progressBar; private TextView tvProgress; private Button btnUpdate, btnSkip, btnCancel; private volatile boolean isCancelled = false; private final String apkUrl = "http://115.190.154.26/app-release.apk"; private final int serverVersionCode = 2; private final int currentVersionCode; public HtUpdateDialogFragment(Context context) { currentVersionCode = getVersionCode(context); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.dialog_update, container, false); initView(view); return view; } private void initView(View view) { progressBar = view.findViewById(R.id.progress_bar); tvProgress = view.findViewById(R.id.tv_progress); btnUpdate = view.findViewById(R.id.btn_update); btnSkip = view.findViewById(R.id.btn_skip); btnCancel = view.findViewById(R.id.btn_cancel); TextView tvInfo = view.findViewById(R.id.tv_update_info); tvInfo.setText("当前版本:" + currentVersionCode + "\n服务器版本:" + serverVersionCode + "\n\n更新内容:\n- 修复问题\n- 优化性能"); btnUpdate.setOnClickListener(v -> { btnUpdate.setVisibility(View.GONE); btnSkip.setVisibility(View.GONE); btnCancel.setVisibility(View.VISIBLE); progressBar.setVisibility(View.VISIBLE); tvProgress.setVisibility(View.VISIBLE); startDownload(); }); btnSkip.setOnClickListener(v -> dismiss()); btnCancel.setOnClickListener(v -> { isCancelled = true; Toast.makeText(getContext(), "已取消下载", Toast.LENGTH_SHORT).show(); dismiss(); }); } private void startDownload() { new Thread(() -> { HttpURLConnection conn = null; InputStream is = null; BufferedOutputStream bos = null; try { URL url = new URL(apkUrl); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(10000); conn.setReadTimeout(10000); conn.connect(); int length = conn.getContentLength(); if (length <= 0) { showToast("服务器未返回文件大小"); return; } is = new BufferedInputStream(conn.getInputStream()); File apkFile = new File(requireContext().getExternalFilesDir(null), "update.apk"); bos = new BufferedOutputStream(new FileOutputStream(apkFile)); byte[] buffer = new byte[8192]; // 8KB缓冲区 int count = 0; int bytesRead; int lastProgress = 0; while ((bytesRead = is.read(buffer)) != -1 && !isCancelled) { bos.write(buffer, 0, bytesRead); count += bytesRead; int progress = (int) (count * 100L / length); if (progress != lastProgress) { int finalProgress = progress; requireActivity().runOnUiThread(() -> { progressBar.setProgress(finalProgress); tvProgress.setText("下载进度:" + finalProgress + "%"); }); lastProgress = progress; } } bos.flush(); if (!isCancelled) { requireActivity().runOnUiThread(() -> { Toast.makeText(getContext(), "下载完成,准备安装", Toast.LENGTH_SHORT).show(); installApk(apkFile); dismiss(); }); } } catch (Exception e) { Log.e("HtUpdateDialog", "下载出错:" + e.getMessage(), e); showToast("下载失败:" + e.getMessage()); } finally { try { if (is != null) is.close(); if (bos != null) bos.close(); if (conn != null) conn.disconnect(); } catch (Exception ignored) { } } }).start(); } private void installApk(File apkFile) { Context context = requireContext(); Uri apkUri; Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { apkUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", apkFile); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else { apkUri = Uri.fromFile(apkFile); } intent.setDataAndType(apkUri, "application/vnd.android.package-archive"); context.startActivity(intent); } private int getVersionCode(Context context) { try { return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode; } catch (PackageManager.NameNotFoundException e) { return 0; } } private void showToast(String msg) { requireActivity().runOnUiThread(() -> Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show()); } } |
R.layout.dialog_update
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="20dp" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tv_update_info" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="发现新版本..." android:textSize="16sp" /> <ProgressBar android:id="@+id/progress_bar" style="@android:style/Widget.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" android:visibility="gone" /> <TextView android:id="@+id/tv_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下载进度:0%" android:visibility="gone" android:paddingTop="10dp" /> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="20dp"> <Button android:id="@+id/btn_update" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" android:text="立即更新" /> <Button android:id="@+id/btn_skip" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" android:text="跳过" /> <Button android:id="@+id/btn_cancel" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" android:text="取消下载" android:visibility="gone"/> </LinearLayout> </LinearLayout> |
application中添加
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> |
xml文件夹下新建file_paths.xml
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="update_apk" path="." /> </paths> |
使用new HtUpdateDialogFragment(this).show(getSupportFragmentManager(), "update_dialog");