在这篇文章中(参见 android中如何下载文件并显示下载进度  )我们讲到了如何下载文件的问题,今天我介绍如何实现应用的自动更新,其中下载apk模块用到了前一篇文章中的知识。当然这只是一个实现的框架,你需要根据自己的需求是改变一些细节。

自动更新的原理

其实就是客户端将自己的版本号与服务端的版本号进行比对,版本号小于服务端则意味着有新版本,当然服务端的版本号是需要人工放上去的。

先看看效果:


为了代码的简洁,我这里用原生的alert对话框。

点击下载之后:


下载完成安装的界面我就不张贴了。

使用方法:

很简单,在需要检查更新的地方加入如下代码:

updateChecker.setCheckUrl(         "http://jcodecraeer.com/update.php"         );        
         updateChecker.checkForUpdates();



其中http://jcodecraeer.com/update.php返回的是服务器段存放的版本信息。服务端的版本信息分为三部分:

1.版本号;

2.版本描述;

3.存放apk的url(告诉客户端,在哪里下载新版本的apk);

以http://jcodecraeer.com/update.php返回的结果为例,返回的字符串具体是这样的:


         {         "url"         :         "http://www.jcodecraeer.com/***.apk"         ,         "versionCode"         :         "2"         ,         "updateMessage"         :         "1.修改了app图标 2.设备详情的显示方式"         }


这是一个json格式的字符串。

实现

有三个类:


其中

AppVersion是版本信息的模型类,基本上和服务端返回的东西是相对应的。

DownloadService是下载模块。

UpdateChecker是检查更新,调用下载模块,下载完安装的工具类。

AppVersion

package com.jcodecraeer.jcode.update;        
         public class AppVersion {        
                  private String updateMessage;        
                  private String apkUrl;        
                  private int apkCode;        
                  public static final String APK_DOWNLOAD_URL =          "url"         ;        
                  public static final String APK_UPDATE_CONTENT =          "updateMessage"         ;        
                  public static final String APK_VERSION_CODE =          "versionCode"         ;        
                  
                  public void setUpdateMessage(String updateMessage) {        
                  this         .updateMessage = updateMessage;        
                  }        
                  
                  public String getUpdateMessage() {        
                  return          updateMessage;        
                  }        
                  
                  public void setApkUrl(String apkUrl) {        
                  this         .apkUrl = apkUrl;        
                  }        
                  
                  public String getApkUrl() {        
                  return          apkUrl;        
                  }        
                  
                  public void setApkCode(int apkCode) {        
                  this         .apkCode = apkCode;        
                  }        
                  
                  public int getApkCode() {        
                  return          apkCode;        
                  }        
         }


DownloadService

