Android应用版本更新,断点下载并安装方法
程序员文章站
2022-03-26 20:08:40
android应用版本更新,断点下载并安装方法。
1.依赖:
//《okhttp网络请求依赖》
implementation 'com.squareup.okhttp3:okhttp:...
android应用版本更新,断点下载并安装方法。
1.依赖:
//《okhttp网络请求依赖》 implementation 'com.squareup.okhttp3:okhttp:3.10.0' //《gson解析依赖》 implementation 'com.google.code.gson:gson:2.8.2' //《butterknife依赖(黄油刀)》 implementation 'com.jakewharton:butterknife:8.8.1' annotationprocessor 'com.jakewharton:butterknife-compiler:8.8.1'
2.权限
<uses-permission android:name="android.permission.internet"/> <uses-permission android:name="android.permission.mount_unmount_filesystems" tools:ignore="protectedpermissions" /> <uses-permission android:name="android.permission.write_external_storage"/>
<uses-permission android:name="android.permission.read_external_storage"/>
3.封装的外层bean
package com.example.dell.versionupdatedemo05.bean; public class messagebean{ private boolean success; private string msg; private t data; public boolean getsuccess() { return success; } public void setsuccess(boolean success) { success = success; } public string getmsg() { return msg; } public void setmsg(string msg) { this.msg = msg; } public t getdata() { return data; } public void setdata(t data) { this.data = data; } }4.封装的内层bean
package com.example.dell.versionupdatedemo05.bean; public class versioninfo { private int last_must_update;//需强制更新的版本 private int last_version;//需更新的版本 private string md5;//md5码 private string url;//下载地址 public int getlast_must_update() { return last_must_update; } public void setlast_must_update(int last_must_update) { this.last_must_update = last_must_update; } public int getlast_version() { return last_version; } public void setlast_version(int last_version) { this.last_version = last_version; } public string getmd5() { return md5; } public void setmd5(string md5) { this.md5 = md5; } public string geturl() { return url; } public void seturl(string url) { this.url = url; } }
5.定义成功与失败的接口
package com.example.dell.versionupdatedemo05; //定义成功与失败的方法 public interface netcallback { void success(object o); void error(throwable t); }
6.网络请求的工具类
package com.example.dell.versionupdatedemo05.utils; import android.os.handler; import com.example.dell.versionupdatedemo05.netcallback; import com.google.gson.gson; import java.io.ioexception; import java.lang.reflect.type; import okhttp3.call; import okhttp3.callback; import okhttp3.okhttpclient; import okhttp3.request; import okhttp3.response; public class httputils { //1.创建一个私有的静态的单列模式 private static volatile httputils instance; //13.创建一个公有的handler public handler handler = new handler() {}; private final okhttpclient client; //2.创建一个私有的构造方法 private httputils(){ //9.创建okhttpclient client = new okhttpclient(); } //3.提供一个公有的静态方法 public static httputils getinstance(){ //4.判空 if(instance==null){ //5.添加同步锁 synchronized (httputils.class){ if(null==instance){ //6.双向判空后 进行创建 instance = new httputils(); } } } //7.返回instance return instance; } //8.创建一个方法 进行请求数据 public void getdata(string url, final type type, final netcallback netcallback){ //10.创建request对象 final request request = new request.builder() .url(url) .get() .build(); //11.创建call对象 call call = client.newcall(request); //12.进行异步请求 call.enqueue(new callback() { //失败的方法 @override public void onfailure(call call,final ioexception e) { //19.将失败的信息进行返回 handler.post(new runnable() { @override public void run() { netcallback.error(new throwable(e)); } }); } //成功的方法 @override public void onresponse(final call call, response response) throws ioexception { //14.创建netcallback的接口 // 15.解析数据 string data = response.body().string(); //16.使用gson进行解析 gson gson = new gson(); final object o = gson.fromjson(data, type); //17.使用handler发送消息 handler.post(new runnable() { @override public void run() { //18.将传给的信息进行返回 netcallback.success(o); } }); } }); } }7.获取应用版本的工具类
package com.example.dell.versionupdatedemo05.utils; import android.content.context; import android.content.pm.packageinfo; import android.content.pm.packagemanager; public class versionutils { //创建方法 得到本地版本 public static int getversioncode(context context){ //拿到包的管理器 packagemanager packagemanager = context.getpackagemanager(); packageinfo packageinfo=null; try { //通过包名拿到版本号 packageinfo = packagemanager.getpackageinfo(context.getpackagename(), 0); } catch (packagemanager.namenotfoundexception e) { e.printstacktrace(); } //返回版本号 return packageinfo.versioncode; } }
8.md5判断的工具类
package com.example.dell.versionupdatedemo05.utils; import java.io.file; import java.io.fileinputstream; import java.math.biginteger; import java.security.messagedigest; public class filemd5utils { public static string getfilemd5(file file) { if (!file.isfile()) { return null; } messagedigest digest = null; fileinputstream in = null; byte buffer[] = new byte[1024]; int len; try { digest = messagedigest.getinstance("md5"); in = new fileinputstream(file); while ((len = in.read(buffer, 0, 1024)) != -1) { digest.update(buffer, 0, len); } in.close(); } catch (exception e) { e.printstacktrace(); return null; } biginteger bigint = new biginteger(1, digest.digest()); return bigint.tostring(16); } }
9.activity
package com.example.dell.versionupdatedemo05; import android.app.progressdialog; import android.content.dialoginterface; import android.content.intent; import android.content.sharedpreferences; import android.net.uri; import android.os.bundle; import android.os.environment; import android.support.v7.app.alertdialog; import android.support.v7.app.appcompatactivity; import android.util.log; import android.widget.button; import android.widget.textview; import android.widget.toast; import com.example.dell.versionupdatedemo05.bean.messagebean; import com.example.dell.versionupdatedemo05.bean.versioninfo; import com.example.dell.versionupdatedemo05.utils.filemd5utils; import com.example.dell.versionupdatedemo05.utils.httputils; import com.example.dell.versionupdatedemo05.utils.versionutils; import com.google.gson.reflect.typetoken; import java.io.file; import java.io.ioexception; import java.io.inputstream; import java.io.randomaccessfile; import java.lang.reflect.type; import butterknife.bindview; import butterknife.butterknife; import butterknife.onclick; import okhttp3.call; import okhttp3.callback; import okhttp3.okhttpclient; import okhttp3.request; import okhttp3.response; public class mainactivity extends appcompatactivity { @bindview(r.id.mian_show) textview mian_show; @bindview(r.id.mian_btn) button mian_btn; private boolean ismust = false;//定义状态默认为false private file file;//初始化地址 private progressdialog dialog;//初始化下载进度条 private static final int intent_apk = 0x123;//创建十六进制数 private sharedpreferences sp;//数据存储 private long filelength = 0;//初始化数据总汉长度为0 private boolean issaved = false;//定义标识 是否已经存储过长度 @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); butterknife.bind(this); //初始化sharedpreferences sp = getsharedpreferences("info",mode_private); filelength = sp.getlong("length",0); issaved = sp.getboolean("is_saved",false);//初始化没有存储 //判断外置存储是否挂载 if(environment.getexternalstoragestate().equals(environment.media_mounted)){ //若挂载 则创建存放位置(sdk中) file externalstoragedirectory = environment.getexternalstoragedirectory(); string path = externalstoragedirectory.getabsolutepath() + file.separator + "new.apk"; log.e("+++++存入路径", "oncreate: "+path ); file = new file(path); //判断文件是否存在 不存在则创建 if(!file.exists()){ try { file.createnewfile(); } catch (ioexception e) { e.printstacktrace(); } } } //创建下载进度条 dialog = new progressdialog(this); dialog.setprogressstyle(progressdialog.style_horizontal);//进度条样式为水平 dialog.setmax(100);//设置最大值为100 dialog.settitle("下载"); } //点击时 进行版本检查 @onclick(r.id.mian_btn) public void onviewclicked() { //创建检测版本有无更新的方法 checkversion(); } //定义检测版本有无更新的方法 private void checkversion() { type type = new typetoken>() { }.gettype(); httputils.getinstance().getdata("https://www.xieast.com/api/checkversion.php", type, new netcallback() { //成功的方法 @override public void success(object o) { //强转成我们所需的数据集合 messagebean messagebean = (messagebean) o; //log.e("+++++", "success: "+messagebean.getmsg()); versioninfo info = messagebean.getdata(); //创建判断当前版本是否需要更新的方法 isneedupdate(info); } //失败的方法 @override public void error(throwable t) { log.e("+++++", "error: " + t.getmessage()); } }); } //定义判断当前版本是否需要更新的方法 private void isneedupdate(final versioninfo info) { //拿到当前的版本号 int currentversioncode = versionutils.getversioncode(this); //判断当前版本号是否小于网络请求的需更新的版本号 if (currentversioncode < info.getlast_version()) { //创建弹出对话框 alertdialog.builder builder = new alertdialog.builder(this) .settitle("版本更新") .setmessage("检查到新的版本"); //需要更新 判断当前的版本是否小于等于网络请求的需强制更新的版本号 if (currentversioncode < info.getlast_must_update()) { //需强制更新 将状态值变成false ismust = false; builder.setpositivebutton("立即更新", new dialoginterface.onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //创建更新apk文件的方法 downloadapk(info.geturl(),info.getmd5()); //设置监听事件 } }).setoncancellistener(new dialoginterface.oncancellistener() { @override public void oncancel(dialoginterface dialog) { // 若不想更新 则点击模拟器返回按钮 关闭页面 finish(); } }); } else { //可更新 不更新则将状态值变成false ismust = true; builder.setpositivebutton("更新", new dialoginterface.onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //创建更新apk文件的方法 downloadapk(info.geturl(),info.getmd5()); } //也可不更新 }).setnegativebutton("取消", null); } //显示弹出框 alertdialog dialog = builder.create(); //设置强制更新时 弹出框点外部不会消失 //ismust为true 失去焦点不隐藏 dialog.setcanceledontouchoutside(ismust);//设置弹出框失去焦点是否隐藏,即点击屏蔽其它地方是否隐藏 dialog.show(); } else { // 吐司提示 toast.maketext(this, "当前版本已经是最新版本了!", toast.length_short).show(); } } //更新下载apk文件的方法 private void downloadapk(string url, final string md5) { log.e("+++++", "downloadapk: 开始下载" ); string range = "";//定义变量 //进行判断 if(issaved){ //若存储过文件总长度 则直接将总长度赋给range即可 range = "bytes="+file.length()+"-"+filelength; }else{ //若没有存储过 则直接指定range为0 range = "bytes="+file.length()+"-"; } //使用原生okhttpclient进行请求 okhttpclient client = new okhttpclient(); final request request = new request.builder() .url(url) .addheader("range",range) .get() .build(); call call = client.newcall(request); //下载是显示进度条 dialog.show(); call.enqueue(new callback() { @override //失败 public void onfailure(call call, ioexception e) { dialog.dismiss();//失败时,进度条不显示 log.e("+++++", "onfailure:下载失败 "); } @override //成功 public void onresponse(call call, response response) throws ioexception { long contentlength =0; if(filelength==0){ //得到数据的总长度 contentlength = response.body().contentlength(); //并存入数据存储中 sp.edit().putlong("length",contentlength).commit(); }else{ contentlength =filelength; } log.e("++++文件大小", "onresponse: "+contentlength); int length = 0; long sum = file.length() ;//定义一个总长度 //创建随机存储的类 rw:可读可写模式 randomaccessfile raf = new randomaccessfile(file,"rw"); //移动到目前已经下载的文件大小的位置 raf.seek(sum); //拿到流文件 inputstream输入流:往内存写数据 inputstream inputstream = response.body().bytestream(); //按照2048个字节进行写入 byte[] bytes = new byte[2048]; //循环写入 while ((length = inputstream.read(bytes,0,bytes.length))!=-1){ sum+=length;//每次循环读取的数据赋给sum raf.write(bytes,0,length); raf.seek(sum); //总长度*100/文件长度 = 进度 int progress = (int) (sum * 100 / contentlength); //子线程不能更新ui,但是dialog内部已经回调到主线程了 dialog.setprogress(progress);//将进度赋给进度条 if(progress>99){ //下载完成 安装apk 并关闭dialog dialog.dismiss(); //创建校验md5值的方法 checkapk(file,md5); break;//循环完毕 跳出循环 } } //关流 inputstream.close(); } }); } //创建校验md5的方法 private void checkapk(file file, string md5) { //校验成功后再进行安装 string filemd5 = filemd5utils.getfilemd5(file); log.e("+++++", "checkapk:源文件的md5值 "+filemd5 ); log.e("+++++", "checkapk: 下载的md5值"+md5 ); //tolowercase:全部转为小写 equalsignorecase:忽略大小写 if(filemd5.equalsignorecase(md5)) { installapk(file);//创建安装apk方法 }else{ log.e("++++", "checkapk:文件不合法 " ); } } //安装apk的方法 private void installapk(file file) { log.e("++++", "installapk:进行安装 " ); intent intent = new intent(); intent.setaction(intent.action_view); intent.addcategory(intent.category_default); intent.setdataandtype(uri.fromfile(file),"application/vnd.android.package-archive"); intent.setflags(intent.flag_activity_new_task);//启动另一个任务栈 startactivityforresult(intent,intent_apk); system.exit(0);//安装完成后 退出即可 } }10.主布局
xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <textview android:id="@+id/mian_show" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="hello world!" /> <button android:id="@+id/mian_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="版本检查"/> linearlayout>