现在很多的安卓APP都有检查更新的功能,最近也在做这个,在网上查阅了下相关的信息。
更新的原理就是检查客户端的版本号和服务端的版本号进行比对,如果服务端的版本号大于客户端,就提示更新,否则无,
一般会在服务端放一个APP和一个xml文件,xml文件中有版本号,下载地址等,当客户端请求服务端时,会得到xml文件,
并解析获得xml文件中的内容来判断是否需要更新等,实现的思路大致就是这样。
我在做这个时,参考了一个大神写的文章,地址:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1201/2088.html
感谢分享!
先来看一下我的结构图:一个客户端一个服务端,客户端eclipse不介绍了,服务端也是eclipse和tomact,服务端可以有两个方式,一种我是
用了一个servlet来封装一个json数组,当然也需要用到Gson了,往list集合中添加数据,并转换为json数组;另一中直接给个.json文件,里面放置
需要用到的版本号地址等信息;在浏览器中请求Servlet可以得到这样的一个信息:
<pre name="code" class="html"><pre name="code" class="plain">[{"updateMessage":"1,增加一些功能;2,修复了一些BUG","url":"http://10.8.2.81:8080/WebTest/CZSoft.apk","versionCode":"2"}]
<span style="font-family: Arial, Helvetica, sans-serif;">也就是json数组,里面是版本号地址等,还有就是json 文件了,里面也是放置的json数组,同上;</span>
上面是结构图,下面贴代码:
首先是MainActivity里面的,比较简单,就是一个按钮:主要就是在检查更新的地方加上:
UpdateChecker updateChecker = new UpdateChecker(MainActivity.this);
updateChecker.setCheckUrl("http://10.8.2.81:8080/WebTest/AppServlet");
//updateChecker.setCheckUrl("http://10.8.2.81:8080/WebTest/version.json");
updateChecker.checkForUpdates();
加上这两句代码即可。
MainActivity 一个按钮的点击事件,
public class MainActivity extends Activity {
private Button updateBtn;
//UpdateChecker updateChecker;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
/**
* 可以是手动检测更新,也可以是打开app后自己检测,根据需要
*/
updateBtn = (Button) findViewById(R.id.updateBtn);
updateBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
/**
* 有两种方式,一种是直接请求得到json,另一种是给一个.json的文件,同样的功能
* 很多都是在服务端放置.xml文件,放json文件也可
*/
UpdateChecker updateChecker = new UpdateChecker(MainActivity.this);
updateChecker.setCheckUrl("http://10.8.2.81:8080/WebTest/AppServlet");
//updateChecker.setCheckUrl("http://10.8.2.81:8080/WebTest/version.json");
updateChecker.checkForUpdates();
}
});
}
}
这里的请求地址得到的就是上面说的json数组:
[{"updateMessage":"1,增加一些功能;2,修复了一些BUG","url":"http://10.8.2.81:8080/WebTest/CZSoft.apk","versionCode":"2"}]
当然也可以使用下面的地址,直接请求json文件,得到同样的结构;
点击按钮检查更新时:如果从请求地址得到的版本号大于客户端的版本号就会提示更新,点击下载就会自动下载,这么并没有做后台更新,
下载完成后自动提示安装:
点击安装就自动安装了,
AppVersion 代码:版本号,地址等,实体类
package com.android.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;
}
}
UpdateChecker 代码:
package com.android.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.zip.GZIPInputStream;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
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 {
public 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;
Log.e(TAG, "---url===" + mCheckUrl);
}
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();
}
/**
* dialog提示信息
*/
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();
}
/**
* 下载apk
*/
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;
// Log.e(TAG, "---filename==="+filename);
// Log.e(TAG, "---destinationFilePath==="+destinationFilePath);
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);
}
/**
* 请求网络
*
* @return
*/
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;
}
/**
* 解析json信息
*
* @param json
* @return
*/
private AppVersion parseJson(String json) {
AppVersion appVersion = new AppVersion();
try {
/**
* 这里根据请求返回的是json数组还是对象,选择相应的解析方式
*/
JSONArray jsonArray = new JSONArray(json);
// JSONObject obj = new JSONObject(json);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject obj = jsonArray.getJSONObject(0);
Log.e(TAG, "---obj==" + obj);
String updateMessage = obj
.getString(AppVersion.APK_UPDATE_CONTENT);
String apkUrl = obj.getString(AppVersion.APK_DOWNLOAD_URL);
// Log.e(TAG, "---apkUrl=="+apkUrl);
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;
}
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) {
}
}
}
}
}
}
DownloadService 代码:
package com.android.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 android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.os.ResultReceiver;
public class DownloadService extends IntentService {
public static final int UPDATE_PROGRESS = 8344;
private static final String TAG = "DownloadService";
public DownloadService() {
super("DownloadService");
}
@Override
protected void onHandleIntent(Intent intent) {
String urlToDownload = intent.getStringExtra("url");
// Log.e(TAG, "---------urlToDownload"+urlToDownload);
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);
}
}
布局文件就一个main.xml里面就是一个按钮,这个就不贴了。
服务端的代码:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
AppMessage apps = new AppMessage();
apps.setUpdateMessage("1,增加一些功能;2,修复了一些BUG");
apps.setUrl("http://10.8.2.81:8080/WebTest/CZSoft.apk");
apps.setVersionCode("2");
List<AppMessage> list = new ArrayList<AppMessage>();
list.add(apps);
/**
* 用gson将list集合转换为json数组
*/
Gson gson = new Gson();
String jsons = gson.toJson(list);
System.out.print(jsons);
PrintWriter out = response.getWriter();
out.print(jsons);
}
就是一个servlet里面封装了一个json数组,当然,服务端里面还会放置apk,versison.json文件,最后不要忘了在AndroidManifest.xml
文件中配置service,还有相关的权限:
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity android:name="com.android.update.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>
<service android:name="com.android.update.DownloadService"/>
</application>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
以上就是这部分的代码,最参考用,
最后还是感谢http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1201/2088.html这位大神的博客,
感谢分享!
源码下载