分享

Android软件的自动更新

 飞鹰飞龙飞天 2014-06-18

   今天重新写了一篇自动更新的文章,参考:http://aokunsang./blog/1750440。本篇文章的源码整理了下,上传到了附件,需要的去下载。

 

   看了几个博客,讲自动升级的程序,但是感觉都不是很完整,因为项目需要,自己手动写了个自动更新的程序,备忘下(本篇博客源码不上传了,免得误入歧途,需要源码的去新写的博客下载)。

 

    一、 需求:如下图流程所示,需要在后台检查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
Java代码  收藏代码
  1. public class MainActivity extends Activity {  
  2. @Override  
  3.     protected void onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         setContentView(R.layout.main);  
  6.             .............  
  7.         //注册一个广播  
  8.         IntentFilter intentFilter = new IntentFilter(UpdateReceiver.ACTION_PROCRESS);  
  9.         intentFilter.addCategory(Intent.CATEGORY_DEFAULT);   //添加一个Category属性,CheckUpdateAsyncTask发送广播时候也要添加该属性。保持遥相呼应  
  10.         receiver = new UpdateReceiver();  
  11.         registerReceiver(receiver, intentFilter);  
  12.         //启动后台异步执行检查更新  
  13.         CheckUpdateAsyncTask checkAsyncTask = new CheckUpdateAsyncTask(WholeMainActivity.this);  
  14.         checkAsyncTask.execute(10);  
  15.     }  
  16. }  
 2、CheckUpdateAsyncTask.java