package com.jcodecraeer.jcode.update;        
         import java.io.BufferedInputStream;        
         import java.io.FileOutputStream;        
         import java.io.IOException;        
         import java.io.InputStream;        
         import java.io.OutputStream;        
         import java.net.URL;        
         import java.net.URLConnection;        
         import java.util.ArrayList;        
         import java.util.Date;        
         import java.util.HashMap;        
         import java.util.Map;        
         import org.jsoup.Jsoup;        
         import org.jsoup.nodes.Document;        
         import org.jsoup.nodes.Element;        
         import org.jsoup.select.Elements;        
         import com.jcodecraeer.PullToRefreshListView;        
         import android.annotation.SuppressLint;        
         import android.app.AlertDialog;        
         import android.app.IntentService;        
         import android.content.ContentResolver;        
         import android.content.ContentUris;        
         import android.content.Context;        
         import android.content.DialogInterface;        
         import android.content.Intent;        
         import android.content.res.TypedArray;        
         import android.database.Cursor;        
         import android.graphics.Rect;        
         import android.graphics.drawable.ColorDrawable;        
         import android.net.Uri;        
         import android.os.Bundle;        
         import android.os.Handler;        
         import android.os.Message;        
         import android.os.ResultReceiver;        
         import android.provider.ContactsContract;        
         import android.provider.MediaStore.Images;        
         import android.provider.MediaStore.Images.ImageColumns;        
         import android.support.v4.app.Fragment;        
         import android.util.DisplayMetrics;        
         import android.util.Log;        
         import android.util.SparseBooleanArray;        
         import android.view.LayoutInflater;        
         import android.view.Menu;        
         import android.view.MenuInflater;        
         import android.view.MenuItem;        
         import android.view.View;        
         import android.view.ViewGroup;        
         import android.view.ViewGroup.LayoutParams;        
         import android.widget.AbsListView;        
         import android.widget.AdapterView;        
         import android.widget.AdapterView.OnItemClickListener;        
         import android.widget.GridView;        
         import android.widget.ShareActionProvider;        
         import android.widget.TextView;        
         import android.widget.Toast;        
         import android.view.ActionMode;        
         public class DownloadService extends IntentService {        
                  public static final int UPDATE_PROGRESS = 8344;        
                  public DownloadService() {        
                  super         (         "DownloadService"         );        
                  }        
                  @Override        
                  protected void onHandleIntent(Intent intent) {        
                  String urlToDownload = intent.getStringExtra(         "url"         );        
                  String fileDestination = intent.getStringExtra(         "dest"         );        
                  ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra(         "receiver"         );        
                  try          {        
                  URL url =          new          URL(urlToDownload);        
                  URLConnection connection = url.openConnection();        
                  connection.connect();        
                  // this will be useful so that you can show a typical 0-100% progress bar        
                  int fileLength = connection.getContentLength();        
                  // download the file        
                  InputStream input =          new          BufferedInputStream(connection.getInputStream());        
                  OutputStream output =          new          FileOutputStream(fileDestination);        
                  byte data[] =          new          byte[100];        
                  long total = 0;        
                  int count;        
                  while          ((count = input.read(data)) != -1) {        
                  total += count;        
                  // publishing the progress....        
                  Bundle resultData =          new          Bundle();        
                  resultData.putInt(         "progress"          ,(int) (total * 100 / fileLength));        
                  receiver.send(UPDATE_PROGRESS, resultData);        
                  output.write(data, 0, count);        
                  }        
                  output.flush();        
                  output.close();        
                  input.close();        
                  }          catch          (IOException e) {        
                  e.printStackTrace();        
                  }        
                  Bundle resultData =          new          Bundle();        
                  resultData.putInt(         "progress"          ,100);        
                  receiver.send(UPDATE_PROGRESS, resultData);        
                  }        
         }


UpdateChecker

