转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46916021

一、概述

相信大家都遇到过这种情况,就是在Android手机中的应用,往往在应用的设置里面,都会有一个检查更新的功能,如果应用开发商或者运营商发布了新的应用版本,点击设置里面检查更新的按钮就会提示用户有新版本,是否需要更新,如果用户点击了“立即更新”后,会将应用开发商或运营商服务器上最新的应用版本更新到手机中,如果用户没有选择立即更新,则不会更新手机应用程序。

二、实现原理

服务端存放有一个描述应用版本信息的文件,我们这个程序中姑且将这个版本描述文件定义为XML格式,在这个文件中定义了应用的版本号,名称大小,下载链接,更新描述等信息。这个文件中所描述的应用版本号与服务器上发布的版本信息对应。当手机用户点击应用设置中检查更新的功能时,会将手机中安装的应用版本号与服务器中描述应用版本信息的文件版本号比较,如果手机中的版本号小于文件中的版本号,则提示用户有新版本,是否需要更新。否则,提示用户手机中安装的应用程序已是最新版本。

三、服务端实现

这里服务端需要一个描述应用更新信息的XML文件,一个新版本的APK文件和一个下载APK文件的接口路径。

1、描述应用更新信息的XML文件

在这个文件中定义了要更新的应用的版本号,名称大小,下载链接,更新描述等信息,它作为手机客户端应用程序与服务端应用程序对比的媒介。这个文件中描述的版本信息与更新信息与服务端存放的APK信息完全一致。

具体实现如下:

<?xml version="1.0" encoding="utf-8"?>
<update><version>2</version><versiondesc>0.2</versiondesc><name>update.apk</name><size>6313406</size><url>http://192.168.254.103:9999/Wechat/app/get-app/update.apk</url><description>我们的产品上线啦</description>
</update>

2、实现更新APK文件的接口类MobileVersionAPI

在这个类中主要是实现了以文件流的形式来响应Android客户端的请求,从而实现XML文件的下载与APK文件的下载功能。

具体实现代码如下:

package com.cdsmartlink.system.mobile.api.version;import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;import com.cdsmartlink.utils.common.FileCopyUtils;
import com.cdsmartlink.utils.common.StringUtils;/*** 获取App版本的api* @author liuyazhuang**/
@Controller
public class MobileVersionAPI {private static final String TYPE_APK = "apk";private static final String TYPE_XML = "xml";/*** 获取Android版本比对文件* @param request* @param response*/@RequestMapping(value="/app/get-android-version/android.xml")public void getAndroidVersionXml(HttpServletRequest request,HttpServletResponse response){try {this.downloadFile(request, response,TYPE_XML ,"version/android/version.xml");} catch (IOException e) {e.printStackTrace();}}/*** 获取IOS版本对比文件* @param request* @param response*/@RequestMapping(value="/app/get-ios-version/ios.xml")public void getIOSVersionXml(HttpServletRequest request,HttpServletResponse response){try {this.downloadFile(request, response,TYPE_XML, "version/ios/version.xml");} catch (IOException e) {e.printStackTrace();}}/*** 下载* @param request* @param response*/@RequestMapping(value="/app/get-app/update.ipa")public void getIOSApp(HttpServletRequest request,HttpServletResponse response){try {this.downloadFile(request, response,TYPE_APK, "app/update.ipa");} catch (IOException e) {e.printStackTrace();}}/*** 获取IOS apk* @param request* @param response*/@RequestMapping(value="/app/get-web-app/update.ipa")public void getIOSWebApp(HttpServletRequest request,HttpServletResponse response){try {this.download(request, response, "app/update.ipa","application/octet-stream");} catch (Exception e) {e.printStackTrace();}}/*** 下载app* @param request* @param response*/@RequestMapping(value="/app/get-app/update.apk")public void getApp(HttpServletRequest request,HttpServletResponse response){try {this.downloadFile(request, response,TYPE_APK, "app/update.apk");} catch (IOException e) {e.printStackTrace();}}@RequestMapping(value="/app/get-web-app/update.apk")public void getWebApp(HttpServletRequest request,HttpServletResponse response){try {this.download(request, response, "app/update.apk","application/octet-stream");} catch (Exception e) {e.printStackTrace();}}/*** 网页下载* @param request* @param response* @param fileName* @param contentType* @throws Exception*/public static void download(HttpServletRequest request,  HttpServletResponse response, String fileName, String contentType) throws Exception {  String path = request.getServletContext().getRealPath("/");if(StringUtils.isEmpty(path)) return;if(path.contains("\\"))path = path.replace("\\", "/");path = path.concat(fileName);
//        response.setContentType("text/html;charset=UTF-8");  request.setCharacterEncoding("UTF-8");  BufferedInputStream bis = null;  BufferedOutputStream bos = null;  File file = new File(path);if(file==null || !file.exists() || !file.isFile())return;long fileLength = file.length();  response.setContentType(contentType);  response.setHeader("Content-disposition", "attachment; filename="+new String(fileName.getBytes("utf-8"), "ISO8859-1"));  response.setHeader("Content-Length", String.valueOf(fileLength));  bis = new BufferedInputStream(new FileInputStream(path));  bos = new BufferedOutputStream(response.getOutputStream());
//        byte[] buff = new byte[2048];
//        int bytesRead;
//        while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
//            bos.write(buff, 0, bytesRead);
//        }  FileCopyUtils.copy(bis, bos);
}  /*** 下载文件(未下载的时候要显示文件的大小信息)* @param request* @param response* @param type*/private void downloadFile(HttpServletRequest request,HttpServletResponse response,String type,String fileName)throws IOException{String path = request.getServletContext().getRealPath("/");if(StringUtils.isEmpty(path)) return;if(path.contains("\\"))path = path.replace("\\", "/");path = path.concat(fileName);File file = new File(path);if(file == null || !file.exists()|| !file.isFile())return;response.setHeader("Content-Length", String.valueOf(file.length()));  InputStream in = new FileInputStream(file);BufferedInputStream bin = new BufferedInputStream(in);OutputStream out = response.getOutputStream();BufferedOutputStream bout = new BufferedOutputStream(out);FileCopyUtils.copy(bin, bout);}
}

