Android实现版本更新功能

龙旋

共 26757字,需浏览 54分钟

 ·

2021-03-07 08:46

毫无疑问,版本更新是每个应用不可缺少的功能之一 , 为了方便之后的集成 , 现在记录一下 , 这个是比较久之前写的了,目前使用的是HttpURLConnection进行网络请求 , 后期会使用其它网络请求优化,这篇注重逻辑的实现。


现在版本更新有两种处理方式了:

1、跳转到App应用市场(例如:应用宝、豌豆荚等等),通过应用市场下载更新安装。

2、在App内进行Apk下载,下载完成后更新安装。


我们这边使用第二种方式,为了方便大家理解,画了一张流程图:



实现流程:


1、启动页后调用方法

private UpdateInfo iwudif;//版本更新bean类private String path = "";//请求路径
// 检测版本是否一致,private void isNeedUpdate() {
new Thread(new Runnable() { @Override public void run() {         //调用网络请求的方法,获取后台配置的请求数据,path为请求路径 iwudif = getUpDateInfo(path); if (null == iwudif) { Log.d("text", "没有更新"); runOnUiThread(new Runnable() { @Override public void run() { //进入到登录界面 jumpToI8LoginActivity(); } }); } else { Log.d("text", "有更新"); apkName = I8SP.getDownloadBag(TCSplashActivity.this);//保存apk的name到sp中 runOnUiThread(new Runnable() {
@Override public void run() {                        //有更新 if (iwudif.getMsg().getUrl() != null) { if (UPDATATYPE == iwudif.getMsg().getUpdateType()) { File file = new File(Environment.getExternalStorageDirectory() .getAbsolutePath(), apkName);
//新版本存在,直接安装 if (file.exists()) { try { String md5ByFile = StatusBarUtil.getMd5ByFile(file);
//判定md5值是否正确存在进入已存在更新,不存在进入show的对话框 if (null != md5ByFile && md5ByFile.equals(iwudif .getMsg().getMd5())) { //弹出提示框,已经存在,是重新下载还是直接安装。 existApkInstall(iwudif); } else { //apk没下载,是否更新对话框 showUpdateDialog(iwudif); }
} catch (FileNotFoundException e) { e.printStackTrace(); //进入到登录界面 jumpToI8LoginActivity(); } } else { //apk没下载,是否更新对话框 showUpdateDialog(iwudif); } } else { //强制更新时进入是否更新对话框 showUpdateDialog(iwudif); } } else {//无更新 //版本号一致,进入到登录界面 jumpToI8LoginActivity(); } } });            } } }).start();}


2、获取升级信息的方法

/** * 获取升级信息 */public UpdateInfo getUpDateInfo(String path) {    StringBuffer sb = new StringBuffer();    String line;    BufferedReader reader = null;    try {        // 创建一个url对象        URL url = new URL(path);        // 通過url对象,创建一个HttpURLConnection对象(连接)        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();        urlConnection.setRequestMethod("GET");        urlConnection.setConnectTimeout(2000);        urlConnection.setReadTimeout(2000);        if (urlConnection.getResponseCode() == 200) {            // 通过HttpURLConnection对象,得到InputStream            reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));            while ((line = reader.readLine()) != null) {                sb.append(line);            }            String info = sb.toString();            Log.d("text", "版本升级信息:" + info);//--------            // 对升级的信息进行封装            Gson gson = new Gson();            Type type = new TypeToken<UpdateInfo>() {            }.getType();            return gson.fromJson(info, type);        }    } catch (Exception e) {        e.printStackTrace();        return null;    } finally {        try {            if (reader != null) {                reader.close();            }        } catch (Exception e) {            e.printStackTrace();        }    }    return null;}


3、弹出提示框方法,已经存在,是重新下载还是直接安装

//是否重新下载private void existApkInstall(final UpdateInfo iwudif) {
ExistVersionUpdataDialog existVersionUpdataDialog = new ExistVersionUpdataDialog(iwudif.getMsg().getDesc(), iwudif.getMsg() .getUpdateType(), this, "版本更新", "温馨提示:检测到本机已存在最新版本的安装包" + apkName + ",可选择立即安装或重新下载。", "立即安装", "重新下载", new ExistVersionUpdataDialog .ExistVersionUpdataDialogCallBack() { @Override public void sureUpdata(boolean isUpdata) { installApk(); }
@Override public void downloadAgain(boolean isUpdata) { downFile(iwudif.getMsg().getUrl()); }
@Override public void close(boolean isUpdata) { jumpToI8LoginActivity();//进入到登录界面 } }); //按对话框外区域不消失 existVersionUpdataDialog .setCanceledOnTouchOutside(false); //按返回键也不起作用 existVersionUpdataDialog .setCancelable(false); existVersionUpdataDialog .show();}


4、安装apk的方法

 //安装apk,也可以进行静默安装 private void installApk() {    //更新后自动打开apk    Intent intent = new Intent(Intent.ACTION_VIEW);    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//如果不加,最后安装完成,点打开,无法打开新版本应用。    intent.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory(), apkName)), "application/vnd.android.package-archive");    startActivityForResult(intent, 10);    android.os.Process.killProcess(android.os.Process.myPid());//如果不加,最后不会提示完成、打开。}


5、下载apk的方法

/** * 下载最新版本的apk * * @param path apk下载地址
private void downFile(final String path) { final ProgressDialog pBar = new ProgressDialog(TCSplashActivity.this); pBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pBar.setCancelable(false); pBar.setTitle("正在下载..."); pBar.setMessage("请稍候..."); pBar.setProgress(0); pBar.show(); new Thread() { public void run() { try { URL url = new URL(path); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setReadTimeout(3000); con.setConnectTimeout(3000); con.setRequestProperty("Charset", "UTF-8"); con.setRequestMethod("GET"); if (con.getResponseCode() == 200) { int length = con.getContentLength();// 获取文件大小 InputStream is = con.getInputStream(); pBar.setMax(100); // 设置进度条的总长度 FileOutputStream fileOutputStream = null; if (is != null) { // 对apk进行保存
String[] split = getPackageName().split("\\."); int length1 = split.length - 1; apkName = "apkshow_" + split[length1] + "_" + getTime() + ".apk"; I8SP.setDownloadBag(TCSplashActivity.this, apkName);
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), apkName); fileOutputStream = new FileOutputStream(file); byte[] buf = new byte[1024]; int ch; int process = 0; while ((ch = is.read(buf)) != -1) { fileOutputStream.write(buf, 0, ch); process += ch; pBar.setProgress(process * 100 / length); // 实时更新进度了 } } if (fileOutputStream != null) { fileOutputStream.flush(); fileOutputStream.close(); } //ui线程,然后进入主线程 runOnUiThread(new Runnable() { @Override public void run() { // 将下载进度对话框取消 pBar.cancel(); installApk(); } }); } } catch (Exception e) { e.printStackTrace(); runOnUiThread(new Runnable() { @Override public void run() { pBar.cancel(); Toast.makeText(getApplicationContext(), "下载失败", Toast.LENGTH_LONG).show(); jumpToI8LoginActivity();//进入到登录界面 } }); } }
}.start();   }


6、弹出下载apk的对话框方法

/** * 显示升级信息的对话框 */ private void showUpdateDialog(final UpdateInfo iwudif) {    VersionUpdataDialog dialog = new VersionUpdataDialog            (iwudif.getMsg().getDesc(), iwudif.getMsg().getUpdateType(), this,                    "版本升级", "请升级APP版本至" + iwudif.getMsg().getVersion() + ":", "更新", "取消", new                    VersionUpdataDialog                            .VersionUpdataDialogCallBack() {                        @Override                        public void sureDelete(boolean isUpdata) {                            downFile(iwudif.getMsg().getUrl());// 点击确定将apk下载                        }
@Override public void close(boolean isUpdata) { //MobclickAgent.onKillProcess(TCSplashActivity.this); android.os.Process.killProcess(android.os.Process.myPid()); jumpToI8LoginActivity();//进入到登录界面 }
@Override public void cancle(boolean isUpdata) { //isHaveHotUpdata();//判断是否有热更新 jumpToI8LoginActivity();//进入到登录界面 } }); //按对话框外区域不消失 dialog.setCanceledOnTouchOutside(false); //按返回键也不起作用 dialog.setCancelable(false); dialog.show();}


7、获取当前时间的方法

//获取当前时间public static String getTime() {    SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");    Date curDate = new Date(System.currentTimeMillis());//获取当前时间    return formatter.format(curDate);}


8、自定义版本更新对话框

public class I8ShowVersionUpdataDialog extends Dialog {public interface I8WanVersionUpdataDialogCallBack {    void sureDelete(boolean isUpdata);    void close(boolean isUpdata);    void cancle(boolean isUpdata);}
private String mUpdateContent;private int updateType;private Context mContext;private String mTitle;private String mTipContent;private I8ShowVersionUpdataDialog.I8WanVersionUpdataDialogCallBack mI8WanVersionUpdataDialogCallBack;// 标识是帐号登录还是手机登录;手机0,帐号1
private TextView tv_Title;private TextView tv_tip;private TextView tv_sure;private TextView tv_think;private ImageView tv_close;private String sureText;private String thinkText;private int contentViewWidth = -1;private TextView constraintUpdata;private LinearLayout OptionUpdata;private int UPDATATYPE = 0; //为0时是选择更新,
public I8ShowVersionUpdataDialog(String UpdataContent, int type, Context mContext, String mTitle, String mTipText, String sureText, String thinkText, I8WanVersionUpdataDialogCallBack mI8WanVersionUpdataDialogCallBack) { super(mContext); mUpdateContent = UpdataContent; updateType = type; this.mContext = mContext; this.mTitle = mTitle; this.mTipContent = mTipText; this.sureText = sureText; this.thinkText = thinkText;// this.contentViewWidth = width; this.mI8WanVersionUpdataDialogCallBack = mI8WanVersionUpdataDialogCallBack;}
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getContext().setTheme(CommonUtils.getIdByName("i8_dialog_login", "style", mContext.getPackageName(), mContext)); View contentView = View.inflate(mContext, CommonUtils.getIdByName("i8wan_version_updata_dialog", "layout", mContext.getPackageName(), mContext), null); setContentView(contentView); if (contentViewWidth > 0) contentView.getLayoutParams().width = contentViewWidth; tv_Title = (TextView) findViewById( CommonUtils.getIdByName("i8_comfirm_dialog_title", "id", mContext.getPackageName(), mContext)); tv_tip = (TextView) findViewById( CommonUtils.getIdByName("i8_comfirm_dialog_tipText", "id", mContext.getPackageName(), mContext)); tv_sure = (TextView) findViewById( CommonUtils.getIdByName("i8_comfirm_dialog_sureDelete", "id", mContext.getPackageName(), mContext)); tv_think = (TextView) findViewById( CommonUtils.getIdByName("i8_comfirm_dialog_think", "id", mContext.getPackageName(), mContext)); constraintUpdata = (TextView) findViewById( CommonUtils.getIdByName("tv_constraintUpdata", "id", mContext.getPackageName(), mContext)); OptionUpdata = (LinearLayout) findViewById( CommonUtils.getIdByName("ll_OptionUpdata", "id", mContext.getPackageName(), mContext)); tv_close = (ImageView) findViewById( CommonUtils.getIdByName("i8_comfirm_dialog_close", "id", mContext.getPackageName(), mContext));
tv_Title.setText(mTitle);// tv_tip.setText(mTipContent + "\n" + "\n" + mUpdateContent); tv_tip.setText(mUpdateContent);//普通更新不要显示第一行的mTipContent内容了。 //选择更新 Log.d("text","AAAAAAupdateType=="+updateType);//--------------- if (UPDATATYPE == updateType) { tv_sure.setText(sureText); tv_think.setText(thinkText);
tv_sure.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mI8WanVersionUpdataDialogCallBack.sureDelete(true); I8ShowVersionUpdataDialog.this.dismiss(); } }); tv_think.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mI8WanVersionUpdataDialogCallBack.cancle(true); I8ShowVersionUpdataDialog.this.dismiss(); } }); Log.d("text","AAAAAA选择更新");//--------------- //强制更新 } else { Log.d("text","AAAAAA强制更新");//--------------- tv_close.setVisibility(View.VISIBLE); tv_close.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mI8WanVersionUpdataDialogCallBack.close(true); I8ShowVersionUpdataDialog.this.dismiss(); } }); OptionUpdata.setVisibility(View.GONE); constraintUpdata.setVisibility(View.VISIBLE); constraintUpdata.setText(sureText); constraintUpdata.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mI8WanVersionUpdataDialogCallBack.sureDelete(true); I8ShowVersionUpdataDialog.this.dismiss(); mI8WanVersionUpdataDialogCallBack.cancle(true); } }); }}
@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) { return true; }}


9、自定义本机上已存在apk的对话框

public class I8ShowExistVersionUpdataDialog extends Dialog{
public interface i8ShowExistVersionUpdataDialogCallBack { void sureUpdata(boolean isUpdata); void downloadAgain(boolean isUpdata); void close(boolean isUpdata);}
private String mUpdateContent;private int updateType;private Context mContext;private String mTitle;private String mTipContent;private I8ShowExistVersionUpdataDialog.i8ShowExistVersionUpdataDialogCallBack mi8ShowExistVersionUpdataDialogCallBack;
private TextView tv_Title;private ImageView close;private TextView tv_tip;private TextView tv_sure;private TextView tv_think;private String sureText;private String thinkText;private TextView constraintUpdata;private LinearLayout OptionUpdata;
public I8ShowExistVersionUpdataDialog(String UpdataContent, int type, Context mContext, String mTitle, String mTipText, String sureText, String thinkText, I8ShowExistVersionUpdataDialog.i8ShowExistVersionUpdataDialogCallBack i8ShowExistVersionUpdataDialogCallBack) {
super(mContext); mUpdateContent = UpdataContent; updateType = type; this.mContext = mContext; this.mTitle = mTitle; this.mTipContent = mTipText; this.sureText = sureText; this.thinkText = thinkText;    this.mi8ShowExistVersionUpdataDialogCallBack = i8ShowExistVersionUpdataDialogCallBack;}
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getContext().setTheme(CommonUtils.getIdByName("i8_dialog_login", "style", mContext.getPackageName(), mContext)); View contentView = View.inflate(mContext, CommonUtils.getIdByName("i8wan_version_updata_dialog", "layout", mContext.getPackageName(), mContext), null); setContentView(contentView);

tv_Title = (TextView) findViewById( CommonUtils.getIdByName("i8_comfirm_dialog_title", "id", mContext.getPackageName(), mContext)); close = (ImageView) findViewById( CommonUtils.getIdByName("i8_comfirm_dialog_close", "id", mContext.getPackageName(), mContext)); tv_tip = (TextView) findViewById( CommonUtils.getIdByName("i8_comfirm_dialog_tipText", "id", mContext.getPackageName(), mContext)); tv_sure = (TextView) findViewById( CommonUtils.getIdByName("i8_comfirm_dialog_sureDelete", "id", mContext.getPackageName(), mContext)); tv_think = (TextView) findViewById( CommonUtils.getIdByName("i8_comfirm_dialog_think", "id", mContext.getPackageName(), mContext)); constraintUpdata = (TextView) findViewById( CommonUtils.getIdByName("tv_constraintUpdata", "id", mContext.getPackageName(), mContext)); OptionUpdata = (LinearLayout) findViewById( CommonUtils.getIdByName("ll_OptionUpdata", "id", mContext.getPackageName(), mContext));
close.setVisibility(View.VISIBLE); tv_Title.setText(mTitle); tv_tip.setText(mTipContent + "\n" + "\n" + mUpdateContent);
tv_sure.setText(sureText); tv_think.setText(thinkText);

tv_sure.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mi8ShowExistVersionUpdataDialogCallBack.sureUpdata(true); I8ShowExistVersionUpdataDialog.this.dismiss(); } }); tv_think.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mi8ShowExistVersionUpdataDialogCallBack.downloadAgain(true); I8ShowExistVersionUpdataDialog.this.dismiss(); } });
close.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mi8ShowExistVersionUpdataDialogCallBack.close(true); I8ShowExistVersionUpdataDialog.this.dismiss(); }    });}
@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) { return true; }}