Java代码  收藏代码
  1. /** 
  2.  * 检查是否有更新 
  3.  * @author: aokunsang 
  4.  * @date: 2012-4-13 
  5.  */  
  6. public class CheckUpdateAsyncTask extends AsyncTask<Integer, Integer, String> {  
  7.       
  8.     private Context mContext;  
  9.     private final static String NOTE = "亲,有最新的软件包,赶紧下载吧~";  
  10.     private final static String SETTING_UPDATE_APK_INFO = "setting_updateapkinfo";  
  11.     private final static String CHECK_DATE = "checkdate";  
  12.     private final static String UPDATE_DATE = "updatedate";  
  13.     private final static String APK_VERSION = "apkversion";  
  14.     private final static String APK_VERCODE = "apkvercode";  
  15.       
  16.     private AlertDialog noticeDialog;    //提示弹出框  
  17.     private UpdateApkInfo apkInfo;  
  18.       
  19.     private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
  20.       
  21.     public CheckUpdateAsyncTask(Context mContext){  
  22.         this.mContext= mContext;  
  23.     }  
  24.       
  25.     @Override  
  26.     protected String doInBackground(Integer... params) {  
  27.         String result = "";  
  28.         //检查是否能够连接网络,根据日期判断是否需要进行更新  
  29.         if(checkTodayUpdate() && PeaceUtil.isNetworkAvailable(mContext)){  
  30.             getUpateApkInfo();   
  31.             if(apkInfo!=null && checkApkVersion()){  //检查版本号  
  32.                 alreayCheckTodayUpdate();    //设置今天已经检查过更新  
  33.                 result = "success";  
  34.             }else{  
  35.                 Log.i("---------检查应用更新-------------", "从服务器获取下载数据失败或者该版本code不需要升级");  
  36.                 result = "fail";  
  37.             }  
  38.         }else{  
  39.             Log.i("---------检查应用更新-------------", "无法连接网络或者根据日期判断不需要更新软件");  
  40.             result = "fail";  
  41.         }  
  42.         return result;  
  43.     }  
  44.   
  45.     @Override  
  46.     protected void onCancelled() {  
  47.         // TODO Auto-generated method stub  
  48.         super.onCancelled();  
  49.     }  
  50.   
  51.     @Override  
  52.     protected void onPostExecute(String result) {  
  53.         if("success".equals(result)){  
  54.             showNoticeDialog();  
  55.         }  
  56.         super.onPostExecute(result);  
  57.     }  
  58.       
  59.     /** 
  60.      * 弹出软件更新提示对话框 
  61.      */  
  62.     private void showNoticeDialog(){  
  63.         Builder builder = new AlertDialog.Builder(mContext);  
  64.         builder.setTitle("软件版本更新").setMessage(NOTE);  
  65.         builder.setPositiveButton("下载", new DialogInterface.OnClickListener(){  
  66.             @Override  
  67.             public void onClick(DialogInterface dialog, int which) {  
  68.                 Intent intent = new Intent();  
  69.                 intent.setAction(UpdateReceiver.ACTION_PROCRESS);  
  70.                 intent.addCategory(Intent.CATEGORY_DEFAULT);   //一定要添加这个属性,不然onReceive(Context,Intent)中的Context参数不等于mContext,并且报错  
  71.                 intent.putExtra(UpdateReceiver.PARAM_IN, apkInfo);  
  72.                 dialog.dismiss();  
  73.                 mContext.sendBroadcast(intent);  
  74.             }  
  75.         });  
  76.         builder.setNegativeButton("以后再说", new DialogInterface.OnClickListener(){  
  77.             @Override  
  78.             public void onClick(DialogInterface dialog, int which) {  
  79.                 dialog.dismiss();  
  80.             }  
  81.         });  
  82.         noticeDialog = builder.create();  
  83.         noticeDialog.show();  
  84.     }  
  85.     /** 
  86.      * 获取升级APK详细信息 
  87.      * {apkVersion:'1.10',apkVerCode:2,apkName:'1.1.apk',apkDownloadUrl:'http://localhost:8080/myapp/1.1.apk'} 
  88.      * @return 
  89.      */  
  90.     private void getUpateApkInfo(){  
  91.           
  92.         String updateApkJson = NetWorkAction.getnetworkInfo(mContext, Const.checkUpdateApk, null);  
  93.         updateApkJson = Escape.unescape(updateApkJson);  
  94.         try {  
  95.             JSONObject obj = new JSONObject(updateApkJson);  
  96.             String apkVersion = obj.getString("apkVersion");  
  97.             int apkVerCode = obj.getInt("apkVerCode");  
  98.             String apkName = obj.getString("apkName");  
  99.             String apkDownloadUrl = obj.getString("apkDownloadUrl");  
  100.             apkInfo = new UpdateApkInfo(apkVersion, apkName, apkDownloadUrl, apkVerCode);  
  101.         } catch (JSONException e) {  
  102.             e.printStackTrace();  
  103.         }  
  104.     }  
  105.     /** 
  106.      * 根据日期检查是否需要进行软件升级 
  107.      * @throws Exception  
  108.      */  
  109.     private boolean checkTodayUpdate() {  
  110.         SharedPreferences sharedPreference = mContext.getSharedPreferences(SETTING_UPDATE_APK_INFO, 0);  
  111.         String checkDate = sharedPreference.getString(CHECK_DATE, "");  
  112.         String updateDate = sharedPreference.getString(UPDATE_DATE, "");  
  113.         Log.i("-------------------checkDate------------","检查时间:"+checkDate);  
  114.         Log.i("-------------------updateDate------------","最近更新软件时间:"+updateDate);  
  115.         if("".equals(checkDate) && "".equals(updateDate)){  //刚安装的新版本,设置详细信息  
  116.             int verCode = 0;  
  117.             String versionName = "";  
  118.             try {  
  119.                 verCode = mContext.getPackageManager().getPackageInfo("com.peacemap.sl.jyg", 0).versionCode;  
  120.                 versionName = mContext.getPackageManager().getPackageInfo("com.peacemap.sl.jyg", 0).versionName;  
  121.             } catch (NameNotFoundException e) {  
  122.                 e.printStackTrace();  
  123.             }  
  124.             String dateStr = sdf.format(new Date());  
  125.             sharedPreference.edit().putString(CHECK_DATE, dateStr)  
  126.             .putString(UPDATE_DATE, dateStr)  
  127.             .putString(APK_VERSION, versionName)  
  128.             .putInt(APK_VERCODE, verCode).commit();  
  129.             return false;  
  130.         }  
  131.         try {  
  132.             //判断defaultMinUpdateDay天内不检查升级  
  133.             if((new Date().getTime()-sdf.parse(updateDate).getTime())/1000/3600/24<Const.defaultMinUpdateDay){  
  134.                 return false;  
  135.             }else if(checkDate.equalsIgnoreCase(sdf.format(new Date()))){//判断今天是否检查过升级  
  136.                 return false;  
  137.             }  
  138.         } catch (Exception e) {  
  139.             e.printStackTrace();  
  140.             return false;  
  141.         }  
  142.         return true;  
  143.     }  
  144.     /** 
  145.      * 检查版本是否需要更新 
  146.      * @return 
  147.      */  
  148.     private boolean checkApkVersion(){  
  149.         SharedPreferences sharedPreference = mContext.getSharedPreferences(SETTING_UPDATE_APK_INFO, 0);  
  150.         int verCode = sharedPreference.getInt(APK_VERCODE, 0);  
  151.         if(apkInfo.getAplVerCode()>verCode){  //如果新版本Code大于系统更新后的Code,则升级  
  152.             return true;  
  153.         }else{  
  154.             return false;  
  155.         }  
  156.     }  
  157.     /** 
  158.      * 设置今天已经检查过升级 
  159.      * @return 
  160.      */  
  161.     private void alreayCheckTodayUpdate(){  
  162.         String date = sdf.format(new Date());  
  163.         SharedPreferences sharedPreference = mContext.getSharedPreferences(SETTING_UPDATE_APK_INFO, 0);  
  164.         sharedPreference.edit().putString(CHECK_DATE, date).commit();  
  165.     }  
  166. }  
 3.UpdateAsyncTask.java
