1.首先是获取当前程序的版本号
private String getVersion() {
try {
PackageManager packageManager = getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0);
return packageInfo.versionName;
} catch (NameNotFoundException e) {
e.printStackTrace();
return "版本号未知";
}
}
2. 访问服务器检查是否更新
private boolean isNeedUpdate(String version) {
UpdateInfoService updateInfoService = new UpdateInfoService(this);
try {
info = updateInfoService.getUpdateInfo(R.string.serverUrl);
String v = info.getVersion();
if(v.equals(version)) {
Log.i(TAG, "当前版本:" + version);
Log.i(TAG, "最新版本:" + v);
loadMainUI();
return false;
} else {
Log.i(TAG, "需要更新");
return true;
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "获取更新信息异常,请稍后再试", Toast.LENGTH_SHORT).show();
loadMainUI();
}
return false;
}
R.string.serverUrl是服务器放置更新信息的地址,使用一个类去查询更新信息
public class UpdateInfoService {
private Context context;
public UpdateInfoService(Context context) {
this.context = context;
}
public UpdateInfo getUpdateInfo(int urlId) throws Exception {
String path = context.getResources().getString(urlId);拿到config.xml里面存放的地址
URL url = new URL(path);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();//开启一个http链接
httpURLConnection.setConnectTimeout(10000);//设置链接的超时时间,现在为5秒
httpURLConnection.setRequestMethod("GET");//设置请求的方式
InputStream is = httpURLConnection.getInputStream();//拿到一个输入流。里面包含了update.xml的信息
return UpdateInfoParser.getUpdateInfo(is);//解析xml
}
}
其中用到了一个解析xml文件的类
public class UpdateInfoParser {
public static UpdateInfo getUpdateInfo(InputStream is) throws Exception {
UpdateInfo info = new UpdateInfo();
XmlPullParser xmlPullParser = Xml.newPullParser();
xmlPullParser.setInput(is, "utf-8");
int type = xmlPullParser.getEventType();
while(type != XmlPullParser.END_DOCUMENT) {
switch(type) {
case XmlPullParser.START_TAG :
if(xmlPullParser.getName().equals("version")) {
info.setVersion(xmlPullParser.nextText());
} else if (xmlPullParser.getName().equals("description")) {
info.setDescription(xmlPullParser.nextText());
} else if (xmlPullParser.getName().equals("apkurl")) {
info.setUrl(xmlPullParser.nextText());
}
break;
default :
break;
}
type = xmlPullParser.next();
}
return info;
}
}
3.下面调用isNeedUpdate(getVersion())检查是否需要更新
这里需要注意,由于UpdateInfoService类的getUpdateInfo方法中访问服务器不是异步的,如果直接在Activity的onCreate()中直接调用isNeedUpdate(getVersion())获取不到更新信息,需要将isNeedUpdate(getVersion())放入到子线程中。
class UpdateHandler implements Runnable {
@Override
public void run() {
Looper.prepare();
if(isNeedUpdate(getVersion())) {
showUpdateDialog();
}
Looper.loop();
}
}
然后在onCreate方法中启动线程
Runnable r = new UpdateHandler();
Thread thread = new Thread(r);
thread.start();
然后是显示对话框
private void showUpdateDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(SplashActivity.this);
builder.setIcon(android.R.drawable.ic_dialog_info);
builder.setCancelable(false);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dir = new File(Environment.getExternalStorageDirectory(), "/chaoshibang/update");
if(!dir.exists()) {
dir.mkdirs();
}
String apkPath = Environment.getExternalStorageDirectory() + "/chaoshibang/update/new.apk";
//这里启动DownloadFileTask任务
new DownloadFileTask().execute(info.getUrl(), apkPath);
progressDialog.show();
} else {
Toast.makeText(SplashActivity.this, "SD卡不可用,请插入SD卡", Toast.LENGTH_SHORT).show();
loadMainUI();
}
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
loadMainUI();
}
});
builder.create().show();
}
在对话框的确定按钮的响应事件中下载APK文件
4.一种方式使用AsyncTask来下载apk文件
private class DownloadFileTask extends AsyncTask<String, Integer, File> {
public DownloadFileTask() {
super();
}
@Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog.show();
}
@Override
protected void onPostExecute(File file) {
super.onPostExecute(file);
progressDialog.dismiss();
loadMainUI();
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
progressDialog.setMax(values[1]);
progressDialog.setProgress(values[0]);
}
@Override
protected File doInBackground(String... params) {
try {
URL url = new URL(params[0]);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(2000);
httpURLConnection.setRequestMethod("GET");
if(httpURLConnection.getResponseCode() == 200) {
int total = httpURLConnection.getContentLength();
InputStream is = httpURLConnection.getInputStream();
File file = new File(params[1]);
FileOutputStream fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int len;
int process = 0;
while((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
process += len;
publishProgress(process, total);
}
fos.flush();
fos.close();
is.close();
return file;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
doInbackground()在子线程中下载apk文件,在其它三个方法中操作progressDialog的显示、进度
5.另一种方式使用Service,参照http://blog.csdn.net/xiaanming/article/details/9750689
public class DownloadFileService extends Service {
private String urlPath="";
//文件存储
private File updateFile = null;
private OnProgressListener onProgressListener;
private Intent mIntent = new Intent("com.yifeng.chaoshibang.broadcast.UPDATE_APK");
public DownloadFileService() {
}
public void setOnProgressListener(OnProgressListener onProgressListener) {
this.onProgressListener = onProgressListener;
}
/**
* 返回一个Binder对象
*/
@Override
public IBinder onBind(Intent intent) {
super.onBind(intent);
return new DownloadBinder();
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
urlPath = intent.getStringExtra("url");
//创建文件
if(FileUtil.haveSDCard()) {
updateFile = FileUtil.getFile(Config.sd_folder_name, Config.sd_apk_name);
}
//开启一个新的线程下载,如果使用Service同步下载,会导致ANR问题,Service本身也会阻塞
new Thread(new DownloadRunnable()).start();
return super.onStartCommand(intent, flags, startId);
}
class DownloadRunnable implements Runnable {
@Override
public void run() {
HttpURLConnection httpURLConnection = null;
InputStream input = null;
FileOutputStream fos = null;
try {
URL url = new URL(urlPath);
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(2000);
httpURLConnection.setRequestMethod("GET");
if(httpURLConnection.getResponseCode() == 200) {
int total = httpURLConnection.getContentLength();
if(onProgressListener != null) {
onProgressListener.onProgressInitialize(total);
}
input = httpURLConnection.getInputStream();
fos = new FileOutputStream(updateFile);
byte[] buffer = new byte[1024];
int len;
int progress = 0;
while((len = input.read(buffer)) != -1) {
fos.write(buffer, 0, len);
progress += len;
//进度发生变化通知调用方
if(onProgressListener != null){
onProgressListener.onProgressUpdate(progress);
}
}
fos.flush();
if(onProgressListener != null){
onProgressListener.onProgressComplete(updateFile);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(fos != null) {
fos.close();
}
if(input != null) {
input.close();
}
if(httpURLConnection != null) {
httpURLConnection.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class DownloadBinder extends Binder {
/**
* 获取当前Service的实例
*/
public DownloadFileService getService() {
return DownloadFileService.this;
}
}
}
为了能使DownloadFileService中进度变化信息通知给调用方,首先定义一个接口
public interface OnProgressListener {
void onProgressInitialize(int total);
void onProgressUpdate(int progress);
void onProgressComplete(File file);
}
然后在Activity中绑定Service
public class SplashActivity extends BaseActivity {
private ProgressDialog progressDialog;
private UpdateInfo info;
private static final String TAG = "SplashActivity";
private DownloadFileService downloadFileService;
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//返回一个DownloadFileService对象
downloadFileService = ((DownloadFileService.DownloadBinder)service).getService();
//注册回调接口来接收下载进度的变化
downloadFileService.setOnProgressListener(new OnProgressListener() {
@Override
public void onProgressInitialize(int total) {
progressDialog.setMax(total);
}
@Override
public void onProgressUpdate(int progress) {
progressDialog.setProgress(progress);
}
@Override
public void onProgressComplete(File file) {
progressDialog.dismiss();
Intent intent = new Intent(SplashActivity.this, DownloadFileService.class);
unbindService(conn);
stopService(intent);
//安装下载的文件
install(file);
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
在ServiceConnection的onServiceConnected回调中为service设置了回调监听器。当在DownloadFileService中调用时就会执行这里的回调,更新progressDialog
然后在对话框的确定按钮的响应事件中启动Service
将new DownloadFileTask().execute(info.getUrl(), apkPath); 替换为
//绑定DownloadFileService
//Intent intent = new Intent("com.yifeng.chaoshibang.service.DOWNLOAD_APK_ACTION");
Intent intent = new Intent(SplashActivity.this, DownloadFileService.class);
intent.putExtra("url", info.getUrl());
startService(intent);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
progressDialog.show();
这里需要注意,在DownloadFileService中是在onStartCommand()方法中启动线程去下载文件。根据Service的生命周期,bindService()方法不会调用onStartCommand(),所以还需使用startService()启动Service。
6.Service和BroadcastReceiver实现
public class DownloadFileService extends BaseService {
private String urlPath="";
//文件存储
private File updateFile = null;
private Intent mIntent = new Intent("com.yifeng.chaoshibang.broadcast.UPDATE_APK");
public DownloadFileService() {
}
/**
* 返回一个Binder对象
*/
@Override
public IBinder onBind(Intent intent) {
super.onBind(intent);
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
urlPath = intent.getStringExtra("url");
//创建文件
if(FileUtil.haveSDCard()) {
updateFile = FileUtil.getFile(Config.sd_folder_name, Config.sd_apk_name);
}
//开启一个新的线程下载,如果使用Service同步下载,会导致ANR问题,Service本身也会阻塞
new Thread(new DownloadRunnable()).start();
return super.onStartCommand(intent, flags, startId);
}
class DownloadRunnable implements Runnable {
@Override
public void run() {
HttpURLConnection httpURLConnection = null;
InputStream input = null;
FileOutputStream fos = null;
try {
URL url = new URL(urlPath);
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(2000);
httpURLConnection.setRequestMethod("GET");
if(httpURLConnection.getResponseCode() == 200) {
int total = httpURLConnection.getContentLength();
input = httpURLConnection.getInputStream();
fos = new FileOutputStream(updateFile);
byte[] buffer = new byte[1024];
int len;
int progress = 0;
while((len = input.read(buffer)) != -1) {
fos.write(buffer, 0, len);
progress += len;
mIntent.putExtra("progress", progress);
mIntent.putExtra("total", total);
sendBroadcast(mIntent);
}
fos.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(fos != null) {
fos.close();
}
if(input != null) {
input.close();
}
if(httpURLConnection != null) {
httpURLConnection.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
onCreate()方法中注册广播接收器
//动态注册广播接收器
updateReceiver = new UpdateProgressReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.yifeng.chaoshibang.broadcast.UPDATE_APK");
registerReceiver(updateReceiver, intentFilter);
protected void onDestroy() {
Intent intent = new Intent(SplashActivity.this, DownloadFileService.class);
//停止服务
stopService(intent);
//注销广播
unregisterReceiver(updateReceiver);
super.onDestroy();
}
然后在确定下载按钮的监听事件响应中启动service
private void showUpdateDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(SplashActivity.this);
builder.setIcon(android.R.drawable.ic_dialog_info);
builder.setCancelable(false);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dir = new File(Environment.getExternalStorageDirectory(), "/chaoshibang/update");
if(!dir.exists()) {
dir.mkdirs();
}
String apkPath = Environment.getExternalStorageDirectory() + "/chaoshibang/update/new.apk";
//启动服务
Intent mIntent = new Intent(SplashActivity.this, DownloadFileService.class);
mIntent.putExtra("url", info.getUrl());
startService(mIntent);
progressDialog.show();
} else {
Toast.makeText(SplashActivity.this, "SD卡不可用,请插入SD卡", Toast.LENGTH_SHORT).show();
loadMainUI();
}
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
loadMainUI();
}
});
builder.create().show();
}