10、自定义对话框布局

<?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"            android:background="@drawable/i8_popwin_bg"            android:padding="@dimen/I8d6">
<TextView android:id="@+id/i8_comfirm_dialog_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="@dimen/I8d5" android:text="Tiele" android:textColor="#353535" android:textSize="17sp"/>
<ImageView android:id="@+id/i8_comfirm_dialog_close" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:src="@drawable/i8_popwin_close" android:visibility="gone"/>

<ScrollView android:id="@+id/i8_comfirm_dialog_scroll" android:layout_width="match_parent" android:layout_height="191dp" android:layout_below="@+id/i8_comfirm_dialog_title" android:layout_marginBottom="@dimen/I8d8" android:layout_marginLeft="@dimen/I8d12" android:layout_marginRight="@dimen/I8d12" android:layout_marginTop="@dimen/I8d15" android:background="@drawable/i8wan_updata_scroll" android:fillViewport="true" android:scrollbarSize="4sp" android:scrollbarThumbVertical="@color/scrollball" android:scrollbars="vertical"> <!-- android:scrollbarStyle="outsideOverlay" android:scrollbarSize="3sp" android:fadeScrollbars="false" android:scrollbarThumbVertical="@color/scrollball"
android:background="@drawable/i8_find_password_success_message_bg"--> <TextView android:id="@+id/i8_comfirm_dialog_tipText" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" android:paddingLeft="14dp" android:paddingRight="12dp" android:textColor="#353535" android:lineSpacingExtra="8dp" android:textSize="15sp"/>
</ScrollView>

