前几天抽空搞了一下android的自动更新功能,没想到很快就被我搞定了! 很有成就感,现在来回顾一下自动更新的开发流程!
自动更新首先肯定需要服务器,于是在公司的测试服务器上的tomcat中部署了一个文件夹! 里面有两个文件,一个是xml 记录版本号信息,另一个自然就是最新的更新包了! 这是服务器上所必须的!
有了服务器,就可以开始着手开发客户的代码了,代码在我的小猪音乐里面!
第一步:
更新程序,在第一个activity的onCreate中 调用 检查更新的办法 因为在主线程不允许执行长耗时操作,所以要新建线程
new Thread(new CheckVersionTask()).start();
private class CheckVersionTask implements Runnable {
@Override
public void run() {
Looper.prepare();
Message msg = new Message();
UpdateManager manager = new UpdateManager(MusicMainActivity.this);
versionCode=manager.getVersionCode(MusicMainActivity.this);
if(manager.isUpdate()){//需要更新就更新
msg.what = UPDATA_CLIENT;
handler.sendMessage(msg);
}else{
msg.what = UPDATA_NONEED;
handler.sendMessage(msg);
}
//manager.checkUpdate();
}
}
相应的handler 如下:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch (msg.what) {
case UPDATA_NONEED:
Toast.makeText(MusicMainActivity.this,"当前版本号为:"+String.valueOf(versionCode)+ " 不需要更新",
Toast.LENGTH_SHORT).show();
break;
case UPDATA_CLIENT:
//对话框通知用户升级程序
showUpdataDialog();
break;
case GET_UNDATAINFO_ERROR:
//服务器超时
Toast.makeText(MusicMainActivity.this, "获取服务器更新信息失败", 1).show();
break;
case DOWN_ERROR:
//下载apk失败
Toast.makeText(MusicMainActivity.this, "下载新版本失败", 1).show();
break;
}
}
};
联网去访问服务器里面的那个xml文件,读取里面的版本号,和当前版本号对比,如果服务器的版本号高于当前程序版本号,则 出现提示框 提示用户是否 更新!
//提示是否更新对话框
private void showUpdataDialog()
{
AlertDialog.Builder builder=new AlertDialog.Builder(this);
builder.setTitle("发现新版本,是否更新?"); //设置标题
//builder.setIcon(R.drawable.ic_launcher); //设置图标
builder.setPositiveButton("更新",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
//Toast.makeText(MusicMainActivity.this, "马上更新", 1).show();
downLoadApk();
}
}
); //设置确定按钮
builder.setNegativeButton("下次再说", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
//Toast.makeText(MusicMainActivity.this, "好的", 1).show();
}
}); //设置取消按钮
//builder.setView(diaview); //设置自定义的样式
alertDialog=builder.create(); //生成Alertdialog对话框
alertDialog.show(); //显示对话框
}
当用户选择了开始更新的时候,前台页面需要用到进度条等更新界面,所以用到handler来操作进度条的更新,一边下载,一边更新下载进度当下载完成的时候,就关闭掉进度条的对话框,开始自动安装!安装完后要出现 “完成” 和“打开” 窗口!
protected void downLoadApk() {
final ProgressDialog pd; //进度条对话框
pd = new ProgressDialog(this);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setMessage("正在下载更新");
pd.show();
new Thread(){
public void run() {
try {
File file = DownLoadManager.getFileFromServer(updateurl, pd);
sleep(2000);
installApk(file);
pd.dismiss(); //结束掉进度条对话框 } catch (Exception e) {
}
catch (Exception e) {
Message msg = new Message();
msg.what = DOWN_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
}
}
}.start();
}
//安装apk
protected void installApk(File file) {
Intent intent = new Intent();//执行动作
intent.setAction(Intent.ACTION_VIEW);//执行的数据类型
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //这句话非常关键,没有这句话安装完不会出现完成 和打开页面
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
到这里 ,自动更新的功能就完成了,上面用到了两个工具类 ,这里也贴出来吧!
UpdateManager 类
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 java.util.HashMap;
import com.example.mymusic.R;
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.NameNotFoundException;
import android.net.Uri;
import android.os.Environment;
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;
/**
* @author coolszy
* @date 2012-4-26
* @blog http://blog.92coding.com
*/
public class UpdateManager {
/* 保存解析的XML信息 */
HashMap<String, String> mHashMap;
// 服务器地址apk
private String updateurl = " http://apptest.erke.com:8504/testVersion/MyMusic.apk";
private String versionurl = " http://apptest.erke.com:8504/testVersion/updateVersion.xml";
private Context mContext;
public UpdateManager(Context context) {
this.mContext = context;
}
/**
* 检查软件是否有更新版本
*
* @return
*/
public boolean isUpdate() {
// 获取当前软件版本
int versionCode = getVersionCode(mContext);
// 把version.xml放到网络上,然后获取文件信息
System.out.println("当前版本:" + versionCode);
// InputStream inStream =
// ParseXmlService.class.getClassLoader().getResourceAsStream("version.xml");
URL url = null;
try {
url = new URL(versionurl);
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) url.openConnection();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
conn.setConnectTimeout(5000);
InputStream inStream = null;
try {
inStream = conn.getInputStream();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// 解析XML文件。 由于XML文件比较小,因此使用DOM方式进行解析
ParseXmlService service = new ParseXmlService();
try {
mHashMap = service.parseXml(inStream);
} catch (Exception e) {
e.printStackTrace();
}
if (null != mHashMap) {
int serviceCode = Integer.valueOf(mHashMap.get("version"));
// 版本判断
if (serviceCode > versionCode) {
System.out.println("检查更新--最新版本" + serviceCode);
return true;
}
}
return false;
}
/**
* 获取软件版本号
*
* @param context
* @return
*/
public int getVersionCode(Context context) {
int versionCode = 0;
try {
// 获取软件版本号,对应AndroidManifest.xml下android:versionCode
versionCode = context.getPackageManager().getPackageInfo(
"com.example.mymusic", 0).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return versionCode;
}
}
DownLoadManager 类
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.ProgressDialog;
import android.os.Environment;
public class DownLoadManager {
public static File getFileFromServer(String path, ProgressDialog pd)
throws Exception {
// 如果相等的话表示当前的sdcard挂载在手机上并且是可用的
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
// 获取到文件的大小
pd.setMax(conn.getContentLength());
InputStream is = conn.getInputStream();
File file = new File(Environment.getExternalStorageDirectory(),
"updata.apk");
FileOutputStream fos = new FileOutputStream(file);
BufferedInputStream bis = new BufferedInputStream(is);
byte[] buffer = new byte[1024];
int len;
int total = 0;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
total += len;
// 获取当前下载量
pd.setProgress(total);
}
fos.close();
bis.close();
is.close();
return file;
} else {
return null;
}
}
}
自动更新首先肯定需要服务器,于是在公司的测试服务器上的tomcat中部署了一个文件夹! 里面有两个文件,一个是xml 记录版本号信息,另一个自然就是最新的更新包了! 这是服务器上所必须的!
有了服务器,就可以开始着手开发客户的代码了,代码在我的小猪音乐里面!
第一步:
更新程序,在第一个activity的onCreate中 调用 检查更新的办法 因为在主线程不允许执行长耗时操作,所以要新建线程
new Thread(new CheckVersionTask()).start();
private class CheckVersionTask implements Runnable {
@Override
public void run() {
Looper.prepare();
Message msg = new Message();
UpdateManager manager = new UpdateManager(MusicMainActivity.this);
versionCode=manager.getVersionCode(MusicMainActivity.this);
if(manager.isUpdate()){//需要更新就更新
msg.what = UPDATA_CLIENT;
handler.sendMessage(msg);
}else{
msg.what = UPDATA_NONEED;
handler.sendMessage(msg);
}
//manager.checkUpdate();
}
}
相应的handler 如下:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch (msg.what) {
case UPDATA_NONEED:
Toast.makeText(MusicMainActivity.this,"当前版本号为:"+String.valueOf(versionCode)+ " 不需要更新",
Toast.LENGTH_SHORT).show();
break;
case UPDATA_CLIENT:
//对话框通知用户升级程序
showUpdataDialog();
break;
case GET_UNDATAINFO_ERROR:
//服务器超时
Toast.makeText(MusicMainActivity.this, "获取服务器更新信息失败", 1).show();
break;
case DOWN_ERROR:
//下载apk失败
Toast.makeText(MusicMainActivity.this, "下载新版本失败", 1).show();
break;
}
}
};
联网去访问服务器里面的那个xml文件,读取里面的版本号,和当前版本号对比,如果服务器的版本号高于当前程序版本号,则 出现提示框 提示用户是否 更新!
//提示是否更新对话框
private void showUpdataDialog()
{
AlertDialog.Builder builder=new AlertDialog.Builder(this);
builder.setTitle("发现新版本,是否更新?"); //设置标题
//builder.setIcon(R.drawable.ic_launcher); //设置图标
builder.setPositiveButton("更新",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
//Toast.makeText(MusicMainActivity.this, "马上更新", 1).show();
downLoadApk();
}
}
); //设置确定按钮
builder.setNegativeButton("下次再说", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
//Toast.makeText(MusicMainActivity.this, "好的", 1).show();
}
}); //设置取消按钮
//builder.setView(diaview); //设置自定义的样式
alertDialog=builder.create(); //生成Alertdialog对话框
alertDialog.show(); //显示对话框
}
当用户选择了开始更新的时候,前台页面需要用到进度条等更新界面,所以用到handler来操作进度条的更新,一边下载,一边更新下载进度当下载完成的时候,就关闭掉进度条的对话框,开始自动安装!安装完后要出现 “完成” 和“打开” 窗口!
protected void downLoadApk() {
final ProgressDialog pd; //进度条对话框
pd = new ProgressDialog(this);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setMessage("正在下载更新");
pd.show();
new Thread(){
public void run() {
try {
File file = DownLoadManager.getFileFromServer(updateurl, pd);
sleep(2000);
installApk(file);
pd.dismiss(); //结束掉进度条对话框 } catch (Exception e) {
}
catch (Exception e) {
Message msg = new Message();
msg.what = DOWN_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
}
}
}.start();
}
//安装apk
protected void installApk(File file) {
Intent intent = new Intent();//执行动作
intent.setAction(Intent.ACTION_VIEW);//执行的数据类型
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //这句话非常关键,没有这句话安装完不会出现完成 和打开页面
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
到这里 ,自动更新的功能就完成了,上面用到了两个工具类 ,这里也贴出来吧!
UpdateManager 类
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 java.util.HashMap;
import com.example.mymusic.R;
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.NameNotFoundException;
import android.net.Uri;
import android.os.Environment;
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;
/**
* @author coolszy
* @date 2012-4-26
* @blog http://blog.92coding.com
*/
public class UpdateManager {
/* 保存解析的XML信息 */
HashMap<String, String> mHashMap;
// 服务器地址apk
private String updateurl = " http://apptest.erke.com:8504/testVersion/MyMusic.apk";
private String versionurl = " http://apptest.erke.com:8504/testVersion/updateVersion.xml";
private Context mContext;
public UpdateManager(Context context) {
this.mContext = context;
}
/**
* 检查软件是否有更新版本
*
* @return
*/
public boolean isUpdate() {
// 获取当前软件版本
int versionCode = getVersionCode(mContext);
// 把version.xml放到网络上,然后获取文件信息
System.out.println("当前版本:" + versionCode);
// InputStream inStream =
// ParseXmlService.class.getClassLoader().getResourceAsStream("version.xml");
URL url = null;
try {
url = new URL(versionurl);
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) url.openConnection();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
conn.setConnectTimeout(5000);
InputStream inStream = null;
try {
inStream = conn.getInputStream();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// 解析XML文件。 由于XML文件比较小,因此使用DOM方式进行解析
ParseXmlService service = new ParseXmlService();
try {
mHashMap = service.parseXml(inStream);
} catch (Exception e) {
e.printStackTrace();
}
if (null != mHashMap) {
int serviceCode = Integer.valueOf(mHashMap.get("version"));
// 版本判断
if (serviceCode > versionCode) {
System.out.println("检查更新--最新版本" + serviceCode);
return true;
}
}
return false;
}
/**
* 获取软件版本号
*
* @param context
* @return
*/
public int getVersionCode(Context context) {
int versionCode = 0;
try {
// 获取软件版本号,对应AndroidManifest.xml下android:versionCode
versionCode = context.getPackageManager().getPackageInfo(
"com.example.mymusic", 0).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return versionCode;
}
}
DownLoadManager 类
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.ProgressDialog;
import android.os.Environment;
public class DownLoadManager {
public static File getFileFromServer(String path, ProgressDialog pd)
throws Exception {
// 如果相等的话表示当前的sdcard挂载在手机上并且是可用的
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
// 获取到文件的大小
pd.setMax(conn.getContentLength());
InputStream is = conn.getInputStream();
File file = new File(Environment.getExternalStorageDirectory(),
"updata.apk");
FileOutputStream fos = new FileOutputStream(file);
BufferedInputStream bis = new BufferedInputStream(is);
byte[] buffer = new byte[1024];
int len;
int total = 0;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
total += len;
// 获取当前下载量
pd.setProgress(total);
}
fos.close();
bis.close();
is.close();
return file;
} else {
return null;
}
}
}