3、资源存放

在这个示例中我在WebRoot下面新建目录叫做app,将要更新的apk文件存放到目录下;在WebRoot目录下新建目录叫做version/android,将更新信息描述文件放在version/android目录中。

整体结构图如下:

四、Android实现

通过上面两篇博文《Android之——多线程下载示例》与《Android之——多线程断点续传下载示例》的学习,我们对Android中多线程的下载有了较为深入的理解,这里我们将版本更新的功能同样放在子线程中去执行。首先,用户点击界面上的更新按钮,向服务器发送请求,服务器将更新描述文件响应给Android客户端,客户端通过解析这个XML文件,获取文件中描述的版本号与本应用程序的版本号比较,如果文件中描述的版本号大于本应用程序的版本号,则提示用户有新版本,是否更新;否则提示用户当前已是最新版本。

1、主界面布局实现

此布局实现起来比较简单,就是在界面上显示一个按钮,用户点击按钮实现程序的更新操作。

具体实现的代码如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"android:orientation="vertical"tools:context=".MainActivity" ><Buttonandroid:layout_centerInParent="true"android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="update"android:text="更新"/></RelativeLayout>

2、更新操作界面布局实现

在这个界面中主要是显示更新过程中显示的更新信息,更新进度,版本说明等信息。

具体实现代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="wrap_content" ><!-- 版本号 --><ScrollViewandroid:layout_width="match_parent"android:layout_height="220dp" ><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:orientation="vertical" ><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="8dp"android:orientation="horizontal" ><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="版本号:"android:textSize="16sp" /><TextViewandroid:id="@+id/vesion_num_textview"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="16sp" /></LinearLayout><!-- 更新文件大小 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="8dp"android:orientation="horizontal" ><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="文件大小:"android:textSize="16sp" /><TextViewandroid:id="@+id/file_size_textview"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="16sp" /></LinearLayout><!-- 更新内容 --><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="8dp"android:text="更新内容:"android:textSize="16sp" /><LinearLayoutandroid:id="@+id/update_content_layout"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="8dp"android:orientation="vertical" ></LinearLayout></LinearLayout></ScrollView></RelativeLayout>

