android应用升级代码
看了几个博客,讲自动升级的程序,但是感觉都不是很完整,因为项目需要,自己手动写了个自动更新的程序,备忘下。
在另一篇文章中有个更好的例子,还附上了源码:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2013/0709/1425.html。
**一、 需求:**如下图流程所示,需要在后台检查APK是否需要升级,需要升级则弹出提示下载升级对话框,用户点击下载进行升级,然后自动安装。
软件下载流程图:
**二、 思路:**APK自动检查是否升级,这个当然需要在后台进行。因此需要使用异步线程操作,想到IntentService和AsyncTask。
**三、选择原因:**使用IntentService异步检查升级,但是无法提示弹出对话框;因此需要使用广播通知BroadcastReceiver。但是我想直接在异步类中直接弹出下载对话框,IntentService没有提供Context这样的参数;并且需要提供一个异步检查升级,一个异步下载,需要两个IntentService,而IntentService是可以执行多个任务的,客户端只需通过startService(Intent) 方法调用,那么intentService就一个接着一个的顺序来处理。那么我要是建立两个IntentService类,有点大材小用。那就使用AsyncTask类吧,每个任务启动一个新的asycnTask来工作,一个asyncTask只能使用一次。正好符合我的要求。
**四、执行顺序:**时序图就不画了,说下类的执行流程吧。
1>mainActivity(主UI),
2>UpdateReceiver(更新广播通知),
3>CheckUpdateAsyncTask(检查更新),
4>UpdateAsyncTask(下载APK)
进入mainActivity,注册UpdateReceiver,同时执行CheckUpdateAsyncTask;检查完更新,由CheckUpdateAsyncTask广播通知UpdateReceiver,
UpdateReceiver的onReceive(Context,Intent)接收广播,并且启动UpdateAsyncTask下载。
五、代码展示:
1、mainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
.............
//注册一个广播
IntentFilter intentFilter = new IntentFilter(UpdateReceiver.ACTION_PROCRESS);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT); //添加一个Category属性,CheckUpdateAsyncTask发送广播时候也要添加该属性。保持遥相呼应
receiver = new UpdateReceiver();
registerReceiver(receiver, intentFilter);
//启动后台异步执行检查更新
CheckUpdateAsyncTask checkAsyncTask = new CheckUpdateAsyncTask(WholeMainActivity.this);
checkAsyncTask.execute(10);
}
}
2、CheckUpdateAsyncTask.java
/**
* 检查是否有更新
* @author: aokunsang
* @date: 2012-4-13
*/
public class CheckUpdateAsyncTask extends AsyncTask<Integer, Integer, String> {
private Context mContext;
private final static String NOTE = "亲,有最新的软件包,赶紧下载吧~";
private final static String SETTING_UPDATE_APK_INFO = "setting_updateapkinfo";
private final static String CHECK_DATE = "checkdate";
private final static String UPDATE_DATE = "updatedate";
private final static String APK_VERSION = "apkversion";
private final static String APK_VERCODE = "apkvercode";
private AlertDialog noticeDialog; //提示弹出框
private UpdateApkInfo apkInfo;
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public CheckUpdateAsyncTask(Context mContext){
this.mContext= mContext;
}
@Override
protected String doInBackground(Integer... params) {
String result = "";
//检查是否能够连接网络,根据日期判断是否需要进行更新
if(checkTodayUpdate() && PeaceUtil.isNetworkAvailable(mContext)){
getUpateApkInfo();
if(apkInfo!=null && checkApkVersion()){ //检查版本号
alreayCheckTodayUpdate(); //设置今天已经检查过更新
result = "success";
}else{
Log.i("---------检查应用更新-------------", "从服务器获取下载数据失败或者该版本code不需要升级");
result = "fail";
}
}else{
Log.i("---------检查应用更新-------------", "无法连接网络或者根据日期判断不需要更新软件");
result = "fail";
}
return result;
}
@Override
protected void onCancelled() {
// TODO Auto-generated method stub
super.onCancelled();
}
@Override
protected void onPostExecute(String result) {
if("success".equals(result)){
showNoticeDialog();
}
super.onPostExecute(result);
}
/**
* 弹出软件更新提示对话框
*/
private void showNoticeDialog(){
Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("软件版本更新").setMessage(NOTE);
builder.setPositiveButton("下载", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(UpdateReceiver.ACTION_PROCRESS);
intent.addCategory(Intent.CATEGORY_DEFAULT); //一定要添加这个属性,不然onReceive(Context,Intent)中的Context参数不等于mContext,并且报错
intent.putExtra(UpdateReceiver.PARAM_IN, apkInfo);
dialog.dismiss();
mContext.sendBroadcast(intent);
}
});
builder.setNegativeButton("以后再说", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
noticeDialog = builder.create();
noticeDialog.show();
}
/**
* 获取升级APK详细信息
* {apkVersion:'1.10',apkVerCode:2,apkName:'1.1.apk',apkDownloadUrl:'http://localhost:8080/myapp/1.1.apk'}
* @return
*/
private void getUpateApkInfo(){
String updateApkJson = NetWorkAction.getnetworkInfo(mContext, Const.checkUpdateApk, null);
updateApkJson = Escape.unescape(updateApkJson);
try {
JSONObject obj = new JSONObject(updateApkJson);
String apkVersion = obj.getString("apkVersion");
int apkVerCode = obj.getInt("apkVerCode");
String apkName = obj.getString("apkName");
String apkDownloadUrl = obj.getString("apkDownloadUrl");
apkInfo = new UpdateApkInfo(apkVersion, apkName, apkDownloadUrl, apkVerCode);
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* 根据日期检查是否需要进行软件升级
* @throws Exception
*/
private boolean checkTodayUpdate() {
SharedPreferences sharedPreference = mContext.getSharedPreferences(SETTING_UPDATE_APK_INFO, 0);
String checkDate = sharedPreference.getString(CHECK_DATE, "");
String updateDate = sharedPreference.getString(UPDATE_DATE, "");
Log.i("-------------------checkDate------------","检查时间:"+checkDate);
Log.i("-------------------updateDate------------","最近更新软件时间:"+updateDate);
if("".equals(checkDate) && "".equals(updateDate)){ //刚安装的新版本,设置详细信息
int verCode = 0;
String versionName = "";
try {
verCode = mContext.getPackageManager().getPackageInfo("com.peacemap.sl.jyg", 0).versionCode;
versionName = mContext.getPackageManager().getPackageInfo("com.peacemap.sl.jyg", 0).versionName;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
String dateStr = sdf.format(new Date());
sharedPreference.edit().putString(CHECK_DATE, dateStr)
.putString(UPDATE_DATE, dateStr)
.putString(APK_VERSION, versionName)
.putInt(APK_VERCODE, verCode).commit();
return false;
}
try {
//判断defaultMinUpdateDay天内不检查升级
if((new Date().getTime()-sdf.parse(updateDate).getTime())/1000/3600/24<Const.defaultMinUpdateDay){
return false;
}else if(checkDate.equalsIgnoreCase(sdf.format(new Date()))){//判断今天是否检查过升级
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 检查版本是否需要更新
* @return
*/
private boolean checkApkVersion(){
SharedPreferences sharedPreference = mContext.getSharedPreferences(SETTING_UPDATE_APK_INFO, 0);
int verCode = sharedPreference.getInt(APK_VERCODE, 0);
if(apkInfo.getAplVerCode()>verCode){ //如果新版本Code大于系统更新后的Code,则升级
return true;
}else{
return false;
}
}
/**
* 设置今天已经检查过升级
* @return
*/
private void alreayCheckTodayUpdate(){
String date = sdf.format(new Date());
SharedPreferences sharedPreference = mContext.getSharedPreferences(SETTING_UPDATE_APK_INFO, 0);
sharedPreference.edit().putString(CHECK_DATE, date).commit();
}
}
3.UpdateAsyncTask.java
/**
* 异步更新软件
* @author: aokunsang
* @date: 2012-4-13
*/
public class UpdateAsyncTask extends AsyncTask<Integer, Integer, String> {
private final static String SETTING_UPDATE_APK_INFO = "setting_updateapkinfo";
private final static String UPDATE_DATE = "updatedate";
private final static String APK_VERSION = "apkversion";
private final static String APK_VERCODE = "apkvercode";
private final static String savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + Const.apkSaveDir;
private String fileName;
private Context mContext;
private ProgressBar progressView; //进度条
private TextView textView;
private AlertDialog downloadDialog; //下载弹出框
private UpdateApkInfo apkInfo; //APK更新的详细信息
private boolean interceptFlag = false; //是否取消下载
private boolean sdExists = false; //是否存在SD卡
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public UpdateAsyncTask(Context mContext,UpdateApkInfo apkInfo) {
this.mContext = mContext;
this.apkInfo = apkInfo;
if(apkInfo!=null){
fileName = savePath + "/" + apkInfo.getApkName();
}
}
/**
* 升级成功,更新升级日期和版本号,和版本code
*/
private void alearyUpdateSuccess(){
SharedPreferences sharedPreference = mContext.getSharedPreferences(SETTING_UPDATE_APK_INFO, 0);
sharedPreference.edit().putString(UPDATE_DATE, sdf.format(new Date()))
.putString(APK_VERSION, apkInfo.getApkVersion()).putInt(APK_VERCODE, apkInfo.getAplVerCode()).commit();
}
/**
* 安装apk
*/
private void installApk(){
File file = new File(fileName);
if(!file.exists()){
Log.i("---------软件更新之安装应用-------------", "找不到下载的软件");
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
mContext.startActivity(intent);
}
/**
* 检测手机是否存在SD卡
*/
private boolean checkSoftStage(){
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ //判断是否存在SD卡
File file = new File(savePath);
if(!file.exists()){
file.mkdir();
}
sdExists = true;
return true;
}else{
Toast.makeText(mContext, "检测到手机没有存储卡,请安装了内存卡后再升级。", Toast.LENGTH_LONG).show();
return false;
}
}
@Override
protected void onPreExecute() {
if(apkInfo!=null && checkSoftStage()){
showDownloadDialog();
}
super.onPreExecute();
}
/**
* 弹出下载进度对话框
*/
private void showDownloadDialog(){
Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("正在更新版本");
//---------------------------- 设置在对话框中显示进度条 ---------------------------------------
final LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.updateprogressbar, null);
textView = (TextView)view.findViewById(R.id.progressCount_text);
textView.setText("进度:0");
progressView = (ProgressBar)view.findViewById(R.id.progressbar);
builder.setView(view);
builder.setNegativeButton("取消", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
interceptFlag = true;
}
});
downloadDialog = builder.create();
downloadDialog.show();
}
@Override
protected String doInBackground(Integer... params) {
String result = "";
if(apkInfo==null){
result = "fail";
}else if(!NetWorkAction.checkURL(apkInfo.getApkDownloadUrl())){ //检查apk的下载地址是否可用
result = "netfail";
}else if(apkInfo!=null && sdExists){
InputStream is = null;
FileOutputStream fos = null;
File file = new File(savePath);
if(!file.exists()){
file.mkdirs();
}
try {
URL url = new URL(apkInfo.getApkDownloadUrl());
URLConnection urlConn = url.openConnection();
is = urlConn.getInputStream();
int length = urlConn.getContentLength(); //文件大小
fos = new FileOutputStream(fileName);
int count = 0,numread = 0;
byte buf\[\] = new byte\[1024\];
while(!interceptFlag && (numread = is.read(buf))!=-1){
count+=numread;
int progressCount =(int)(((float)count / length) * 100);
publishProgress(progressCount);
fos.write(buf, 0, numread);
}
fos.flush();
result = "success";
} catch (Exception e) {
e.printStackTrace();
result = "fail";
}finally{
try {
if(fos!=null)
fos.close();
if(is!=null)
is.close();
} catch (IOException e) {
e.printStackTrace();
result = "fail";
}
}
}
return result;
}
@Override
protected void onPostExecute(String result) {
if(downloadDialog!=null){
downloadDialog.dismiss();
}
if(!interceptFlag && "success".equals(result)){
alearyUpdateSuccess();
installApk();
}else if("netfail".equals(result)){
Toast.makeText(mContext, "连接服务器失败,请稍后重试。", Toast.LENGTH_LONG).show();
}
super.onPostExecute(result);
}
@Override
protected void onProgressUpdate(Integer... values) {
int count = values\[0\];
progressView.setProgress(count); //设置下载进度
textView.setText("进度:"+count+"%");
super.onProgressUpdate(values);
}
}
4、UpdateReceiver.java
/**
* 升级广播通知
* @author: aokunsang
* @date: 2012-4-13
*/
public class UpdateReceiver extends BroadcastReceiver {
public final static String ACTION_PROCRESS = "com.peacemap.sl.jyg.intent.action.ACTION_PROCRESS";
public final static String PARAM_IN = "apkinfo";
@Override
public void onReceive(Context context, Intent intent) {
//获取升级APK的详细信息
UpdateApkInfo apkInfo = (UpdateApkInfo)intent.getExtras().getSerializable(PARAM_IN);
//启动升级的异步进程
UpdateAsyncTask asyncTask = new UpdateAsyncTask(context,apkInfo);
asyncTask.execute(10);
}
}
**注意:**UpdateReceiver需要在AndroidManifest.xml中配置:
5.在下载的时候,有个下载进度对话框,需要一个XML或者用java代码写一个UI。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:id="@+id/progressCount_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="14dip"
/>
<ProgressBar
android:id="@+id/progressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
6.其他实体类;
/**
- 升级APK详细信息
- @author: aokunsang
- @date: 2012-4-13
*/
public class UpdateApkInfo implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String apkVersion; //apk版本号
private String apkName; //apk名字
private String apkDownloadUrl; //下载地址
private int aplVerCode; //apk升级标示
setter和getter.....
}
注:Const.java是final类型,属于常用数据存储类。放置一些URL路径的。
7、服务器类方法(读取配置文件内容)
/**
* 检查apk是否可以升级
* {apkVersion:'1.10',apkVerCode:2,apkName:'1.1.apk',apkDownloadUrl:'http://localhost:8080/myapp/1.1.apk'}
* @return
*/
@Action(value="checkUpdateApk")
public String checkApkUpdate(){
Properties pp = new Properties();
ResourceLoader loader = new DefaultResourceLoader();
try {
InputStream is = loader.getResource("classpath:config/sysconf.properties").getInputStream();
pp.load(new InputStreamReader(is, "utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String apkVersion = pp.getProperty("apkVersion");
String apkDownloadUrl = pp.getProperty("apkDownloadUrl");
String apkName = pp.getProperty("apkName");
int apkVerCode = NumberUtils.toInt(pp.getProperty("apkVerCode"),0);
String result = "{apkVersion:'"+apkVersion+"',apkVerCode:"+apkVerCode+",apkName:'"+apkName+"',apkDownloadUrl:'"+apkDownloadUrl+"'}";
Httptear.ResponseResultByEscape(result); //通过流写出去
// Httptear.ResponseResult(result);
return NONE;
}