作为一个app,如果要长期使用,我们是要对它的版本进行更新迭代的,记录一下,网上参考一个一下别人的检查更新的做法,然后自己修改成自己需要的。
我们可以看一些效果图:
以上是效果图
我们还是看回自己的代码,布局那些就不放上来了,自己可以随便撸一个自己喜欢的样式出来。
这个是我的检查更新按钮:
case R.id.ll_auto_update:
//更新方法
auto_update();
break;
1.auto_update()这个方法里,我去调用服务器上有apk 的接口,请求获得返回 版本号信息,apk下载链接,版本内容介绍。(代码不可以复制使用,可以参考),重要的是拿到接口返回来的apk下载链接。
//更新版本信息
private void auto_update() {
String url = "XXXXXXXXXXXXXX";
//获取本地内部版本好
final int vcode = PackageUtils.getVersionCode(getContext());
Map<String, String> map = new HashMap<>();
map.put("XXXXX", "XXXXX");
//调用封装好的网络请求,返回json格式
OkHttpRequest.getInstance().getRequest(url, map, getContext(), new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
try {
ResponseModer responseModer = (ResponseModer) msg.obj;
JSONObject jsonObject = responseModer.getJsonResultData();
System.out.println("asdad" + jsonObject);
String version = jsonObject.getString("version");
//重要的是拿到下载apk链接
String update_url = jsonObject.getString("update_url");
int verion_int = jsonObject.getInt("verion_int");
String ver_introduction = jsonObject.getString("ver_introduction");
System.out.println("外部版本号==" + version);
System.out.println("下载链接==" + update_url);
System.out.println("内部版本号==" + verion_int);
System.out.println("版本介绍==" + ver_introduction);
if (verion_int <= vcode) {
Util.showToast(getContext(), "现在是最新的版本");
} else {
// Util.showToast(getContext(), "检查到有新版本信息更新");
showUpdateDialog();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
这里面去获取当前apk的信息,拿到当前的版本信息,我是通过一个(PackageUtils)类去获取的。
public class PackageUtils {
/**
* 获取版本名称
*
* @param context 上下文
*
* @return 版本名称
*/
public static String getVersionName(Context context) {
//获取包管理器
PackageManager pm = context.getPackageManager();
//获取包信息
try {
PackageInfo packageInfo = pm.getPackageInfo(context.getPackageName(), 0);
//返回版本号
return packageInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取版本号
*
* @param context 上下文
*
* @return 版本号
*/
public static int getVersionCode(Context context) {
//获取包管理器
PackageManager pm = context.getPackageManager();
//获取包信息
try {
PackageInfo packageInfo = pm.getPackageInfo(context.getPackageName(), 0);
//返回版本号
return packageInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return 0;
}
/**
* 获取App的名称
*
* @param context 上下文
*
* @return 名称
*/
public static String getAppName(Context context) {
PackageManager pm = context.getPackageManager();
//获取包信息
try {
PackageInfo packageInfo = pm.getPackageInfo(context.getPackageName(), 0);
//获取应用 信息
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
//获取albelRes
int labelRes = applicationInfo.labelRes;
//返回App的名称
return context.getResources().getString(labelRes);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
//获取本地内部版本号
final int vcode = PackageUtils.getVersionCode(getContext());
跟服务器返回接口的内部版本号(verion_int ) 进行比较;
服务器上的内部版本号大于本地的时候,我们在去拿到apk下载链接 去执行下一步的操作。
2.showUpdateDialog();这个方法,来处理;
/**
* 点击下载弹框
*/
private void showUpdateDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("检测到新版本");
builder.setMessage("版本介绍:\n" + ver_introduction);
builder.setPositiveButton("以后再说", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).setNegativeButton("立即更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
showDownloadDialog();
}
});
builder.show();
}
弹出窗提示,版本介绍内容,当点击立即更新时,我们再进去下一步的下载操作。
3.showDownloadDialog(); 下载进度;
/*
* 显示正在下载对话框
*/
protected void showDownloadDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("下载中");
View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_progress, null);
mProgressBar = view.findViewById(R.id.id_progress);
builder.setView(view);
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 隐藏当前对话框
dialog.dismiss();
// 设置下载状态为取消
mIsCancel = true;
}
});
mDownloadDialog = builder.create();
mDownloadDialog.show();
// 下载文件
downloadAPK();
}
/* 开启新线程下载apk文件
*/
private void downloadAPK() {
new Thread(new Runnable() {
@Override
public void run() {
try {
mIsCancel = false;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String sdPath = Environment.getExternalStorageDirectory() + "/";
// 文件保存路径
mSavePath = sdPath + "lianzhandownload";
File dir = new File(mSavePath);
if (!dir.exists()) {
dir.mkdir();
}
// 下载文件
HttpURLConnection conn = (HttpURLConnection) new URL(update_url).openConnection();
conn.connect();
InputStream is = conn.getInputStream();
int length = conn.getContentLength();
File apkFile = new File(mSavePath, version);
FileOutputStream fos = new FileOutputStream(apkFile);
int count = 0;
byte[] buffer = new byte[1024];
while (!mIsCancel) {
int numread = is.read(buffer);
count += numread;
// 计算进度条的当前位置
mProgress = (int) (((float) count / length) * 100);
// 更新进度条
mUpdateProgressHandler.sendEmptyMessage(1);
// 下载完成
if (numread < 0) {
mUpdateProgressHandler.sendEmptyMessage(2);
break;
}
fos.write(buffer, 0, numread);
}
fos.close();
is.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
开启线程介绍;下载进度更新ui显示:
/**
* 接收消息
*/
private Handler mUpdateProgressHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
// 设置进度条
mProgressBar.setProgress(mProgress);
break;
case 2:
// 隐藏当前下载对话框
mDownloadDialog.dismiss();
// 安装 APK 文件
installAPK();
}
}
};
当下载完成之后,我们再去安装新版的apk:
我们要注意文件清单添加相应的权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
如果没有添加到最后一条的权限,8.0下载完不会跳转获取apk安装。安装之前还要做一下的处理https://blog.csdn.net/zhaihaohao1/article/details/81809110参考别人的方法。
4。 installAPK()方法;
/*
* 下载到本地后执行安装
*/
protected void installAPK() {
File apkFile = new File(mSavePath, version);
if (!apkFile.exists()) {
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
//判断是否是AndroidN以及更高的版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
getActivity().startActivity(intent);
}
文件清单多加
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="包名.fileProvider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
3、打开file_paths.xml文件添加如下内容
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path="包名/" name="fileProvider" />
<external-path path="." name="apk下载文件夹名" />
</paths>
path:需要临时授权访问的路径(.代表所有路径)
name:就是你给这个访问路径起个名字
然后再去调用installAPK()。到这里就基本上就可以了。这是我修改了部分代码。如果你还是看不懂,你可以参考下别人的
https://blog.csdn.net/zhaihaohao1/article/details/81808902
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、2020.4.28重新补充善、、、、、、、、、、、
1.上面弹窗提示,修改自定义的布局弹窗,效果如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:text="@string/up_titletips"
android:textColor="@color/black"
android:textSize="32sp" />
<View
android:id="@+id/vv"
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_below="@id/tv_01"
android:background="@color/lightpink" />
<LinearLayout
android:id="@+id/ll_01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/vv"
android:layout_margin="25dp"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.8"
android:visibility="gone">
<RelativeLayout
android:layout_width="134dp"
android:layout_height="134dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:background="@drawable/ic_launcher_background">
<ImageView
android:id="@+id/detect_reg_image_item"
android:layout_width="134dp"
android:layout_height="134dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="@mipmap/ic_launcher" />
</RelativeLayout>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/up_versionexpian"
android:textColor="@color/lightpink"
android:textSize="28sp" />
<TextView
android:id="@+id/tv_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFF"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/up_versionexpian"
android:textColor="#FFFFFF"
android:textSize="28sp" />
<TextView
android:id="@+id/tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试123"
android:textColor="@color/lightpink"
android:textSize="28sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:layout_below="@id/ll_01">
<Button
android:id="@+id/btn_EXIT"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="@string/up_neglet"
android:textColor="@color/black"
android:textSize="30sp" />
<Button
android:id="@+id/btn_OK"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="15dp"
android:layout_toLeftOf="@id/btn_EXIT"
android:text="@string/up_installnow"
android:textColor="@color/black"
android:textSize="30sp" />
</RelativeLayout>
</RelativeLayout>
/**
* 弹框处理事件,info:传入版本介绍
*/
private void init_dialog(String info) {
AlertDialog.Builder customizeDialog =
new AlertDialog.Builder(TestFrag_ShowhidedActivity.this);
final View dialogView = LayoutInflater.from(TestFrag_ShowhidedActivity.this)
.inflate(R.layout.layout_upappdialog, null);
customizeDialog.setView(dialogView);
TextView tv_info = (TextView) dialogView.findViewById(R.id.tv_info);
Button btn_OK = dialogView.findViewById(R.id.btn_OK);
Button btn_EXIT = dialogView.findViewById(R.id.btn_EXIT);
// 图片展示Uri.fromFile
tv_info.setText(info);
final AlertDialog dialog = customizeDialog.show();
Window window = dialog.getWindow();
window.setGravity(Gravity.CENTER);
customizeDialog.create();
btn_OK.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
showDownloadDialog();
}
});
btn_EXIT.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
}
2.当点击立即更新按钮之后,跳转到进度条显示:
进度布局:
<?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="match_parent"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="30dp" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_downloadnum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="0%"
android:textSize="30dp"
android:layout_margin="20dp"
android:textColor="@color/black" />
</RelativeLayout>
</LinearLayout>
/*
* 显示正在下载对话框
*/
protected void showDownloadDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(TestFrag_ShowhidedActivity.this);
builder.setTitle("下载中");
View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.dialog_progress, null);
mProgressBar = view.findViewById(R.id.progressBar);
tv_downloadnum = view.findViewById(R.id.tv_downloadnum);
builder.setView(view);
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 隐藏当前对话框
dialog.dismiss();
// 设置下载状态为取消
mIsCancel = true;
}
});
mDownloadDialog = builder.create();
mDownloadDialog.show();
// 下载文件,g跟第一步的方法一下,找到下载好的文件存放路径,执行跳转安装即可
downloadAPK();
}
private void downloadAPK() {
File file=new File(download);
if(!file.exists()){
file.mkdirs();
}
new Thread(new Runnable() {
@Override
public void run() {
try {
mIsCancel = false;
// 下载文件
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.connect();
InputStream is = conn.getInputStream();
int length = conn.getContentLength();
// url_version + ".apk",自己定义的apK名称
File apkFile = new File(download, url_version + ".apk");
FileOutputStream fos = new FileOutputStream(apkFile);
int count = 0;
byte[] buffer = new byte[1024];
while (!mIsCancel) {
int numread = is.read(buffer);
count += numread;
// 计算进度条的当前位置
mProgress = (int) (((float) count / length) * 100);
// 更新进度条
init_message(68, String.valueOf(mProgress));
// 下载完成
if (numread < 0) {
handler.sendEmptyMessage(69);
break;
}
fos.write(buffer, 0, numread);
}
fos.close();
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
//线程
private void init_message(int num, String str) {
Message message = handler.obtainMessage();
message.what = num;
message.obj = str;
handler.sendMessage(message);
}
对应线程68显示进度条百分之几, 69 执行安装:
case 68:
// 设置进度条
mProgressBar.setProgress(mProgress);
String num = String.valueOf(msg.obj);
Log.i(TAG, "handleMessage: 进度啊=" + num);
tv_downloadnum.setText(num + "%");
break;
case 69:
// 隐藏当前下载对话框
mDownloadDialog.dismiss();
// 安装 APK 文件
installAPK();
/* Intent intent= IntentUtil.installAPK(TestFrag_ShowhidedActivity.this,download+ File.separator +url_version + ".apk");
startActivity(intent);*/
Log.i(TAG, "handleMessage: 安装啊");
break;
备注:一些定义全局变量名称
private ProgressBar mProgressBar; private volatile boolean mIsCancel = false; // 进度 private int mProgress; private TextView tv_downloadnum;
这次补充和新增一些弹框优化,总体的核心步骤没变化,根据后台接口的定义返回的url去下载apk,存在本地,当下载完成就调用Intent意图去找到apk文件uri地址,执行手动确定安装。
每个人的需求不一定相同,但大致的方向还是一样的,我们可以参考别的踩过的坑,避免再去踩,当然目前这样的方式还是多少会存在不足,但我们可以实现之前,有了大致的思路,我们再去深入学习探讨,这样的学习方式也是不错的,由于本人知识有限,上面的东西没过多的详细说明,只是简单的说明。如有什么问题,可在评论区留意,我们一起探讨学习,谢谢。