3、新建操作Android程序的工具类AppUtils

在这个程序中,我主要封装了一些获取应用程序基本信息的方法。比如获取应用版本号,版本名称和安装情况等等。

具体实现代码如下:

package com.lyz.utils.app;import java.util.List;import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.view.WindowManager;/*** 操作当前的App的工具类* * @author liuyazhuang* */
public final class AppUtils {/*** 获取软件版本号* * @param context* @return*/public static int getVersionCode(Context context) {int versionCode = 0;try {// 获取软件版本号,对应AndroidManifest.xml下android:versionCodeversionCode = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;} catch (NameNotFoundException e) {e.printStackTrace();}return versionCode;}/*** 获取当前应用的版本号* * @param pName*            :应用程序包名* @param mContext*            :* @return*/public static int getVersionCode(String pName, Context mContext) throws Exception {PackageInfo pinfo = mContext.getPackageManager().getPackageInfo(pName,PackageManager.GET_CONFIGURATIONS);return pinfo.versionCode;}/*** 获取当前应用的版本名称* * @param pName* @param mContext* @return* @throws Exception*/public static String getVersionName(String pName, Context mContext) {try {PackageInfo pinfo = mContext.getPackageManager().getPackageInfo(pName,PackageManager.GET_CONFIGURATIONS);return pinfo.versionName;} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}/*** 判断该应用在手机中的安装情况* * @param packageName*            要判断应用的包名*/public static boolean checkAPK(String packageName, Context context) {List<PackageInfo> pakageinfos = context.getPackageManager().getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);for (PackageInfo pi : pakageinfos) {String pi_packageName = pi.packageName;if (packageName.endsWith(pi_packageName)) {return true;}}return false;}/*** 退出当前应用程序* * @param context*/public static void exitCurrentProgress(Context context) {Intent startMain = new Intent(Intent.ACTION_MAIN);startMain.addCategory(Intent.CATEGORY_HOME);startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(startMain);System.exit(0);}/*** 下载新的app* * @param url* @param context*/public static void downloadNewApp(String url, Context context) {Intent it = new Intent("android.intent.action.VIEW", Uri.parse(url));context.startActivity(it);}/*** 隐藏软键盘* * @param context*/public static void hideSoftKeyBoard(Activity context) {context.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);}/*** 将Drawable转化为Bitmap* * @param drawable* @return*/public static Bitmap drawableToBitmap(Drawable drawable) {int width = drawable.getIntrinsicWidth();int height = drawable.getIntrinsicHeight();Bitmap bitmap = Bitmap.createBitmap(width, height, drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888: Bitmap.Config.RGB_565);Canvas canvas = new Canvas(bitmap);drawable.setBounds(0, 0, width, height);drawable.draw(canvas);return bitmap;}
}

4、自动以广播接收者来接收者PkInstallReceiver

主要接收更新功能发出的广播来进行应用程序的安装操作,在这个类中,主要实现了应用程序的自动安装功能

具体实现的代码如下:

package com.lyz.utils.update.broadcast;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;/*** 自定义广播接收者* 来实现自动安装APK的功能* * @author liuyazhuang* */
public class PkInstallReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 监听安装程序if (intent.getAction().equals("android.intent.action.PACKAGE_ADDED")) {String packageName = intent.getDataString().substring(8);Log.i("安装应用", "安装:" + packageName + "包名的程序");Intent newIntent = new Intent();newIntent.setClassName("com.lyz.update.activity", "com.lyz.update.activity.MainActivity");newIntent.setAction("android.intent.action.MAIN");newIntent.addCategory("android.intent.category.LAUNCHER");newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(newIntent);}//监听卸载
//      if (intent.getAction().equals("android.intent.action.PACKAGE_REMOVED")) {
//          String packageName = intent.getDataString().substring(8);
//          Log.i("卸载应用", "卸载:" + packageName + "包名的程序");
//      }}}

5、新建UpdateDatas类

为了使应用程序的开发更加符合面向对象的封装,我们将更新文件的长度和文件流信息封装为一个实体类,以实现对这些信息的封装操作。

具体实现的代码如下:

package com.lyz.utils.update;import java.io.InputStream;
import java.io.Serializable;/*** 封装更新文件的数据流和长度* @author liuyazhuang**/
public class UpdateDatas implements Serializable {private static final long serialVersionUID = 9210856546501387030L;//长度信息private Integer length;//文件流信息private InputStream in;public Integer getLength() {return length;}public void setLength(Integer length) {this.length = length;}public InputStream getIn() {return in;}public void setIn(InputStream in) {this.in = in;}}

6、新建XML文件的解析类ParseXmlUtils

这个类主要负责XML文件的解析操作,将获取到的文件输入流解析成一个Map对象返回,其中Map中存放的每一个键值对就代表XML文件中的每一个节点信息。

具体实现的代码如下:

package com.lyz.utils.update;import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;import com.lyz.utils.common.XmlUtils;
/*** 解析版本更新文件* @author liuyazhuang**/
public class ParseXmlUtils {//版本信息public static final String Version = "version";//名称public static final String Name = "name";//下载的链接public static final String Url = "url";//项目更新描述public static final String Description = "description";//大小public static final String Size = "size";//用户看到的版本信息public static final String VERSION_DESC = "versiondesc";/*** 将输入流转化为map* @param inStream* @return* @throws Exception*/public static Map<String, String> parseXml(InputStream inStream) throws Exception {NodeList childNodes = XmlUtils.parseXml(inStream);if(childNodes == null) return null;Map<String, String> hashMap = new HashMap<String, String>();for (int j = 0; j < childNodes.getLength(); j++) {// 遍历子节点Node childNode = childNodes.item(j);if (childNode.getNodeType() == Node.ELEMENT_NODE) {Element childElement = (Element) childNode;// 版本号if (Version.equals(childElement.getNodeName())) {hashMap.put(Version, childElement.getFirstChild().getNodeValue());}// 软件名称else if ((Name.equals(childElement.getNodeName()))) {hashMap.put(Name, childElement.getFirstChild().getNodeValue());}// 下载地址else if ((Url.equals(childElement.getNodeName()))) {hashMap.put(Url, childElement.getFirstChild().getNodeValue());//应用更新描述}else if(Description.equals(childElement.getNodeName())){hashMap.put(Description, childElement.getFirstChild().getNodeValue());}//大小else if(Size.equals(childElement.getNodeName())){hashMap.put(Size, childElement.getFirstChild().getNodeValue());}//用户所看到的版本号else if(VERSION_DESC.equals(childElement.getNodeName())){hashMap.put(VERSION_DESC, childElement.getFirstChild().getNodeValue());}}}return hashMap;}
}

7、新建应用程序的核心类UpdateManager

这个类是整个应用程序的核心实现类,在这个类中,我们实现了文件的下载,版本对比与是否更新的操作,我们同样将文件的下载放在子线程中执行,子线程对过Handler与Message机制与主线程交互数据,通知主线程更新UI,同时在这个类的构造方法中注册广播接收者。在解析XML文件时,文件中的版本号不大于手机中应用版本号的时候取消注册广播接收者,同时在下载最新APK文件结束,安装完成后取消注册广播接收者。

具体实现代码如下:

package com.lyz.utils.update;import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;import com.lyz.update.activity.R;
import com.lyz.utils.app.AppUtils;
import com.lyz.utils.common.FileConnectionUtils;
import com.lyz.utils.common.FileUtils;
import com.lyz.utils.common.StringUtils;
import com.lyz.utils.update.broadcast.PkInstallReceiver;/*** 应用更新类* @author liuyazhuang**/
public class UpdateManager {//获取更新文件的基本路径private static final String URL  = "http://192.168.254.103:9999/Wechat";private ProgressDialog mProgressDialog = null;/* 下载中 */private static final int DOWNLOAD = 1;/* 下载结束 */private static final int DOWNLOAD_FINISH = 2;// 解析下载的版本文件private static final int PARSE_XML = 3;//版本号private static final String VERSION = "version";//长度private static final String LENGTH = "length";/* 保存解析的XML信息 */private Map<String, String> mHashMap;/* 下载保存路径 */private String mSavePath;/* 记录进度条数量 */private int progress;
//  int count;/* 是否取消更新 */private boolean cancelUpdate = false;private int count;private Context mContext;/* 更新进度条 */private PkInstallReceiver pkInstallReceiver;private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {// 正在下载case DOWNLOAD:// 设置进度条位置mProgressDialog.incrementProgressBy(progress);//设置第一进度值   (数值)mProgressDialog.setProgress(count);
//              mProgressDialog.setSecondaryProgress(10);break;case DOWNLOAD_FINISH:// 安装文件installApk();if(pkInstallReceiver != null){mContext.unregisterReceiver(pkInstallReceiver);}break;case PARSE_XML:
//              int serviceCode = (Integer) msg.obj;Map<String, Integer> map = (Map<String, Integer>) msg.obj;int serviceCode = map.get(VERSION);int length = map.get(LENGTH);// 获取当前软件版本int versionCode = AppUtils.getVersionCode(mContext);if (serviceCode > versionCode) {// 显示提示对话框showNoticeDialog(length);} else {Toast.makeText(mContext, R.string.soft_update_no,Toast.LENGTH_LONG).show();if(pkInstallReceiver != null){mContext.unregisterReceiver(pkInstallReceiver);}}break;default:break;}};};/*** 构造方法* @param context*/public UpdateManager(Context context) {this.mContext = context;pkInstallReceiver = new PkInstallReceiver();IntentFilter intentFilter=new IntentFilter(); intentFilter.addAction("android.intent.action.PACKAGE_ADDED"); intentFilter.addAction("android.intent.action.PACKAGE_REMOVED"); intentFilter.addDataScheme("package");this.mContext.registerReceiver(pkInstallReceiver, intentFilter);}/*** 检查软件是否有更新版本* @return*/public void checkUpdate() {new Thread(new Runnable() {@Overridepublic void run() {try {InputStream inStream = FileUtils.getInputStreamFromUrl(URL+"/app/get-android-version/android.xml");// 解析XML文件。 由于XML文件比较小,因此使用DOM方式进行解析mHashMap = ParseXmlUtils.parseXml(inStream);if (null != mHashMap) {String size = mHashMap.get(ParseXmlUtils.Size);int length = StringUtils.isEmpty(size) ? 0 : Integer.parseInt(size);Map<String, Integer> map = new HashMap<String, Integer>();int serviceCode = Integer.valueOf(mHashMap.get(ParseXmlUtils.Version));map.put(VERSION, serviceCode);map.put(LENGTH, length);Message msg = new Message();msg.what = PARSE_XML;msg.obj = map;mHandler.sendMessage(msg);}} catch (Exception e) {e.printStackTrace();}}}).start();}/*** 显示软件更新对话框*/private void showNoticeDialog(final int length) {// 构造对话框AlertDialog.Builder builder = new Builder(mContext);builder.setTitle(R.string.soft_update_title);
//      builder.setMessage(R.string.soft_update_info);//构建更新提示内容布局View view =  LayoutInflater.from(mContext).inflate(R.layout.softupdate_progress, null);TextView tvVersion = (TextView) view.findViewById(R.id.vesion_num_textview);TextView tvSize = (TextView) view.findViewById(R.id.file_size_textview);LinearLayout layout = (LinearLayout) view.findViewById(R.id.update_content_layout);//设置要更新至的版本号tvVersion.setText(mHashMap.get(ParseXmlUtils.VERSION_DESC));String size = Double.toString(((double)length) / (1000*1000));//获取更新文件的大小tvSize.setText(size.substring(0,size.indexOf(".") +3)+"M");//要更新的内容String [] updateContetnts = mHashMap.get(ParseXmlUtils.Description).split(";");for (int i = 0; i < updateContetnts.length; i++) {TextView tv = new TextView(mContext);tv.setText((i+1)+"."+updateContetnts[i]);tv.setTextSize(16);layout.addView(tv);}builder.setView(view);// 更新builder.setPositiveButton(R.string.soft_update_updatebtn,new OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();// 显示下载对话框showDownloadDialog(length);}});// 稍后更新builder.setNegativeButton(R.string.soft_update_later,new OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();}});Dialog noticeDialog = builder.create();noticeDialog.show();}/*** 显示软件下载对话框*/@SuppressWarnings("deprecation")public void showDownloadDialog(int length) {mProgressDialog = new ProgressDialog(mContext);
//      mProgressDialog.setIcon(R.drawable.app_icon);mProgressDialog.setTitle(R.string.soft_updating);//        按对话框以外的地方不起作用。按返回键也不起作用mProgressDialog.setCancelable(false);mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//设置文件大小的显示  --  使用mProgressDialog.setMax(length);mProgressDialog.setButton("取消",new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog,int whichButton) {// 这里添加点击后的逻辑mProgressDialog.dismiss();// 设置取消状态cancelUpdate = true;}});mProgressDialog.show();// 下载文件downloadApk();}/*** 下载apk文件*/private void downloadApk() {// 启动新线程下载软件new downloadApkThread().start();}//下载APK的线程private class downloadApkThread extends Thread {@Overridepublic void run() {try {// 判断SD卡是否存在,并且是否具有读写权限if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {// 获得存储卡的路径StringBuffer sb = new StringBuffer();sb.append(Environment.getExternalStorageDirectory()+ "/").append("download");mSavePath = sb.toString();UpdateDatas datas = FileConnectionUtils.getDatasFromUrl(mHashMap.get(ParseXmlUtils.Url));int length = datas.getLength();InputStream is = datas.getIn();File file = new File(mSavePath);// 判断文件目录是否存在if (!file.exists()) {file.mkdir();}File apkFile = new File(mSavePath, mHashMap.get(ParseXmlUtils.Name));FileOutputStream fos = new FileOutputStream(apkFile);
//                  int count = 0;// 缓存byte buf[] = new byte[1024];// 写入到文件中do {int numread = is.read(buf);count = count + numread;// 计算进度条位置progress = (int) (((float) count / length) * 100);
//                      Log.i("下载应用的进度", String.valueOf(progress));// 更新进度mHandler.sendEmptyMessage(DOWNLOAD);if (numread <= 0) {                         // 下载完成mHandler.sendEmptyMessage(DOWNLOAD_FINISH);break;}// 写入文件fos.write(buf, 0, numread);} while (!cancelUpdate);// 点击取消就停止下载.fos.close();is.close();}} catch (Exception e) {e.printStackTrace();}// 取消下载对话框显示mProgressDialog.dismiss();}};/*** 安装APK文件*/private void installApk() {File apkfile = new File(mSavePath, mHashMap.get(ParseXmlUtils.Name));if (!apkfile.exists()) {return;}// 通过Intent安装APK文件Intent i = new Intent(Intent.ACTION_VIEW);i.setDataAndType(Uri.parse("file://" + apkfile.toString()),"application/vnd.android.package-archive");mContext.startActivity(i);}
}

8、完善MainActivity

这个类中很简单,只是实现了按钮的点击事件。

具体代码实现如下:

package com.lyz.update.activity;import com.lyz.utils.update.UpdateManager;import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;/*** 应用程序的入口* @author liuyazhuang**/
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}public void update(View v){UpdateManager manager = new UpdateManager(this);manager.checkUpdate();}
}

五、应用授权

最后不要忘记我们的应用程序要联网操作,要给应用程序添加网络授权。

具体实现如下

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.lyz.update.activity"android:versionCode="2"android:versionName="0.2" ><uses-permission android:name="android.permission.INTERNET" /><uses-sdkandroid:minSdkVersion="8"android:targetSdkVersion="18" /><applicationandroid:allowBackup="true"android:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme" ><activityandroid:name="com.lyz.update.activity.MainActivity"android:label="@string/app_name" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

六、运行效果

提醒:大家可以到http://download.csdn.net/detail/l1028386804/8906859链接获取完整Android应用更新示例代码

Android之——应用更新功能相关推荐

