android应用升级代码

泡在网上的日子 / 文 发表于2013-07-09 00:16 次阅读

  看了几个博客,讲自动升级的程序,但是感觉都不是很完整,因为项目需要,自己手动写了个自动更新的程序,备忘下。

  在另一篇文章中有个更好的例子,还附上了源码: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中配置:

<!-- 广播 -->
<receiver android:name="com.peacemap.sl.jyg.receiver.UpdateReceiver" >
   <intent-filter>
   <!-- action的name值一定要和UpdateReceiver中的ACTION_PROCRESS一致 -->
   <action android:name="com.peacemap.sl.jyg.intent.action.ACTION_PROCRESS" />
   </intent-filter>
</receiver>

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;
   }

收藏 赞 (3) 踩 (0)
上一篇:android中使用setCustomView不能填满actionbar的问题
在安卓原生日历中日程的编辑界面的title是用setCustomView来自定义的。自定义actionbar的代码基本是如下形式: ViewGroup customView = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.pen_note_custom_title, null);getActionBar().setCustomView
下一篇:android Camera开发-手机拍照流程
android 拍照API流程 1. 在布局文件中添加一个 surfaceView (摄影平面) 2.根据 SurfaceView 获得 Holder (固定器) 3.给固定器设置 SurfaceHolder.Callback ; 这个 Callback 有一系列的周期方法, 比如:surfaceCreated,surfaceChanged,surfaceDestroyed等方法.