以下内容部分来自网络资源,这里做了个小小的总结。
目前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地址,否则无法正常运行。