Java代码  收藏代码
  1. /** 
  2.  * 异步更新软件 
  3.  * @author: aokunsang 
  4.  * @date: 2012-4-13 
  5.  */  
  6. public class UpdateAsyncTask extends AsyncTask<Integer, Integer, String> {  
  7.   
  8.     private final static String SETTING_UPDATE_APK_INFO = "setting_updateapkinfo";  
  9.     private final static String UPDATE_DATE = "updatedate";  
  10.     private final static String APK_VERSION = "apkversion";  
  11.     private final static String APK_VERCODE = "apkvercode";  
  12.       
  13.     private final static String savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + Const.apkSaveDir;  
  14.       
  15.     private String fileName;  
  16.       
  17.     private Context mContext;  
  18.     private ProgressBar progressView;      //进度条  
  19.     private TextView textView;  
  20.     private AlertDialog downloadDialog;    //下载弹出框  
  21.       
  22.     private UpdateApkInfo apkInfo;   //APK更新的详细信息  
  23.       
  24.     private boolean interceptFlag = false;  //是否取消下载  
  25.     private boolean sdExists = false;   //是否存在SD卡  
  26.       
  27.     private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
  28.       
  29.     public UpdateAsyncTask(Context mContext,UpdateApkInfo apkInfo) {  
  30.         this.mContext = mContext;  
  31.         this.apkInfo = apkInfo;  
  32.         if(apkInfo!=null){  
  33.             fileName = savePath + "/" + apkInfo.getApkName();  
  34.         }  
  35.     }  
  36.       
  37.     /** 
  38.      * 升级成功,更新升级日期和版本号,和版本code 
  39.      */  
  40.     private void alearyUpdateSuccess(){  
  41.         SharedPreferences sharedPreference = mContext.getSharedPreferences(SETTING_UPDATE_APK_INFO, 0);  
  42.         sharedPreference.edit().putString(UPDATE_DATE, sdf.format(new Date()))  
  43.         .putString(APK_VERSION, apkInfo.getApkVersion()).putInt(APK_VERCODE, apkInfo.getAplVerCode()).commit();  
  44.     }  
  45.       
  46.     /** 
  47.      * 安装apk 
  48.      */  
  49.     private void installApk(){   
  50.         File file = new File(fileName);  
  51.         if(!file.exists()){  
  52.             Log.i("---------软件更新之安装应用-------------", "找不到下载的软件");  
  53.             return;  
  54.         }  
  55.         Intent intent = new Intent(Intent.ACTION_VIEW);  
  56.         intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");  
  57.         mContext.startActivity(intent);  
  58.     }  
  59.     /** 
  60.      * 检测手机是否存在SD卡 
  61.      */  
  62.     private boolean checkSoftStage(){  
  63.         if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  //判断是否存在SD卡  
  64.             File file = new File(savePath);  
  65.             if(!file.exists()){  
  66.                 file.mkdir();  
  67.             }  
  68.             sdExists = true;  
  69.             return true;  
  70.         }else{  
  71.             Toast.makeText(mContext, "检测到手机没有存储卡,请安装了内存卡后再升级。", Toast.LENGTH_LONG).show();  
  72.             return false;  
  73.         }  
  74.     }  
  75.     @Override  
  76.     protected void onPreExecute() {  
  77.           
  78.         if(apkInfo!=null && checkSoftStage()){  
  79.             showDownloadDialog();  
  80.         }  
  81.         super.onPreExecute();  
  82.     }  
  83.     /** 
  84.      * 弹出下载进度对话框 
  85.      */  
  86.     private void showDownloadDialog(){  
  87.         Builder builder = new AlertDialog.Builder(mContext);  
  88.         builder.setTitle("正在更新版本");  
  89.         //---------------------------- 设置在对话框中显示进度条 ---------------------------------------  
  90.         final LayoutInflater inflater = LayoutInflater.from(mContext);  
  91.         View view = inflater.inflate(R.layout.updateprogressbar, null);  
  92.         textView = (TextView)view.findViewById(R.id.progressCount_text);  
  93.         textView.setText("进度:0");  
  94.         progressView = (ProgressBar)view.findViewById(R.id.progressbar);  
  95.         builder.setView(view);  
  96.           
  97.         builder.setNegativeButton("取消", new DialogInterface.OnClickListener(){  
  98.             @Override  
  99.             public void onClick(DialogInterface dialog, int which) {  
  100.                 dialog.dismiss();  
  101.                 interceptFlag = true;   
  102.             }  
  103.         });  
  104.         downloadDialog = builder.create();  
  105.         downloadDialog.show();  
  106.     }  
  107.       
  108.     @Override  
  109.     protected String doInBackground(Integer... params) {  
  110.           
  111.         String result = "";  
  112.         if(apkInfo==null){  
  113.             result = "fail";  
  114.         }else if(!NetWorkAction.checkURL(apkInfo.getApkDownloadUrl())){   //检查apk的下载地址是否可用  
  115.             result = "netfail";  
  116.         }else if(apkInfo!=null && sdExists){  
  117.             InputStream is = null;  
  118.             FileOutputStream fos = null;  
  119.             File file = new File(savePath);  
  120.             if(!file.exists()){  
  121.                 file.mkdirs();  
  122.             }  
  123.             try {  
  124.                 URL url = new URL(apkInfo.getApkDownloadUrl());  
  125.                 URLConnection urlConn = url.openConnection();  
  126.                 is = urlConn.getInputStream();  
  127.                 int length = urlConn.getContentLength();   //文件大小  
  128.                 fos = new FileOutputStream(fileName);  
  129.                   
  130.                 int count = 0,numread = 0;  
  131.                 byte buf[] = new byte[1024];  
  132.                   
  133.                 while(!interceptFlag && (numread = is.read(buf))!=-1){  
  134.                     count+=numread;  
  135.                     int progressCount =(int)(((float)count / length) * 100);  
  136.                     publishProgress(progressCount);  
  137.                     fos.write(buf, 0, numread);  
  138.                 }  
  139.                 fos.flush();  
  140.                 result = "success";  
  141.             } catch (Exception e) {  
  142.                 e.printStackTrace();  
  143.                 result = "fail";  
  144.             }finally{  
  145.                 try {  
  146.                     if(fos!=null)  
  147.                         fos.close();  
  148.                     if(is!=null)  
  149.                         is.close();  
  150.                 } catch (IOException e) {  
  151.                     e.printStackTrace();  
  152.                     result = "fail";  
  153.                 }  
  154.             }  
  155.         }  
  156.         return result;  
  157.     }  
  158.   
  159.     @Override  
  160.     protected void onPostExecute(String result) {  
  161.         if(downloadDialog!=null){  
  162.             downloadDialog.dismiss();  
  163.         }  
  164.         if(!interceptFlag && "success".equals(result)){  
  165.             alearyUpdateSuccess();  
  166.             installApk();  
  167.         }else if("netfail".equals(result)){  
  168.             Toast.makeText(mContext, "连接服务器失败,请稍后重试。", Toast.LENGTH_LONG).show();  
  169.         }  
  170.         super.onPostExecute(result);  
  171.     }  
  172.       
  173.     @Override  
  174.     protected void onProgressUpdate(Integer... values) {  
  175.         int count = values[0];  
  176.         progressView.setProgress(count);   //设置下载进度  
  177.         textView.setText("进度:"+count+"%");  
  178.         super.onProgressUpdate(values);  
  179.     }  
  180. }  
 4、UpdateReceiver.java
