首页 > 代码库 > [转]Android应用的自动更新

[转]Android应用的自动更新

软件的自动更新一般都与Splash界面绑定在一起, 由于需要维护的软件界面很复杂, 一个Activity中嵌入ViewPager, 并且逻辑比较复杂, 索性重新写一个Activity, 现在的软件都很流行使用Splash界面, 正好与自动更新配套在一起;

 

在这个自动更新Splash中, 使用到了 动画设置 ,SharedPerference ,pull解析 ,dialog对话框 ,http网络编程 ,handler 等.

 

注意一个错误 : 已安装具有该名称和不同签名的数据包 , 早上测试人员报告突然出现这个问题, 在开发的时候我直接将eclipse上编译的版本放到了服务器上, 最后出现了这个问题, 开发的时候明明是好的啊, 怎么测试的时候出问题了呢.

编译环境不同, 产生的签名是不一样的, 在eclipse上编译生成 与 正式版本在linux下编译 所产生的 数字签名 是不一样的.

 

一. 创建Activity

 

1. 创建Activity大概流程

a. 设置全屏显示.

b. 设置布局, 并在布局中显示当前版本号, 为Splash界面添加动画.

c. 获取当前时间.

d. 获取SharedPerence配置文件.

e. 开启检查版本号线程, 后续的操作都在这个线程中执行.

 

2. 设置窗口样式

 

(1) 设置全屏显示

a. 代码实现 : 由于是Splash界面, 这里需要设置成无标题, 并且全屏显示, 注意下面的两行代码需要在setContentView()方法之前调用;

 

 //隐藏标题栏  requestWindowFeature(Window.FEATURE_NO_TITLE);  //隐藏状态栏  getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,   WindowManager.LayoutParams.FLAG_FULLSCREEN); 

 

b. 配置实现 :

 

 AndroidManifest.xml      <activity           android:name="myAcitivty"            android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />  

 

(2) 关于窗口的其它设置

 

 //①设置窗体始终点亮  getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,WindowManager.LayoutParams

 

//②设置窗体始终点亮      getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  

设置窗体始终点亮的配置文件实现

 

 //③AndroidManifest.xml添加权限      <uses-permission android:name="android.permission.WAKE_LOCK" />  

 

 //设置窗体背景模糊  getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,WindowManager.LayoutParams.FLAG_B

(3) 屏幕方向设置

 

a. 配置文件实现

 

 //设置横屏  <activity android:name="myAcitivty"  android:screenOrientation="landscape" />         //设置竖屏  <activity android:name="myAcitivty"  android:screenOrientation="portrait" />  

 

b. 代码实现

 

//设置横屏  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);        //设置竖屏  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);  

 

c. 获取屏幕方向

 

 //获取横屏方向  int orientation = this.getResources().getConfiguration().orientation;  

 

其中的orientation方向可以使 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE 或者 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE .

 

3. 设置动画

 

 

 

为了更好的用户体验, 这里给Splash界面添加一个动画, 这个动画加给整个界面.

 

 

 

(1) 创建动画

 

AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);<span style="white-space:pre">    </span>//创建动画  animation.setDuration(2000);<span style="white-space:pre">  </span>//设置渐变  splash_rl.setAnimation(animation);<span style="white-space:pre">    </span>//设置动画载体

 

创建动画吧: 创建的这个动画是透明度渐变动画, 传入浮点型参数, 0代表完全透明, 1代表不透明, 传入参数代表透明度从完全透明到不透明.

 

设置时间 : 设置的duration是动画渐变过程所消耗的时间.

设置动画 : 最后使用setAnimation()方法将穿件的动画设置给Splash界面.

 

(2) 动画常用方法

 

a. 普通设置 

 

    alphaAnimation.setRepeatCount(5);//设置重复次数      alphaAnimation.setFillAfter(true);//动画执行完是否停留在执行完的状态      alphaAnimation.setStartOffset(1000);//动画执行前等待的时间, 单位是毫秒      alphaAnimation.start();//开始动画  

 