<!-- <TextView android:id="@+id/i8_comfirm_dialog_tipText" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/i8_comfirm_dialog_title" android:layout_marginBottom="@dimen/I8d8" android:layout_marginLeft="@dimen/I8d12" android:layout_marginRight="@dimen/I8d12" android:layout_marginTop="@dimen/I8d15" android:background="@drawable/i8_find_password_success_message_bg" android:gravity="center_vertical" android:textColor="#353535" android:textSize="15sp"/>-->
<RelativeLayout android:id="@+id/rl_constraintUpdata" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/i8_comfirm_dialog_scroll" android:padding="@dimen/I8d6">
<TextView android:id="@+id/tv_constraintUpdata" android:layout_width="match_parent" android:layout_height="48dp" android:layout_marginRight="@dimen/I8d6" android:background="@drawable/i8_login_bg_blue" android:gravity="center" android:text="更新" android:textColor="@android:color/white" android:textSize="17sp" android:visibility="gone"/>
<LinearLayout android:id="@+id/ll_OptionUpdata" android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:id="@+id/i8_comfirm_dialog_sureDelete" android:layout_width="@dimen/I8d0" android:layout_height="48dp" android:layout_marginRight="@dimen/I8d6" android:layout_weight="1" android:background="@drawable/i8_login_bg_blue" android:gravity="center" android:text="@string/i8_comfirm_delete" android:textColor="@android:color/white" android:textSize="17sp"/>
<TextView android:id="@+id/i8_comfirm_dialog_think" android:layout_width="@dimen/I8d0" android:layout_height="48dp" android:layout_marginLeft="@dimen/I8d6" android:layout_weight="1" android:background="@drawable/i8_popwin_comfirm_think_bg" android:gravity="center" android:text="@string/i8_comfirm_think" android:textColor="@android:color/white" android:textSize="17sp"/> </LinearLayout></RelativeLayout>