package com.jcodecraeer.jcode.update;        
         import java.io.BufferedReader;        
         import java.io.File;        
         import java.io.IOException;        
         import java.io.InputStream;        
         import java.io.InputStreamReader;        
         import java.net.HttpURLConnection;        
         import java.net.URL;        
         import java.util.ArrayList;        
         import java.util.Date;        
         import java.util.zip.GZIPInputStream;        
         import org.json.JSONException;        
         import org.json.JSONObject;        
         import com.jcodecraeer.jcode.Code;        
         import android.app.AlertDialog;        
         import android.app.ProgressDialog;        
         import android.content.Context;        
         import android.content.DialogInterface;        
         import android.content.Intent;        
         import android.content.pm.PackageManager;        
         import android.net.Uri;        
         import android.os.Bundle;        
         import android.os.Environment;        
         import android.os.Handler;        
         import android.os.Message;        
         import android.os.ResultReceiver;        
         import android.util.Log;        
         import android.widget.Toast;        
         public class UpdateChecker{        
                  private static final String TAG =          "UpdateChecker"         ;        
                  private Context mContext;        
                  //检查版本信息的线程        
                  private Thread mThread;        
                  //版本对比地址        
                  private String mCheckUrl;        
                  private AppVersion mAppVersion;        
                  //下载apk的对话框        
                  private ProgressDialog mProgressDialog;        
                  
                  private File apkFile;        
                  public void setCheckUrl(String url) {        
                  mCheckUrl = url;        
                  }        
                  
                  public UpdateChecker(Context context) {        
                  mContext = context;        
                  // instantiate it within the onCreate method        
                  mProgressDialog =          new          ProgressDialog(context);        
                  mProgressDialog.setMessage(         "正在下载"         );        
                  mProgressDialog.setIndeterminate(         false         );        
                  mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);        
                  mProgressDialog.setCancelable(         true         );        
                  mProgressDialog.setOnCancelListener(         new          DialogInterface.OnCancelListener() {        
                  @Override        
                  public void onCancel(DialogInterface dialog) {        
                  
                  }        
                  });        
                  mProgressDialog.setOnDismissListener(         new          DialogInterface.OnDismissListener() {        
                  @Override        
                  public void onDismiss(DialogInterface dialog) {        
                  // TODO Auto-generated method stub        
                  
                  }        
                  });        
                  }        
                  
                  public void checkForUpdates() {        
                  if         (mCheckUrl ==          null         ) {        
                  //throw new Exception("checkUrl can not be null");        
                  return         ;        
                  }        
                  final  Handler handler =          new          Handler(){        
                  public void handleMessage(Message msg) {             
                  if          (msg.what == 1) {        
                  mAppVersion = (AppVersion) msg.obj;        
                  try         {        
                  int versionCode = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionCode;        
                  if          (mAppVersion.getApkCode() > versionCode) {        
                  showUpdateDialog();        
                  }          else          {        
                  //Toast.makeText(mContext, "已经是最新版本", Toast.LENGTH_SHORT).show();        
                  }        
                  }         catch          (PackageManager.NameNotFoundException ignored) {        
                  //        
                  }        
                  }        
                  }        
                  };        
                  
                  mThread =          new          Thread() {        
                  @Override        
                  public void run() {        
                  //if (isNetworkAvailable(mContext)) {        
                  Message msg =          new          Message();        
                  String json = sendPost();        
                  Log.i(         "jianghejie"         ,         "json = "         +json);        
                  if         (json!=         null         ){        
                  AppVersion appVersion = parseJson(json);        
                  msg.what = 1;        
                  msg.obj = appVersion;        
                  handler.sendMessage(msg);        
                  }         else         {        
                  Log.e(TAG,          "can't get app update json"         );        
                  }        
                  }        
                  };        
                  mThread.start();        
                  }        
                  protected String sendPost() {        
                  HttpURLConnection uRLConnection =          null         ;        
                  InputStream is =          null         ;        
                  BufferedReader buffer =          null         ;        
                  String result =          null         ;        
                  try          {        
                  URL url =          new          URL(mCheckUrl);        
                  uRLConnection = (HttpURLConnection) url.openConnection();        
                  uRLConnection.setDoInput(         true         );        
                  uRLConnection.setDoOutput(         true         );        
                  uRLConnection.setRequestMethod(         "POST"         );        
                  uRLConnection.setUseCaches(         false         );        
                  uRLConnection.setConnectTimeout(10 * 1000);        
                  uRLConnection.setReadTimeout(10 * 1000);        
                  uRLConnection.setInstanceFollowRedirects(         false         );        
                  uRLConnection.setRequestProperty(         "Connection"         ,          "Keep-Alive"         );        
                  uRLConnection.setRequestProperty(         "Charset"         ,          "UTF-8"         );        
                  uRLConnection.setRequestProperty(         "Accept-Encoding"         ,          "gzip, deflate"         );        
                  uRLConnection.setRequestProperty(         "Content-Type"         ,          "application/json"         );        
                  uRLConnection.connect();        
                  is = uRLConnection.getInputStream();        
                  String content_encode = uRLConnection.getContentEncoding();        
                  if          (         null          != content_encode && !         ""         .equals(content_encode) && content_encode.equals(         "gzip"         )) {        
                  is =          new          GZIPInputStream(is);        
                  }        
                  buffer =          new          BufferedReader(         new          InputStreamReader(is));        
                  StringBuilder strBuilder =          new          StringBuilder();        
                  String line;        
                  while          ((line = buffer.readLine()) !=          null         ) {        
                  strBuilder.append(line);        
                  }        
                  result = strBuilder.toString();        
                  }          catch          (Exception e) {        
                  Log.e(TAG,          "http post error"         , e);        
                  } finally {        
                  if         (buffer!=         null         ){        
                  try          {        
                  buffer.close();        
                  }          catch          (IOException e) {        
                  e.printStackTrace();        
                  }        
                  }        
                  if         (is!=         null         ){        
                  try          {        
                  is.close();        
                  }          catch          (IOException e) {        
                  e.printStackTrace();        
                  }        
                  }        
                  if         (uRLConnection!=         null         ){        
                  uRLConnection.disconnect();        
                  }        
                  }        
                  return          result;        
                  }        
                  private AppVersion parseJson(String json) {        
                  AppVersion appVersion =          new          AppVersion();        
                  try          {        
                  JSONObject obj =          new          JSONObject(json);        
                  String updateMessage = obj.getString(AppVersion.APK_UPDATE_CONTENT);        
                  String apkUrl = obj.getString(AppVersion.APK_DOWNLOAD_URL);        
                  int apkCode = obj.getInt(AppVersion.APK_VERSION_CODE);        
                  appVersion.setApkCode(apkCode);        
                  appVersion.setApkUrl(apkUrl);        
                  appVersion.setUpdateMessage(updateMessage);        
                  }          catch          (JSONException e) {        
                  Log.e(TAG,          "parse json error"         , e);        
                  }        
                  return          appVersion;        
                  }        
                  
                  public void showUpdateDialog() {        
                  AlertDialog.Builder builder =          new          AlertDialog.Builder(mContext);        
                  //builder.setIcon(R.drawable.icon);        
                  builder.setTitle(         "有新版本"         );        
                  builder.setMessage(mAppVersion.getUpdateMessage());        
                  builder.setPositiveButton(         "下载"         ,        
                  new          DialogInterface.OnClickListener() {        
                  public void onClick(DialogInterface dialog, int whichButton) {        
                  downLoadApk();        
                  }        
                  });        
                  builder.setNegativeButton(         "忽略"         ,        
                  new          DialogInterface.OnClickListener() {        
                  public void onClick(DialogInterface dialog, int whichButton) {        
                  
                  }        
                  });        
                  builder.show();              
                  }        
                  
                  public void downLoadApk() {        
                  String apkUrl = mAppVersion.getApkUrl();        
                  String dir = mContext.getExternalFilesDir(          "apk"         ).getAbsolutePath();        
                  File folder = Environment.getExternalStoragePublicDirectory(dir);        
                  if         (folder.exists() && folder.isDirectory()) {        
                  //do nothing        
                  }         else          {        
                  folder.mkdirs();        
                  }        
                  String filename = apkUrl.substring(apkUrl.lastIndexOf(         "/"         ),apkUrl.length());        
                  String destinationFilePath =  dir +          "/"          + filename;        
                  apkFile =          new          File(destinationFilePath);        
                  mProgressDialog.show();        
                  Intent intent =          new          Intent(mContext, DownloadService.class);        
                  intent.putExtra(         "url"         , apkUrl);        
                  intent.putExtra(         "dest"         , destinationFilePath);        
                  intent.putExtra(         "receiver"         ,          new          DownloadReceiver(         new          Handler()));        
                  mContext.startService(intent);        
                  }        
                  
                  private class DownloadReceiver extends ResultReceiver{        
                  public DownloadReceiver(Handler handler) {        
                  super         (handler);        
                  }        
                  @Override        
                  protected void onReceiveResult(int resultCode, Bundle resultData) {        
                  super         .onReceiveResult(resultCode, resultData);        
                  if          (resultCode == DownloadService.UPDATE_PROGRESS) {        
                  int progress = resultData.getInt(         "progress"         );        
                  mProgressDialog.setProgress(progress);        
                  if          (progress == 100) {        
                  mProgressDialog.dismiss();        
                  //如果没有设置SDCard写权限,或者没有sdcard,apk文件保存在内存中,需要授予权限才能安装        
                  String[] command = {         "chmod"         ,         "777"         ,apkFile.toString()};        
                  try         {        
                  ProcessBuilder builder =          new          ProcessBuilder(command);        
                  builder.start();        
                  Intent intent =          new          Intent(Intent.ACTION_VIEW);        
                  intent.setDataAndType(Uri.fromFile(apkFile),          "application/vnd.android.package-archive"         );        
                  mContext.startActivity(intent);        
                  }         catch          (Exception e){        
                  
                  }        
                  }        
                  }        
                  }        
                  }        
         }



所有代码都已经贴出来了。

这个代码是完全可以用的,但是有些细节问题需要改进。