Java代码  收藏代码
  1. /** 
  2.  * 升级广播通知 
  3.  * @author: aokunsang 
  4.  * @date: 2012-4-13 
  5.  */  
  6. public class UpdateReceiver extends BroadcastReceiver {  
  7.       
  8.     public final static String ACTION_PROCRESS = "com.peacemap.sl.jyg.intent.action.ACTION_PROCRESS";  
  9.     public final static String PARAM_IN = "apkinfo";  
  10.       
  11.     @Override  
  12.     public void onReceive(Context context, Intent intent) {  
  13.         //获取升级APK的详细信息  
  14.         UpdateApkInfo apkInfo = (UpdateApkInfo)intent.getExtras().getSerializable(PARAM_IN);  
  15.         //启动升级的异步进程  
  16.         UpdateAsyncTask asyncTask = new UpdateAsyncTask(context,apkInfo);  
  17.         asyncTask.execute(10);  
  18.     }  
  19. }  
 注意:UpdateReceiver需要在AndroidManifest.xml中配置:
Xml代码  收藏代码
  1. <!-- 广播 -->  
  2. <receiver android:name="com.peacemap.sl.jyg.receiver.UpdateReceiver" >  
  3.     <intent-filter>  
  4.     <!-- action的name值一定要和UpdateReceiver中的ACTION_PROCRESS一致 -->  
  5.     <action android:name="com.peacemap.sl.jyg.intent.action.ACTION_PROCRESS" />  
  6.     </intent-filter>  
  7. </receiver>  
 5.在下载的时候,有个下载进度对话框,需要一个XML或者用java代码写一个UI。