  1. Android 程序自动更新功能模块实现

    2019独角兽企业重金招聘Python工程师标准>>> Android 程序自动更新功能模块实现 在程序启动的时候检测服务器上有没有对应版本更新,如果有更新,提示用户是否更新. 在程 ...

  2. Android应用自动更新功能实现使用AsyncTask!

    为什么80%的码农都做不了架构师?>>>    我所开发应用不是面向大众的应用,所以无法放到应用市场去让大家下载,然后通过应用市场更新.所以我必要做一个应用自动更新功能.但是不难,T ...

  3. android n 支持机型,Android N无缝更新功能不适于现有机型

    原标题:Android N无缝更新功能不适于现有机型 [天极网手机频道]谷歌在I/O大会中宣布AndroidN引入了Chrome OS中的"无缝更新"功能,该功能允许用户下载系统更 ...

  4. android自动更新demo,Android程序自动更新功能模块的实现方法【附完整demo源码下载】...

    本文实例讲述了Android程序自动更新功能模块的实现方法.分享给大家供大家参考,具体如下: 在程序启动的时候检测服务器上有没有对应版本更新,如果有更新,提示用户是否更新. 在程序启动的时候首先调用更 ...

  5. android app实现更新功能

    功能演示 Android App自动更新基本上是每个App都需具备的功能.网上有各种更新方式,但基本都是往年的了,最近刚查资料写完一个app更新功能,发现了许多app更新的博客内没有详细说明的小问题, ...

