以下内容部分来自网络资源,这里做了个小小的总结。
目前Android开发中一般采用的是CS模式,对于Apk的升级就需要有Server端支持。基本思路:我们将升级版本以及一个记录升级版本的配置文件(一般采用json array/Xml格式)放在服务器端,当客户端Client初始化时,如果检测到Server服务器端有更新的版本,也就是配置文件中的版本信息不同,那么就将在Server端的升级版本以Http方式连接,将其下载下来,然后调用android api接口进行升级。
这里我们以Xml为例:
客户端与服务端约定如下:
1,版本信息记录在服务器端的version.xml文件中;
2,客户端每次运行时,先从服务器获取version.xml文件,然后对比该文件中的版本信息version与当前客户端的version是否一致,如果不一致,弹出更新选择框,由用户决定是否更新,如果更新,则启动更新程序;
3,更新程序:客户端从制定的URL中获取APK下载到本地,更新进度,如果下载完毕,启动APK安装程序,同时,允许用户中途取消下载,取消安装。
基于上述约定即可编写程序。
下面介绍一下具体过程:
1,定义version.xml文件
<?xml version="1.0" encoding="UTF-8"?> <update> <version>2.0_20141230</version> <versionCode>2</versionCode> <updateTime>2014-12-30</updateTime> <apkName>Updatedemo.apk</apkName> <downloadURL>http://localhost:8080/UpdateDemo.apk</downloadURL> <updateinformation>Update to the new version</updateinformation> </update>
2,定义version实体类VersionInformation.java
package com.client.update; public class VersionInformation { //Discription of version private String mVersion; //Update time private String mUpdateTime; //Update url private String mDownloadURL; //Update information private String mUpdateInformation; //Version number private int mVersionCode; //Name of android application private String mApkName; public String getVersion() { return mVersion; } public void setVersion(String version) { this.mVersion = version; } public String getUpdateTime() { return mUpdateTime; } public void setUpdateTime(String updateTime) { this.mUpdateTime = updateTime; } public String getDownloadURL() { return mDownloadURL; } public void setDownloadURL(String downloadURL) { this.mDownloadURL = downloadURL; } public String getUpdateInfomation() { return mUpdateInformation; } public void setUpdateInfomation(String updateInfo) { this.mUpdateInformation = updateInfo; } public int getVersionCode() { return mVersionCode; } public void setVersionCode(int versionCode) { this.mVersionCode = versionCode; } public String getApkName() { return mApkName; } public void setApkName(String apkName) { this.mApkName = apkName; } }
3,定义version.xml解析类XMLParserUtils.java
package com.client.update; import java.io.IOException; import java.io.InputStream; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; public class XMLParserUtils { //get VersionInformation entry from version.xml public static VersionInformation getUpdateInformation(InputStream is) { VersionInformation info = new VersionInformation(); try { XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(true); XmlPullParser parser = factory.newPullParser(); parser.setInput(is, "UTF-8"); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_TAG: if ("version".equals(parser.getName())) { info.setVersion(parser.nextText()); } else if ("updateTime".equals(parser.getName())) { info.setUpdateTime(parser.nextText()); } else if ("updateTime".equals(parser.getName())) { info.setUpdateTime(parser.nextText()); } else if ("downloadURL".equals(parser.getName())) { info.setDownloadURL(parser.nextText()); } else if ("updateinformation".equals(parser.getName())) { info.setUpdateInfomation(parseTxtFormat(parser.nextText(), "##")); } else if ("apkName".equals(parser.getName())) { info.setApkName(parser.nextText()); } else if ("versionCode".equals(parser.getName())) { info.setVersionCode(Integer.parseInt(parser.nextText())); } break; case XmlPullParser.END_TAG: break; } eventType = parser.next(); } } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return info; } public static String parseTxtFormat(String data, String formatChar) { StringBuffer backData = new StringBuffer(); String[] txts = data.split(formatChar); for (int i = 0; i < txts.length; i++) { backData.append(txts[i]); backData.append("\n"); } return backData.toString(); } }
4,定义APK更新类UpdateManager.java
package com.client.update; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import android.app.AlertDialog; import android.app.Dialog; import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.DialogInterface.OnClickListener; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ProgressBar; import android.widget.Toast; import com.tutor.update.R; public class UpdateManager { //Context private Context mContext; //Description of update private String mUpdateInfo = null; //description of application private static String TAG = "UpdateAPKDemo"; //Update url of application private String mApkUrl = null;//"http://softfile.3g.qq.com:8080/msoft/179/24659/43549/qq_hd_mini_1.4.apk"; //url address of version.xml private URL mDownloadUrl = null; //new version number private int mNewVersion = 0; //description of update dialog private Dialog mNoticeDialog; //description of download dialog private Dialog mDownloadDialog; //saving path for updated apk private static final String savePath = "/sdcard/"+TAG+"/"; //saving name for updated apk private static final String saveFileName = savePath + "UpdateAPKDemo.apk"; //display current process of loading private ProgressBar mProgressBar; //message for process updating private static final int DOWN_UPDATE = 1; //message for download completed private static final int DOWN_OVER = 2; //value of loading process private int mProgress; //thread for download private Thread downLoadThread; //switch for download private boolean mInteruptDownload = false; private Handler mHandler = new Handler(){ public void handleMessage(Message msg) { switch (msg.what) { case DOWN_UPDATE: mProgressBar.setProgress(mProgress); break; case DOWN_OVER: installApk(); break; default: break; } }; }; public UpdateManager(Context context) { this.mContext = context; } public void checkUpdateInfo(){ if(updateInfo()) showNoticeDialog(); else Toast.makeText(mContext, "No update information", Toast.LENGTH_SHORT).show(); } public boolean updateInfo() { VersionInformation info = getVersionInfoFromServer(); if(info!=null) { mApkUrl = info.getDownloadURL(); mUpdateInfo = info.getUpdateInfomation(); mNewVersion = info.getVersionCode(); } else { Log.e("Error","Version information is null."); return false; } try { int oldversion = (mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), PackageManager.GET_CONFIGURATIONS)).versionCode; if(oldversion < mNewVersion) return true; else return false; } catch (NameNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } } private VersionInformation getVersionInfoFromServer() { VersionInformation info = null; try { //set url address of version.xml mDownloadUrl = new URL("http://10.0.2.2:8080/updateApkServer/version.xml");//just for test } catch (MalformedURLException e) { e.printStackTrace(); } if (mDownloadUrl != null) { try { HttpURLConnection urlConn = (HttpURLConnection) mDownloadUrl .openConnection(); info = XMLParserUtils.getUpdateInformation(urlConn .getInputStream()); urlConn.disconnect(); } catch (IOException e) { e.printStackTrace(); } } return info; } private void showNoticeDialog(){ AlertDialog.Builder builder = new Builder(mContext); builder.setTitle("ChoseUpdate"); builder.setMessage(mUpdateInfo); builder.setPositiveButton("Download", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); showDownloadDialog(); } }); builder.setNegativeButton("Later", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); mNoticeDialog = builder.create(); mNoticeDialog.show(); } private void showDownloadDialog(){ AlertDialog.Builder builder = new Builder(mContext); builder.setTitle("Update"); final LayoutInflater inflater = LayoutInflater.from(mContext); View v = inflater.inflate(R.layout.progress, null); mProgressBar = (ProgressBar)v.findViewById(R.id.progress); builder.setView(v); builder.setNegativeButton("Cancel", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); mInteruptDownload = true; } }); mDownloadDialog = builder.create(); mDownloadDialog.show(); downloadApk(); } private Runnable mdownApkRunnable = new Runnable() { @Override public void run() { if (!android.os.Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED)) { Builder builder = new Builder(mContext); builder.setTitle("Warning"); builder.setMessage("SD card isn't exist!"); builder.setPositiveButton("OK", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); builder.show(); return; } else { try { URL url = new URL(mApkUrl); HttpURLConnection conn = (HttpURLConnection) url .openConnection(); conn.connect(); int length = conn.getContentLength(); InputStream is = conn.getInputStream(); File file = new File(savePath); if (!file.exists()) { file.mkdir(); } String apkFile = saveFileName; File ApkFile = new File(apkFile); FileOutputStream fos = new FileOutputStream(ApkFile); int count = 0; byte buf[] = new byte[1024]; do { int numread = is.read(buf); count += numread; mProgress = (int) (((float) count / length) * 100); // update current process value mHandler.sendEmptyMessage(DOWN_UPDATE); if (numread <= 0) { mHandler.sendEmptyMessage(DOWN_OVER); break; } fos.write(buf, 0, numread); } while (!mInteruptDownload);// cancel download operation if you want fos.close(); is.close(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } }; //download apk private void downloadApk(){ downLoadThread = new Thread(mdownApkRunnable); downLoadThread.start(); } //install apk private void installApk(){ File apkfile = new File(saveFileName); if (!apkfile.exists()) { return; } Intent i = new Intent(Intent.ACTION_VIEW); i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive"); mContext.startActivity(i); } }
5,在每次启动APK时,在MainActivity中调用
package com.tutor.update; import com.client.update.UpdateManager; import android.app.Activity; import android.os.Bundle; public class MainAcitivity extends Activity { private UpdateManager mUpdateManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //check update mUpdateManager = new UpdateManager(this); mUpdateManager.checkUpdateInfo(); //do other things } }
以上5个步骤就是具体的实现过程了,这里的代码需要你设定正确的version.xml 地址和apk地址,否则无法正常运行。