Xml代码  收藏代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas./apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical"  
  6.     >  
  7.       
  8.     <TextView   
  9.         android:id="@+id/progressCount_text"  
  10.         android:layout_width="fill_parent"  
  11.         android:layout_height="wrap_content"  
  12.         android:textColor="@color/white"  
  13.         android:textSize="14dip"  
  14.         />  
  15.       
  16.     <ProgressBar   
  17.         android:id="@+id/progressbar"  
  18.         style="?android:attr/progressBarStyleHorizontal"  
  19.         android:layout_width="fill_parent"   
  20.         android:layout_height="wrap_content"  
  21.     />  
  22. </LinearLayout>  
  6.其他实体类;
Java代码  收藏代码
  1. /** 
  2.  * 升级APK详细信息 
  3.  * @author: aokunsang 
  4.  * @date: 2012-4-13 
  5.  */  
  6. public class UpdateApkInfo implements Serializable {  
  7.   
  8.     /** 
  9.      *  
  10.      */  
  11.     private static final long serialVersionUID = 1L;  
  12.       
  13.     private String apkVersion;     //apk版本号  
  14.     private String apkName;      //apk名字  
  15.     private String apkDownloadUrl;  //下载地址  
  16.     private int aplVerCode;    //apk升级标示  
  17.   
  18.        setter和getter.....  
  19. }  
 
 注:Const.java是final类型,属于常用数据存储类。放置一些URL路径的。
7、服务器类方法(读取配置文件内容)
Java代码  收藏代码
  1. /** 
  2.      * 检查apk是否可以升级 
  3.      * {apkVersion:'1.10',apkVerCode:2,apkName:'1.1.apk',apkDownloadUrl:'http://localhost:8080/myapp/1.1.apk'} 
  4.      * @return 
  5.      */  
  6.     @Action(value="checkUpdateApk")  
  7.     public String checkApkUpdate(){  
  8.           
  9.         Properties pp = new Properties();  
  10.         ResourceLoader loader = new DefaultResourceLoader();  
  11.         try {  
  12.             InputStream is = loader.getResource("classpath:config/sysconf.properties").getInputStream();  
  13.             pp.load(new InputStreamReader(is, "utf-8"));  
  14.         } catch (UnsupportedEncodingException e) {  
  15.             e.printStackTrace();  
  16.         } catch (IOException e) {  
  17.             e.printStackTrace();  
  18.         }  
  19.           
  20.         String apkVersion = pp.getProperty("apkVersion");  
  21.         String apkDownloadUrl = pp.getProperty("apkDownloadUrl");  
  22.         String apkName = pp.getProperty("apkName");  
  23.         int apkVerCode = NumberUtils.toInt(pp.getProperty("apkVerCode"),0);  
  24.           
  25.         String result = "{apkVersion:'"+apkVersion+"',apkVerCode:"+apkVerCode+",apkName:'"+apkName+"',apkDownloadUrl:'"+apkDownloadUrl+"'}";  
  26.         Httptear.ResponseResultByEscape(result);  //通过流写出去  
  27. //      Httptear.ResponseResult(result);  
  28.         return NONE;  
  29.     }  
 


 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多