  6. android 360自动更新,Android应用自动更新功能的实现!!!

    大家好,发现半年没有更新博客了,最近一直都比较忙,决定在凌晨 英超 阿森纳VS富勒姆 中场休息的时候,给大家分享Android里应用版本更新功能这一块的实现. 一个好的应用软件都是需要好的维护,从初出 ...

  7. Android应用自动更新功能的代码实现

    由于Android项目开源所致,市面上出现了N多安卓软件市场.为了让我们开发的软件有更多的用户使用,我们需要向N多市场发布,软件升级后,我们也必须到安卓市场上进行更新,给我们增加了工作量.因此我们有必 ...

  8. Android 实现系统更新功能

    /*** 安装新版本* @param context 当前实例上下文*/public void InstallApk(Context context,String fileName){File f = ...

  9. 小米android8.0新功能,性能大优化:小米A1已向所有用户推Android 8.0更新!

    原标题:性能大优化:小米A1已向所有用户推Android 8.0更新! 近日,小米方面发布消息表示,已经正式向所有的小米A1手机用户推送Android 8.0系统更新,用户在检查更新当中就可以直接OT ...

最新文章

  1. 2022-2028年中国电压力锅市场投资分析及前景预测报告
  2. docker 查看容器日志大小 清除指定容器日志
  3. C++11 运行时变量类型判断
  4. scala学习 之 环境搭建(一)
  5. 曙光服务器优势,5大核心优势 探秘曙光Cloudview三大平台
  6. [视频]产品营销之拍出好电子产品,Peter Belanger是如何为苹果产品拍照的
  7. 金字塔c_C/C++编程笔记:C语言入门题之正倒金字塔,正反三角形代码详解
  8. 2.从Paxos到Zookeeper分布式一致性原理与实践---一致性协议
  9. 如何使用qtp检查网页中显示的文字颜色为指定的颜色
  10. 数字信号处理实验matlab,数字信号处理实验Matlab代码
  11. nb-iot信号测试软件,NB-IOT测试仪
  12. 《人工智能狂潮》读后感——什么是人工智能?(一)
  13. 史上最全自媒体推广工具
  14. python爬取南京市房价_屌丝想买房,爬取南京20000多套二手房|上篇
  15. 【考研英语语法】十大词性
  16. 由浅入深 学习 Android Binder(十一) binder线程池
  17. 不建议轻易做量化玩高频
  18. 自定义alert和confirm的效果,个人感觉蛮好使用的
  19. Java时间日期类处理(LocalDateTime、LocalDate相关操作、获取周几、工作日休息日节假日判定)
  20. huan shen总结

热门文章

  1. 区块链溯源防伪新零售创新企业发展模式,带动企业持续健康发展
  2. 网络安全等级保护测评高风险判定-安全管理中心-5
  3. sqlmap超详细讲解
  4. 理解这25个概念,你的人工智能,深度学习,机器学习才算入门!
  5. Scrapy框架学习 - 爬取Boss直聘网Python职位信息
  6. js正则表达式 验证手机号,email地址和邮政编码
  7. 伤脑筋的bridge模式-我不明白。。
  8. 最新的ssm毕业设计题目50例
  9. 美女------聚会
  10. android的u盘如何卸载,Android教程: 使用RE Manager删除系统自己的软件