b. 设置监听器

 

    alphaAnimation.setAnimationListener(new AnimationListener() {                  //动画开始时回调                  @Override                  public void onAnimationStart(Animation animation) {                  }                  //动画重复执行时回调                  @Override                  public void onAnimationRepeat(Animation animation) {                  }                  //动画执行结束时回调                  @Override                  public void onAnimationEnd(Animation animation) {                  }              });  

 

4. SharedPerference使用

 

    //获取SharedPerference      SharedPreferences sharedPreferences = getSharedPreferences("sp", Context.MODE_PRIVATE);            Editor editor = sharedPreferences.edit();   //获取Editor对象      editor.putBoolean("isUpdate", true);        //向sp中写入数据      editor.commit();                            //提交                sharedPreferences.getBoolean("isUpdate", true);//获取sp中的变量  

 

5. onCreate()方法代码

 

    /**          * 创建Activity时调用          *           * ① 设置全屏显示, 由于是Splash界面, 因此不能有标题          * ② 设置布局, 版本号, 执行动画           * ③ 设置当前时间          * ④ 获取SharedPerference配置文件          * ⑤ 开启检查版本号线程, 后续操作都在改线程中操作          *           */          @Override          public void onCreate(Bundle savedInstanceState) {              super.onCreate(savedInstanceState);              //隐藏标题栏              requestWindowFeature(Window.FEATURE_NO_TITLE);              //隐藏状态栏              getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,                       WindowManager.LayoutParams.FLAG_FULLSCREEN);              //设置布局              setContentView(R.layout.splash);                            /*              *  显示当前软件的版本号              *  获取布局中的TextView控件, 将版本号设置到这个TextView控件中              */              tv_version = (TextView) findViewById(R.id.tv_version);              version =getString(R.string.current_version) + " " + getVersion();              tv_version.setText(version);                            /*              *  在界面设置一个动画, 用来表明正在运行              *  a. 获取布局              *  b. 创建一个动画对象              *  c. 将动画设置到布局中              */              splash_rl = (RelativeLayout) findViewById(R.id.splash_rl);              AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);              animation.setDuration(2000);              splash_rl.setAnimation(animation);                            /*              * 这个时间值是用来控制Splash界面显示时间的              * 记录下这个值, 然后执行到下面, 如果时间差在3秒以内,               * 就执行下面的操作, 如果时间差不足3秒, 就Thread.sleep时间差              * 等够3秒在执行下面的操作              */              time = System.currentTimeMillis();                    //从SharedPreference中获取一些配置              sp = getSharedPreferences("config", Context.MODE_PRIVATE);                            //开启检查版本号线程              new Thread(new CheckVersionTask()).start();          }  

 

 

二. 检查版本号

 

1. 检查版本号线程

流程 : 

a. 保持Splash持续时间 : 获取当前时间与time进行比较, 如果不足3秒, 人为使Splash保持3秒时间;

b. 查看更新设置 : 从sp中获取更新设置, 如果sp中自动更新为true, 那么就执行下面的更新流程, 如果sp中自动更新为false, 那么直接进入主界面.

c. 获取信息 : 从网络中获取更新信息, 根据是否成功获取信息执行不同的操作.

 

源码 : 

 

    private final class CheckVersionTask implements Runnable{          public void run() {              try {                  /*                  * 获取当前时间, 与onCreate方法中获取的时间进行比较                  * 如果不足3秒, 在等待够3秒之后在执行下面的操作                  */                  long temp = System.currentTimeMillis();                  if(temp - time < 3000){                      SystemClock.sleep(temp - time);                  }                                    /*                  * 检查配置文件中的设置, 是否设置了自动更新;                   * 如果设置了自动更新, 就执行下面的操作,                  * 如果没有设置自动更新, 就直接进入主界面                  */                  boolean is_auto_update = sp.getBoolean("is_auto_update", true);                  if(!is_auto_update){                      loadMainUI();                      return;                  }                                    /*                  * 获取更新信息                  * 如果信息不为null, 向handler发信息SUCESS_GET_UPDATEINOF, 执行后续操作                  * 如果信息为null, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作                  * 如果出现异常, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作                  */                  updateInfo = getUpdateInfo(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + XML_FILE_DIRECTORY);                  if(updateInfo != null){                      Message msg = new Message();                      msg.what = SUCESS_GET_UPDATEINOF;                      mHandler.sendMessage(msg);                  }else{                      Message msg = new Message();                      msg.what = ERROR_GET_UPDATEINOF;                      mHandler.sendMessage(msg);                  }              } catch (Exception e) {                  e.printStackTrace();                  Message msg = new Message();                  msg.what = ERROR_GET_UPDATEINOF;                  mHandler.sendMessage(msg);              }          }         }  

 

2. 获取版本号方法

 

流程 : 

a. 创URL建对象;

b. 创建HttpURLConnection对象;

c. 设置超时时间;

d. 设置获取方式;

e. 查看链接是否成功;

f. 解析输入流信息;

 

源码 : 

 

    /**      * 获取更新信息      *      ① 根据字符串地址创建URL对象      *      ② 根据URL对象创建HttpURLConnection链接对象      *      ③ 设置链接对象5秒超时      *      ④ 设置链接对象获取的方式为get方式      *      ⑤ 如果成功连接, conn.getRequestCode值就是200, 此时就可以获取输入流      *      ⑥ 解析输入流获取更新信息      *            */      private UpdateInfo getUpdateInfo(String path){          try {              URL url = new URL(path);    //创建URL对象              //创建连接对象              HttpURLConnection conn = (HttpURLConnection) url.openConnection();              //设置链接超时              conn.setConnectTimeout(5000);              //设置获取方式              conn.setRequestMethod("GET");              //如果连接成功, 获取输入流              if(conn.getResponseCode() == 200){                  InputStream is = conn.getInputStream();                  //解析输入流中的数据, 返回更新信息                  return parserUpdateInfo(is);              }          } catch (MalformedURLException e) {              e.printStackTrace();          } catch (ProtocolException e) {              e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          }          return null;      }  

 

3. 更新信息对象

 

 

将从网上获取的更新信息 包括 版本号, apk文件地址, 软件描述等信息封装在一个类中.

 

    public class UpdateInfo {          private String version; //当前软件版本号          private String url;     //获取到的软件地址          private String description; //软件描述                    public String getVersion() {              return version;          }          public void setVersion(String version) {              this.version = version;          }          public String getUrl() {              return url;          }          public void setUrl(String url) {              this.url = url;          }          public String getDescription() {              return description;          }          public void setDescription(String description) {              this.description = description;          }          @Override          public String toString() {              return "UpdateInfo [version=" + version + ", url=" + url                      + ", description=" + description + "]";          }      }  

 

4. pull解析输入流

 

(1) pull解析流程

 

a. 获取pull解析器 : XmlPullParser parser = Xml.newPullParser();

b. 为pull解析器设置编码 : parser.setInput(inputStream, "UTF-8");

c. 获取pull解析器事件 : int eventType = parser.getEventType(), 之后的解析都要根据这个解析事件进行, 例如开始解析标签的事件时 XmlPullParser.START_TAG, 文档结束的事件时 XmlPullParser.END_DOCUMENT.

d. 解析流程控制 : 解析的时候, 如果没有解析到文档最后就一直解析, 这里使用while循环, eventType != XmlPullParser.END_DOCUMENT 就一直循环, 循环玩一个元素之后, 调用parser.next()遍历下一个元素.

e. 获取标签名 : 在事件解析标签的时候 ( eventType == XmlPullParser.START_TAG ) , 调用parser.getName()可以获取这个标签的标签名, 如果我们想要获取这个标签下的文本元素, 可以使用parser.nextText()来获取. 

 

(2) 更新xml文件

 

    <?xml version="1.0" encoding="UTF-8"?>      <updateInfo>        <version>3.2</version>        <url>http://127.0.0.1:8080/web/mobilesafe.apk</url>        <description>客户端更新</description>      </updateInfo>  

 

(3) 源码

 

    /**      * 获取更新信息      *      ① 创建pull解析器      *      ② 为解析器设置编码格式      *      ③ 获取解析事件      *      ④ 遍历整个xml文件节点, 获取标签元素内容      */      private UpdateInfo parserUpdateInfo(InputStream is){          try {              UpdateInfo updateInfo = null;              //1. 创建pull解析解析器              XmlPullParser parser = Xml.newPullParser();              //2. 设置解析编码              parser.setInput(is, "UTF-8");              //3. 获取解析器解事件, 如解析到文档开始 , 结尾, 标签等              int eventType = parser.getEventType();              //4. 在文档结束前一直解析              while (eventType != XmlPullParser.END_DOCUMENT) {                  switch (eventType) {                  //只解析标签                  case XmlPullParser.START_TAG:                      if ("updateInfo".equals(parser.getName())) {                          //当解析到updateInfo标签的时候, 跟标签开始, 创建一个UpdateInfo对象                          updateInfo = new UpdateInfo();                      } else if ("version".equals(parser.getName())) {                          //解析版本号标签                          updateInfo.setVersion(parser.nextText());                      } else if ("url".equals(parser.getName())) {                          //解析url标签                          updateInfo.setUrl(parser.nextText());                      } else if ("description".equals(parser.getName())) {                          //解析描述标签                          updateInfo.setDescription(parser.nextText());                      }                      break;                  default:                      break;                  }                  //每解析完一个元素, 就将解析标志位下移                  eventType = parser.next();              }              is.close();              return updateInfo;          } catch (XmlPullParserException e) {              e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          }          return null;      }  

 

三. Handler对象

 

Handler对象用来控制整个更新过程的进行;

 

    private Handler mHandler = new Handler(){          public void handleMessage(android.os.Message msg) {              switch (msg.what) {              /*              * 获取更新信息错误 , 在断网或者获取信息出现异常执行              * 提示一下, 之后进入主界面              */              case ERROR_GET_UPDATEINOF:                  ToastHint.getInstance().showHint(R.string.fail_to_get_updateinfo);                  loadMainUI();                  break;              /*              * 成功获取更新信息, 一般在成功从网上获取xml文件并解析出来              * 如果版本号相同, 说明不用更新, 直接进入主界面              * 如果版本号不同, 需要弹出更新对话框              */              case SUCESS_GET_UPDATEINOF:                  if(updateInfo.getVersion().equals(version)){                      loadMainUI();                  }else{                      showUpdateDialog();                  }                  break;              /*              * 下载apk文件出现错误, 中途断网 出现异常等情况              * 提示后进入主界面              */              case ERROR_DOWNLOAD_APK:                  mPb.dismiss();                  ToastHint.getInstance().showHint(R.string.fail_to_get_apk);                  loadMainUI();                  break;              /*              * 成功下载apk文件之后执行的操作              * 取消进度条对话框, 之后安装apk文件              */              case SUCCESS_DOWNLOAD_APK:                  mPb.dismiss();                  installApk();                  break;              default:                  break;              }          };      };  

 

 

四. 下载安装apk文件

 

 

1. 更新对话框

 

(1) 更新流程

 

先弹出更新对话框提示, 点击确定就弹出进度条对话框, 下载apk文件 . 如果点击取消, 直接进入主界面

 

更新对话框 : 这是一个AlertDialog , 先创建builder, 然后设置标题, 显示内容, 设置积极消极按钮, 创建对话框 之后显示对话框;

进度条对话框 : 这是一个ProgressDialog, 直接使用new创建, 设置信息与显示样式, 最后显示对话框.

 

(2) 创建对话框流程

 

创建一个对话框的流程 : 

a. 创建builder对象 : Builder builder = new Builder(context);

b. 设置标题 : builder.setTittle("");

c. 设置显示信息 : builder.setMessage("");

d. 设置按钮 : builder.setPositiveButton("", onClickListener);

e. 创建对话框 : Dialog dialog = builder.create();

f. 显示对话框 : dialog.show();

 

创建进度条对话框流程 : 

a. 创建进度条对话框 : ProgressDialog progressDialog = new ProgressDialog(context);

b. 设置进度条对话框样式 : progressDialog.setProgressStyle();

c. 设置显示信息 : progressDialog.setMessage();

d. 显示对话框 : progressDialog.show();

 

(3) 源码 

 

    /**      * 弹出更新对话框      *       * a. 创建builder对象      * b. 设置标题      * c. 设置对话框显示信息      * d. 设置该对话框不可回退, 如果回退的话就会卡在本界面      * e. 设置确定按钮      * f. 设置取消按钮      * g. 创建对话框      * h. 显示对话框      *       * 确定按钮按下显示进度条对话框      * a. 创建一个进度条对话框      * b. 设置该对话框不能回退      * c. 设置进度条样式      * d. 设置进度条的信息      * e. 显示进度条对话框      * f. 开启一个线程, 下载apk文件      */      protected void showUpdateDialog() {          //创建builder对象          AlertDialog.Builder builder = new AlertDialog.Builder(this);          //设置标题          builder.setTitle(getString(R.string.update_dialog_tittle));          //设置对话框信息          builder.setMessage(updateInfo.getDescription());          //设置不可回退          builder.setCancelable(false);          //设置确定按钮          builder.setPositiveButton(getString(R.string.confirm), new DialogInterface.OnClickListener() {              public void onClick(DialogInterface dialog, int which) {                  //创建进度条对话框                  mPb = new ProgressDialog(SplashActivity.this);                  //设置进度条对话框不可回退                  mPb.setCancelable(false);                  //设置进度条对话框样式                  mPb.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);                  //设置进度条对话框的信息                  mPb.setMessage(getString(R.string.update_dialog_messsage));                  //显示进度条对话框                  mPb.show();                  //开启显示进度条对话框线程                  new Thread(new DownloadApkTask()).start();              }          });          builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {              public void onClick(DialogInterface dialog, int which) {                  loadMainUI();              }          });          //创建更新信息提示对话框          mUpdateInfoDialog = builder.create();          //显示更新信息提示对话框          mUpdateInfoDialog.show();      }  

 

2. 下载apk线程

 

    /**      * 在这个线程中主要执行downloadApk方法, 这个方法传入apk路径和进度条对话框      * 注意 : 下载的前提是sd卡的状态是挂载的      */      private final class DownloadApkTask implements Runnable{          public void run() {              if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){                  try {                      SystemClock.sleep(2000);                      apkFile = downloadApk(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + updateInfo.url,mPb);                      Message msg = new Message();                      msg.what = SUCCESS_DOWNLOAD_APK;                      mHandler.sendMessage(msg);                  } catch (Exception e) {                      e.printStackTrace();                      Message msg = new Message();                      msg.what = ERROR_DOWNLOAD_APK;                      mHandler.sendMessage(msg);                  }              }          }         }  

 

3. 下载apk核心方法

 

 

从网络下载文件流程 : 

a. 创建URL对象 : 这个对象一般根据字符串地址创建, URL url = new URL(path);

b. 创建HttpURLConnection对象 : 这个对象根据URL对象创建, HttpURLConnection conn = (HttpURLConnection)url.openConnection();

c. 设置超时时间 : 单位是毫秒, conn.setConnectionTimeout(5000);

d. 设置请求方式 : conn.setRequestMethod("GET");

e. 成功连接 : 如果成功连接, 那么conn.getResponseCode()的值为200;

 

进度条对话框设置 : 

a. 设置进度条最大值 : mProgressDialog.setMax(int max);

b. 设置进度条当前值 : mProgressDialog.setProgress(int curr);

 

    /**      * 下载apk更新文件      *        * a. 根据SD卡路径创建文件对象, 这个文件用来保存下载的文件      * b. 创建URL对象      * c. 创建HttpUrlConnection对象      * d. 设置链接对象超时时间      * e. 设置请求方式 get      * f. 如果请求成功执行下面的操作      *       * g. 通过链接对象获取网络资源的大小      * h. 将文件大小设置给进度条对话框      * i. 获取输入流, 并且读取输入流信息      * j. 根据读取到的字节数, 将已经读取的数据设置给进度条对话框      */      public File downloadApk(String path,ProgressDialog pb) throws Exception{          //创建本地文件对象          File file = new File(Environment.getExternalStorageDirectory(), getFileName(path));          //创建HttpURL连接          URL url = new URL(path);          HttpURLConnection conn = (HttpURLConnection) url.openConnection();          conn.setConnectTimeout(5000);          conn.setRequestMethod("GET");          if(conn.getResponseCode() == 200){              int max = conn.getContentLength();              //设置进度条对话框的最大值              pb.setMax(max);              int count = 0;              InputStream is = conn.getInputStream();              FileOutputStream fos = new FileOutputStream(file);              byte[] buffer = new byte[1024];              int len = 0;              while((len = is.read(buffer)) != -1){                  fos.write(buffer, 0, len);                  //设置进度条对话框进度                  count = count + len;                  pb.setProgress(count);              }              is.close();              fos.close();          }          return file;      }  

 

4. 安装apk文件

 

 /**  * 安装apk文件流程  *   * a. 设置Action : Intent.ACTION_VIEW.  * b. 设置数据和类型 : 设置apk文件的uri 和 MIME类型  * c. 开启安装文件的Activity.  */  protected void installApk() {      Intent intent = new Intent();      intent.setAction(Intent.ACTION_VIEW);      intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");      startActivity(intent);  }

 

五. 相关的源码 

 

 

(1) 布局文件

splash.xml

 

 <?xml version="1.0" encoding="utf-8"?>  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"      android:layout_width="match_parent"      android:layout_height="match_parent"      android:background="@drawable/ivt_splash"       android:id="@+id/splash_rl">        <ProgressBar android:id="@+id/pb"          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:layout_centerHorizontal="true"          android:layout_alignParentBottom="true"          android:layout_marginBottom="30dip"/>            <TextView android:id="@+id/tv_version"         android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:layout_centerHorizontal="true"          android:layout_above="@id/pb"          android:layout_marginBottom="60dip"          android:textSize="30sp"          android:textColor="#17A6E8"          android:text="version"          />  </RelativeLayout> 

 

(2) Activity页面切换动画

 

main_in.xml

 

    <?xml version="1.0" encoding="utf-8"?>      <set xmlns:android="http://schemas.android.com/apk/res/android"           >          <translate              android:fromXDelta="100%p"              android:toXDelta="0"              android:fromYDelta="0"              android:toYDelta="0"               android:duration="200"              />      </set>  

 

splash_out.xml

 

    <?xml version="1.0" encoding="utf-8"?>      <set xmlns:android="http://schemas.android.com/apk/res/android"           >          <translate              android:fromXDelta="0"              android:toXDelta="-100%p"              android:fromYDelta="0"              android:toYDelta="0"               android:duration="200"              />      </set>  

 

(3) SplashActivity源码

 

SplashActivity.java

 

        public class SplashActivity extends Activity {                        private static final String TAG = "SplashActivity";                            public static final int ERROR_GET_UPDATEINOF = 0;              public static final int SUCESS_GET_UPDATEINOF = 1;              public static final int ERROR_DOWNLOAD_APK = 2;              public static final int SUCCESS_DOWNLOAD_APK = 3;                            private static final String XML_FILE_DIRECTORY = "updateinfo.xml";              private static final String UPDATE_FOLDER_DIRECTORY = "/webupdate/";                            private TextView tv_version;              private PackageManager pm;              private String version;              private UpdateInfo updateInfo;                            private Dialog mUpdateInfoDialog;              private ProgressDialog mPb;              private File apkFile;                            private RelativeLayout splash_rl;              private long time;              private SharedPreferences sp;                            private Handler mHandler = new Handler(){                  public void handleMessage(android.os.Message msg) {                      switch (msg.what) {                      /*                      * 获取更新信息错误 , 在断网或者获取信息出现异常执行                      * 提示一下, 之后进入主界面                      */                      case ERROR_GET_UPDATEINOF:                          ToastHint.getInstance().showHint(R.string.fail_to_get_updateinfo);                          loadMainUI();                          break;                      /*                      * 成功获取更新信息, 一般在成功从网上获取xml文件并解析出来                      * 如果版本号相同, 说明不用更新, 直接进入主界面                      * 如果版本号不同, 需要弹出更新对话框                      */                      case SUCESS_GET_UPDATEINOF:                          if(updateInfo.getVersion().equals(version)){                              loadMainUI();                          }else{                              showUpdateDialog();                          }                          break;                      /*                      * 下载apk文件出现错误, 中途断网 出现异常等情况                      * 提示后进入主界面                      */                      case ERROR_DOWNLOAD_APK:                          mPb.dismiss();                          ToastHint.getInstance().showHint(R.string.fail_to_get_apk);                          loadMainUI();                          break;                      /*                      * 成功下载apk文件之后执行的操作                      * 取消进度条对话框, 之后安装apk文件                      */                      case SUCCESS_DOWNLOAD_APK:                          mPb.dismiss();                          installApk();                          break;                      default:                          break;                      }                  };              };                            /**              * 创建Activity时调用              *               * ① 设置全屏显示, 由于是Splash界面, 因此不能有标题              * ② 设置布局, 版本号, 执行动画               * ③ 设置当前时间              * ④ 获取SharedPerference配置文件              * ⑤ 开启检查版本号线程, 后续操作都在改线程中操作              *               */              @Override              public void onCreate(Bundle savedInstanceState) {                  super.onCreate(savedInstanceState);                  //隐藏标题栏                  requestWindowFeature(Window.FEATURE_NO_TITLE);                  //隐藏状态栏                  getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,                           WindowManager.LayoutParams.FLAG_FULLSCREEN);                  //设置布局                  setContentView(R.layout.splash);                                    /*                  *  显示当前软件的版本号                  *  获取布局中的TextView控件, 将版本号设置到这个TextView控件中                  */                  tv_version = (TextView) findViewById(R.id.tv_version);                  version =getString(R.string.current_version) + " " + getVersion();                  tv_version.setText(version);                                    /*                  *  在界面设置一个动画, 用来表明正在运行                  *  a. 获取布局                  *  b. 创建一个动画对象                  *  c. 将动画设置到布局中                  */                  splash_rl = (RelativeLayout) findViewById(R.id.splash_rl);                  AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);                  alphaAnimation.setDuration(2000);                  splash_rl.setAnimation(alphaAnimation);                                    /*                  * 这个时间值是用来控制Splash界面显示时间的                  * 记录下这个值, 然后执行到下面, 如果时间差在3秒以内,                   * 就执行下面的操作, 如果时间差不足3秒, 就Thread.sleep时间差                  * 等够3秒在执行下面的操作                  */                  time = System.currentTimeMillis();                            //从SharedPreference中获取一些配置                  sp = getSharedPreferences("config", Context.MODE_PRIVATE);                                    //开启检查版本号线程                  new Thread(new CheckVersionTask()).start();              }                            private final class CheckVersionTask implements Runnable{                  public void run() {                      try {                          /*                          * 获取当前时间, 与onCreate方法中获取的时间进行比较                          * 如果不足3秒, 在等待够3秒之后在执行下面的操作                          */                          long temp = System.currentTimeMillis();                          if(temp - time < 3000){                              SystemClock.sleep(temp - time);                          }                                                    /*                          * 检查配置文件中的设置, 是否设置了自动更新;                           * 如果设置了自动更新, 就执行下面的操作,                          * 如果没有设置自动更新, 就直接进入主界面                          */                          boolean is_auto_update = sp.getBoolean("is_auto_update", true);                          if(!is_auto_update){                              loadMainUI();                              return;                          }                                                    /*                          * 获取更新信息                          * 如果信息不为null, 向handler发信息SUCESS_GET_UPDATEINOF, 执行后续操作                          * 如果信息为null, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作                          * 如果出现异常, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作                          */                          updateInfo = getUpdateInfo(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + XML_FILE_DIRECTORY);                          if(updateInfo != null){                              Message msg = new Message();                              msg.what = SUCESS_GET_UPDATEINOF;                              mHandler.sendMessage(msg);                          }else{                              Message msg = new Message();                              msg.what = ERROR_GET_UPDATEINOF;                              mHandler.sendMessage(msg);                          }                      } catch (Exception e) {                          e.printStackTrace();                          Message msg = new Message();                          msg.what = ERROR_GET_UPDATEINOF;                          mHandler.sendMessage(msg);                      }                  }              }                            /**              * 安装apk文件流程              *               * a. 设置Action : Intent.ACTION_VIEW.              * b. 设置数据和类型 : 设置apk文件的uri 和 MIME类型              * c. 开启安装文件的Activity.              */              protected void installApk() {                  Intent intent = new Intent();                  intent.setAction(Intent.ACTION_VIEW);                  intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");                  startActivity(intent);              }                            /**              * 弹出更新对话框              *               * a. 创建builder对象              * b. 设置标题              * c. 设置对话框显示信息              * d. 设置该对话框不可回退, 如果回退的话就会卡在本界面              * e. 设置确定按钮              * f. 设置取消按钮              * g. 创建对话框              * h. 显示对话框              *               * 确定按钮按下显示进度条对话框              * a. 创建一个进度条对话框              * b. 设置该对话框不能回退              * c. 设置进度条样式              * d. 设置进度条的信息              * e. 显示进度条对话框              * f. 开启一个线程, 下载apk文件              */              protected void showUpdateDialog() {                  //创建builder对象                  AlertDialog.Builder builder = new AlertDialog.Builder(this);                  //设置标题                  builder.setTitle(getString(R.string.update_dialog_tittle));                  //设置对话框信息                  builder.setMessage(updateInfo.getDescription());                  //设置不可回退                  builder.setCancelable(false);                  //设置确定按钮                  builder.setPositiveButton(getString(R.string.confirm), new DialogInterface.OnClickListener() {                      public void onClick(DialogInterface dialog, int which) {                          //创建进度条对话框                          mPb = new ProgressDialog(SplashActivity.this);                          //设置进度条对话框不可回退                          mPb.setCancelable(false);                          //设置进度条对话框样式                          mPb.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);                          //设置进度条对话框的信息                          mPb.setMessage(getString(R.string.update_dialog_messsage));                          //显示进度条对话框                          mPb.show();                          //开启显示进度条对话框线程                          new Thread(new DownloadApkTask()).start();                      }                  });                  builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {                      public void onClick(DialogInterface dialog, int which) {                          loadMainUI();                      }                  });                  //创建更新信息提示对话框                  mUpdateInfoDialog = builder.create();                  //显示更新信息提示对话框                  mUpdateInfoDialog.show();              }                            /**              * 在这个线程中主要执行downloadApk方法, 这个方法传入apk路径和进度条对话框              * 注意 : 下载的前提是sd卡的状态是挂载的              */              private final class DownloadApkTask implements Runnable{                  public void run() {                      if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){                          try {                              SystemClock.sleep(2000);                              apkFile = downloadApk(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + updateInfo.url,mPb);                              Message msg = new Message();                              msg.what = SUCCESS_DOWNLOAD_APK;                              mHandler.sendMessage(msg);                          } catch (Exception e) {                              e.printStackTrace();                              Message msg = new Message();                              msg.what = ERROR_DOWNLOAD_APK;                              mHandler.sendMessage(msg);                          }                      }                  }              }                            /**              * 下载apk更新文件              *                * a. 根据SD卡路径创建文件对象, 这个文件用来保存下载的文件              * b. 创建URL对象              * c. 创建HttpUrlConnection对象              * d. 设置链接对象超时时间              * e. 设置请求方式 get              * f. 如果请求成功执行下面的操作              *               * g. 通过链接对象获取网络资源的大小              * h. 将文件大小设置给进度条对话框              * i. 获取输入流, 并且读取输入流信息              * j. 根据读取到的字节数, 将已经读取的数据设置给进度条对话框              */              public File downloadApk(String path,ProgressDialog pb) throws Exception{                  //创建本地文件对象                  File file = new File(Environment.getExternalStorageDirectory(), getFileName(path));                  //创建HttpURL连接                  URL url = new URL(path);                  HttpURLConnection conn = (HttpURLConnection) url.openConnection();                  conn.setConnectTimeout(5000);                  conn.setRequestMethod("GET");                  if(conn.getResponseCode() == 200){                      int max = conn.getContentLength();                      //设置进度条对话框的最大值                      pb.setMax(max);                      int count = 0;                      InputStream is = conn.getInputStream();                      FileOutputStream fos = new FileOutputStream(file);                      byte[] buffer = new byte[1024];                      int len = 0;                      while((len = is.read(buffer)) != -1){                          fos.write(buffer, 0, len);                          //设置进度条对话框进度                          count = count + len;                          pb.setProgress(count);                      }                      is.close();                      fos.close();                  }                  return file;              }                            private String getFileName(String path){                  return path.substring(path.lastIndexOf("/") + 1);              }                            private String getVersion() {                  try {                      pm = this.getPackageManager();                      PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);                      return packageInfo.versionName;                  } catch (Exception e) {                      e.printStackTrace();                  }                  return null;              }                            /**              * 获取更新信息              *      ① 根据字符串地址创建URL对象              *      ② 根据URL对象创建HttpURLConnection链接对象              *      ③ 设置链接对象5秒超时              *      ④ 设置链接对象获取的方式为get方式              *      ⑤ 如果成功连接, conn.getRequestCode值就是200, 此时就可以获取输入流              *      ⑥ 解析输入流获取更新信息              *                    */              private UpdateInfo getUpdateInfo(String path){                  try {                      URL url = new URL(path);    //创建URL对象                      //创建连接对象                      HttpURLConnection conn = (HttpURLConnection) url.openConnection();                      //设置链接超时                      conn.setConnectTimeout(5000);                      //设置获取方式                      conn.setRequestMethod("GET");                      //如果连接成功, 获取输入流                      if(conn.getResponseCode() == 200){                          InputStream is = conn.getInputStream();                          //解析输入流中的数据, 返回更新信息                          return parserUpdateInfo(is);                      }                  } catch (MalformedURLException e) {                      e.printStackTrace();                  } catch (ProtocolException e) {                      e.printStackTrace();                  } catch (IOException e) {                      e.printStackTrace();                  }                  return null;              }                            /**              * 获取更新信息              *      ① 创建pull解析器              *      ② 为解析器设置编码格式              *      ③ 获取解析事件              *      ④ 遍历整个xml文件节点, 获取标签元素内容              */              private UpdateInfo parserUpdateInfo(InputStream is){                  try {                      UpdateInfo updateInfo = null;                      //1. 创建pull解析解析器                      XmlPullParser parser = Xml.newPullParser();                      //2. 设置解析编码                      parser.setInput(is, "UTF-8");                      //3. 获取解析器解事件, 如解析到文档开始 , 结尾, 标签等                      int eventType = parser.getEventType();                      //4. 在文档结束前一直解析                      while (eventType != XmlPullParser.END_DOCUMENT) {                          switch (eventType) {                          //只解析标签                          case XmlPullParser.START_TAG:                              if ("updateInfo".equals(parser.getName())) {                                  //当解析到updateInfo标签的时候, 跟标签开始, 创建一个UpdateInfo对象                                  updateInfo = new UpdateInfo();                              } else if ("version".equals(parser.getName())) {                                  //解析版本号标签                                  updateInfo.setVersion(parser.nextText());                              } else if ("url".equals(parser.getName())) {                                  //解析url标签                                  updateInfo.setUrl(parser.nextText());                              } else if ("description".equals(parser.getName())) {                                  //解析描述标签                                  updateInfo.setDescription(parser.nextText());                              }                              break;                          default:                              break;                          }                          //每解析完一个元素, 就将解析标志位下移                          eventType = parser.next();                      }                      is.close();                      return updateInfo;                  } catch (XmlPullParserException e) {                      e.printStackTrace();                  } catch (IOException e) {                      e.printStackTrace();                  }                  return null;              }                            private void loadMainUI(){                  Intent intent = new Intent(this,HomeActivity.class);                  startActivity(intent);                  finish();                  overridePendingTransition(R.anim.main_in, R.anim.splash_out);              }                                          public class UpdateInfo {                  private String version; //当前软件版本号                  private String url;     //获取到的软件地址                  private String description; //软件描述                                    public String getVersion() {                      return version;                  }                  public void setVersion(String version) {                      this.version = version;                  }                  public String getUrl() {                      return url;                  }                  public void setUrl(String url) {                      this.url = url;                  }                  public String getDescription() {                      return description;                  }                  public void setDescription(String description) {                      this.description = description;                  }                  @Override                  public String toString() {                      return "UpdateInfo [version=" + version + ", url=" + url                              + ", description=" + description + "]";                  }              }          }  

 

转载自:

http://www.cnblogs.com/android100/p/android-auto-update.html

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[转]Android应用的自动更新