11、版本更新的bean类

public class UpdateInfo {
private int status;private UpdateInfoMessage msg;
public int getStatus() { return status;}
public UpdateInfoMessage getMsg() { return msg;}
public void setStatus(int status) { this.status = status;}
public void setMsg(UpdateInfoMessage msg) { this.msg = msg;}
public class UpdateInfoMessage {
private int id; private double version; private String desc; private int channel; private String url; private int updateType; private String md5; private int markCount; private int open; private int space;

public int getOpen() { return open; }
public int getSpace() { return space; }
public void setOpen(int open) { this.open = open; }
public void setSpace(int space) { this.space = space; }

public void setId(int id) { this.id = id; }
public void setVersion(double version) { this.version = version; }
public void setDesc(String desc) { this.desc = desc; }
public void setChannel(int channel) { this.channel = channel; }
public void setUrl(String url) { this.url = url; }
public void setUpdateType(int updateType) { this.updateType = updateType; }
public void setMd5(String md5) { this.md5 = md5; }
public void setMarkCount(int markCount) { this.markCount = markCount; }
public int getId() { return id; }
public double getVersion() { return version; }
public String getDesc() { return desc; }
public int getChannel() { return channel; }
public String getUrl() { return url; }
public int getUpdateType() { return updateType; }
public String getMd5() { return md5; }
public int getMarkCount() { return markCount; }
@Override public String toString() { return "I8WanUpdateInfoMessage{" + "id=" + id + ", version=" + version + ", desc='" + desc + '\'' + ", channel=" + channel + ", url='" + url + '\'' + ", updateType=" + updateType + ", md5='" + md5 + '\'' + ", markCount=" + markCount + '}'; }}}


到这里就结束啦


点击这里留言交流哦


浏览 17
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报