Android应用的自动更新模块实例源码介绍



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

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

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

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

又发现一个BUG : 在弹出更新对话框, 点击确定下载完毕之后会弹出系统自带的替换应用程序对话框, 在这里点取消的话就会一直卡在Splash界面. 设置一个跳转机制解决这个问题.

解决方案 :利用触摸划屏事件, 向左侧划屏100px, 就自动跳转到主界面 , 最后的最终代码已经加上去了

 

  1. /**
  2.  *  设置触摸事件
  3.  *  在手指按下时记录x坐标值 , 在手指抬起的时候记录x坐标值 , 如果两个值相差超过100
  4.  *  那么跳转到主界面
  5.  * @see android.app.Activity#onTouchEvent(android.view.MotionEvent)
  6.  */
  7. @Override
  8. public boolean onTouchEvent(MotionEvent event) {
  9.     switch (event.getAction()) {
  10.         case MotionEvent.ACTION_DOWN :
  11.             touchPositionX0 = (int) event.getX();
  12.             break;
  13.         case MotionEvent.ACTION_UP :
  14.             touchPositionX1 = (int) event.getX();
  15.             if((touchPositionX0 - touchPositionX1) > 100)
  16.                 loadMainUI();
  17.             touchPositionX0 = 0;
  18.             touchPositionX1 = 0;
  19.             break;
  20.     }
  21.     return true;
  22. }

一. 创建Activity

1. 创建Activity大概流程

a. 设置全屏显示.

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

c. 获取当前时间.

d. 获取SharedPerence配置文件.

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

2. 设置窗口样式

(1) 设置全屏显示

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

 

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

b. 配置实现 :

 

  1. AndroidManifest.xml
  2. <activity
  3.     android:name=”myAcitivty”
  4.     android:theme=”@android:style/Theme.NoTitleBar.Fullscreen” />

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

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

 

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

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

 

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

 

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

(3) 屏幕方向设置

a. 配置文件实现

 

  1. //设置横屏
  2. <activity android:name=”myAcitivty”  android:screenOrientation=”landscape” />
  3. //设置竖屏
  4. <activity android:name=”myAcitivty”  android:screenOrientation=”portrait” />

b. 代码实现

  1. //设置横屏
  2. setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  3. //设置竖屏
  4. setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

c. 获取屏幕方向

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

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

3. 设置动画

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

(1) 创建动画

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

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

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

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

(2) 动画常用方法

a. 普通设置

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

b. 设置监听器

  1. alphaAnimation.setAnimationListener(new AnimationListener() {
  2.             //动画开始时回调
  3.             @Override
  4.             public void onAnimationStart(Animation animation) {
  5.             }
  6.             //动画重复执行时回调
  7.             @Override
  8.             public void onAnimationRepeat(Animation animation) {
  9.             }
  10.             //动画执行结束时回调
  11.             @Override
  12.             public void onAnimationEnd(Animation animation) {
  13.             }
  14.         });

4. SharedPerference使用

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

5. onCreate()方法代码

  1. /**
  2.      * 创建Activity时调用
  3.      *
  4.      * ① 设置全屏显示, 由于是Splash界面, 因此不能有标题
  5.      * ② 设置布局, 版本号, 执行动画
  6.      * ③ 设置当前时间
  7.      * ④ 获取SharedPerference配置文件
  8.      * ⑤ 开启检查版本号线程, 后续操作都在改线程中操作
  9.      *
  10.      */
  11.     @Override
  12.     public void onCreate(Bundle savedInstanceState) {
  13.         super.onCreate(savedInstanceState);
  14.         //隐藏标题栏
  15.         requestWindowFeature(Window.FEATURE_NO_TITLE);
  16.         //隐藏状态栏
  17.         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
  18.                 WindowManager.LayoutParams.FLAG_FULLSCREEN);
  19.         //设置布局
  20.         setContentView(R.layout.splash);
  21.         /*
  22.          *  显示当前软件的版本号
  23.          *  获取布局中的TextView控件, 将版本号设置到这个TextView控件中
  24.          */
  25.         tv_version = (TextView) findViewById(R.id.tv_version);
  26.         version =getString(R.string.current_version) + ” ” + getVersion();
  27.         tv_version.setText(version);
  28.         /*
  29.          *  在界面设置一个动画, 用来表明正在运行
  30.          *  a. 获取布局
  31.          *  b. 创建一个动画对象
  32.          *  c. 将动画设置到布局中
  33.          */
  34.         splash_rl = (RelativeLayout) findViewById(R.id.splash_rl);
  35.         AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);
  36.         animation.setDuration(2000);
  37.         splash_rl.setAnimation(animation);
  38.         /*
  39.          * 这个时间值是用来控制Splash界面显示时间的
  40.          * 记录下这个值, 然后执行到下面, 如果时间差在3秒以内,
  41.          * 就执行下面的操作, 如果时间差不足3秒, 就Thread.sleep时间差
  42.          * 等够3秒在执行下面的操作
  43.          */
  44.         time = System.currentTimeMillis();
  45.         //从SharedPreference中获取一些配置
  46.         sp = getSharedPreferences(“config”, Context.MODE_PRIVATE);
  47.         //开启检查版本号线程
  48.         new Thread(new CheckVersionTask()).start();
  49.     }

二. 检查版本号

1. 检查版本号线程

流程 :

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

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

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

源码 :

  1. private final class CheckVersionTask implements Runnable{
  2.     public void run() {
  3.         try {
  4.             /*
  5.              * 获取当前时间, 与onCreate方法中获取的时间进行比较
  6.              * 如果不足3秒, 在等待够3秒之后在执行下面的操作
  7.              */
  8.             long temp = System.currentTimeMillis();
  9.             if(temp - time < 3000){
  10.                 SystemClock.sleep(temp - time);
  11.             }
  12.             /*
  13.              * 检查配置文件中的设置, 是否设置了自动更新;
  14.              * 如果设置了自动更新, 就执行下面的操作,
  15.              * 如果没有设置自动更新, 就直接进入主界面
  16.              */
  17.             boolean is_auto_update = sp.getBoolean(“is_auto_update”, true);
  18.             if(!is_auto_update){
  19.                 loadMainUI();
  20.                 return;
  21.             }
  22.             /*
  23.              * 获取更新信息
  24.              * 如果信息不为null, 向handler发信息SUCESS_GET_UPDATEINOF, 执行后续操作
  25.              * 如果信息为null, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作
  26.              * 如果出现异常, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作
  27.              */
  28.             updateInfo = getUpdateInfo(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + XML_FILE_DIRECTORY);
  29.             if(updateInfo != null){
  30.                 Message msg = new Message();
  31.                 msg.what = SUCESS_GET_UPDATEINOF;
  32.                 mHandler.sendMessage(msg);
  33.             }else{
  34.                 Message msg = new Message();
  35.                 msg.what = ERROR_GET_UPDATEINOF;
  36.                 mHandler.sendMessage(msg);
  37.             }
  38.         } catch (Exception e) {
  39.             e.printStackTrace();
  40.             Message msg = new Message();
  41.             msg.what = ERROR_GET_UPDATEINOF;
  42.             mHandler.sendMessage(msg);
  43.         }
  44.     }
  45.    }

2. 获取版本号方法

流程 :

a. 创URL建对象;

b. 创建HttpURLConnection对象;

c. 设置超时时间;

d. 设置获取方式;

e. 查看链接是否成功;

f. 解析输入流信息;

源码 :

[java] view plaincopy在CODE上查看代码片派生到我的代码片


  1. /**
  2.  * 获取更新信息
  3.  *      ① 根据字符串地址创建URL对象
  4.  *      ② 根据URL对象创建HttpURLConnection链接对象
  5.  *      ③ 设置链接对象5秒超时
  6.  *      ④ 设置链接对象获取的方式为get方式
  7.  *      ⑤ 如果成功连接, conn.getRequestCode值就是200, 此时就可以获取输入流
  8.  *      ⑥ 解析输入流获取更新信息
  9.  *
  10.  */
  11. private UpdateInfo getUpdateInfo(String path){
  12.     try {
  13.         URL url = new URL(path);    //创建URL对象
  14.         //创建连接对象
  15.         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  16.         //设置链接超时
  17.         conn.setConnectTimeout(5000);
  18.         //设置获取方式
  19.         conn.setRequestMethod(“GET”);
  20.         //如果连接成功, 获取输入流
  21.         if(conn.getResponseCode() == 200){
  22.             InputStream is = conn.getInputStream();
  23.             //解析输入流中的数据, 返回更新信息
  24.             return parserUpdateInfo(is);
  25.         }
  26.     } catch (MalformedURLException e) {
  27.         e.printStackTrace();
  28.     } catch (ProtocolException e) {
  29.         e.printStackTrace();
  30.     } catch (IOException e) {
  31.         e.printStackTrace();
  32.     }
  33.     return null;
  34. }

3. 更新信息对象

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

 

  1. public class UpdateInfo {
  2.     private String version; //当前软件版本号
  3.     private String url;     //获取到的软件地址
  4.     private String description; //软件描述
  5.     public String getVersion() {
  6.         return version;
  7.     }
  8.     public void setVersion(String version) {
  9.         this.version = version;
  10.     }
  11.     public String getUrl() {
  12.         return url;
  13.     }
  14.     public void setUrl(String url) {
  15.         this.url = url;
  16.     }
  17.     public String getDescription() {
  18.         return description;
  19.     }
  20.     public void setDescription(String description) {
  21.         this.description = description;
  22.     }
  23.     @Override
  24.     public String toString() {
  25.         return ”UpdateInfo [version=" + version + ", url=" + url
  26.                 + ", description=" + description + "]“;
  27.     }
  28. }

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文件

  1. <?xml version=”1.0″ encoding=”UTF-8″?>
  2. <updateInfo>
  3.   <version>3.2</version>
  4.   <url>http://127.0.0.1:8080/web/mobilesafe.apk</url>
  5.   <description>客户端更新</description>
  6. </updateInfo>

(3) 源码

  1. /**
  2.  * 获取更新信息
  3.  *      ① 创建pull解析器
  4.  *      ② 为解析器设置编码格式
  5.  *      ③ 获取解析事件
  6.  *      ④ 遍历整个xml文件节点, 获取标签元素内容
  7.  */
  8. private UpdateInfo parserUpdateInfo(InputStream is){
  9.     try {
  10.         UpdateInfo updateInfo = null;
  11.         //1. 创建pull解析解析器
  12.         XmlPullParser parser = Xml.newPullParser();
  13.         //2. 设置解析编码
  14.         parser.setInput(is, ”UTF-8″);
  15.         //3. 获取解析器解事件, 如解析到文档开始 , 结尾, 标签等
  16.         int eventType = parser.getEventType();
  17.         //4. 在文档结束前一直解析
  18.         while (eventType != XmlPullParser.END_DOCUMENT) {
  19.             switch (eventType) {
  20.             //只解析标签
  21.             case XmlPullParser.START_TAG:
  22.                 if (“updateInfo”.equals(parser.getName())) {
  23.                     //当解析到updateInfo标签的时候, 跟标签开始, 创建一个UpdateInfo对象
  24.                     updateInfo = new UpdateInfo();
  25.                 } else if (“version”.equals(parser.getName())) {
  26.                     //解析版本号标签
  27.                     updateInfo.setVersion(parser.nextText());
  28.                 } else if (“url”.equals(parser.getName())) {
  29.                     //解析url标签
  30.                     updateInfo.setUrl(parser.nextText());
  31.                 } else if (“description”.equals(parser.getName())) {
  32.                     //解析描述标签
  33.                     updateInfo.setDescription(parser.nextText());
  34.                 }
  35.                 break;
  36.             default:
  37.                 break;
  38.             }
  39.             //每解析完一个元素, 就将解析标志位下移
  40.             eventType = parser.next();
  41.         }
  42.         is.close();
  43.         return updateInfo;
  44.     } catch (XmlPullParserException e) {
  45.         e.printStackTrace();
  46.     } catch (IOException e) {
  47.         e.printStackTrace();
  48.     }
  49.     return null;
  50. }

三. Handler对象

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

  1. private Handler mHandler = new Handler(){
  2.     public void handleMessage(android.os.Message msg) {
  3.         switch (msg.what) {
  4.         /*
  5.          * 获取更新信息错误 , 在断网或者获取信息出现异常执行
  6.          * 提示一下, 之后进入主界面
  7.          */
  8.         case ERROR_GET_UPDATEINOF:
  9.             ToastHint.getInstance().showHint(R.string.fail_to_get_updateinfo);
  10.             loadMainUI();
  11.             break;
  12.         /*
  13.          * 成功获取更新信息, 一般在成功从网上获取xml文件并解析出来
  14.          * 如果版本号相同, 说明不用更新, 直接进入主界面
  15.          * 如果版本号不同, 需要弹出更新对话框
  16.          */
  17.         case SUCESS_GET_UPDATEINOF:
  18.             if(updateInfo.getVersion().equals(version)){
  19.                 loadMainUI();
  20.             }else{
  21.                 showUpdateDialog();
  22.             }
  23.             break;
  24.         /*
  25.          * 下载apk文件出现错误, 中途断网 出现异常等情况
  26.          * 提示后进入主界面
  27.          */
  28.         case ERROR_DOWNLOAD_APK:
  29.             mPb.dismiss();
  30.             ToastHint.getInstance().showHint(R.string.fail_to_get_apk);
  31.             loadMainUI();
  32.             break;
  33.         /*
  34.          * 成功下载apk文件之后执行的操作
  35.          * 取消进度条对话框, 之后安装apk文件
  36.          */
  37.         case SUCCESS_DOWNLOAD_APK:
  38.             mPb.dismiss();
  39.             installApk();
  40.             break;
  41.         default:
  42.             break;
  43.         }
  44.     };
  45. };

四. 下载安装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) 源码

 

 

 

  1. /**
  2.  * 弹出更新对话框
  3.  *
  4.  * a. 创建builder对象
  5.  * b. 设置标题
  6.  * c. 设置对话框显示信息
  7.  * d. 设置该对话框不可回退, 如果回退的话就会卡在本界面
  8.  * e. 设置确定按钮
  9.  * f. 设置取消按钮
  10.  * g. 创建对话框
  11.  * h. 显示对话框
  12.  *
  13.  * 确定按钮按下显示进度条对话框
  14.  * a. 创建一个进度条对话框
  15.  * b. 设置该对话框不能回退
  16.  * c. 设置进度条样式
  17.  * d. 设置进度条的信息
  18.  * e. 显示进度条对话框
  19.  * f. 开启一个线程, 下载apk文件
  20.  */
  21. protected void showUpdateDialog() {
  22.     //创建builder对象
  23.     AlertDialog.Builder builder = new AlertDialog.Builder(this);
  24.     //设置标题
  25.     builder.setTitle(getString(R.string.update_dialog_tittle));
  26.     //设置对话框信息
  27.     builder.setMessage(updateInfo.getDescription());
  28.     //设置不可回退
  29.     builder.setCancelable(false);
  30.     //设置确定按钮
  31.     builder.setPositiveButton(getString(R.string.confirm), new DialogInterface.OnClickListener() {
  32.         public void onClick(DialogInterface dialog, int which) {
  33.             //创建进度条对话框
  34.             mPb = new ProgressDialog(SplashActivity.this);
  35.             //设置进度条对话框不可回退
  36.             mPb.setCancelable(false);
  37.             //设置进度条对话框样式
  38.             mPb.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
  39.             //设置进度条对话框的信息
  40.             mPb.setMessage(getString(R.string.update_dialog_messsage));
  41.             //显示进度条对话框
  42.             mPb.show();
  43.             //开启显示进度条对话框线程
  44.             new Thread(new DownloadApkTask()).start();
  45.         }
  46.     });
  47.     builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
  48.         public void onClick(DialogInterface dialog, int which) {
  49.             loadMainUI();
  50.         }
  51.     });
  52.     //创建更新信息提示对话框
  53.     mUpdateInfoDialog = builder.create();
  54.     //显示更新信息提示对话框
  55.     mUpdateInfoDialog.show();
  56. }

 

2. 下载apk线程

 

 

 

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

 

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

 

 

 

  1. /**
  2.  * 下载apk更新文件
  3.  *
  4.  * a. 根据SD卡路径创建文件对象, 这个文件用来保存下载的文件
  5.  * b. 创建URL对象
  6.  * c. 创建HttpUrlConnection对象
  7.  * d. 设置链接对象超时时间
  8.  * e. 设置请求方式 get
  9.  * f. 如果请求成功执行下面的操作
  10.  *
  11.  * g. 通过链接对象获取网络资源的大小
  12.  * h. 将文件大小设置给进度条对话框
  13.  * i. 获取输入流, 并且读取输入流信息
  14.  * j. 根据读取到的字节数, 将已经读取的数据设置给进度条对话框
  15.  */
  16. public File downloadApk(String path,ProgressDialog pb) throws Exception{
  17.     //创建本地文件对象
  18.     File file = new File(Environment.getExternalStorageDirectory(), getFileName(path));
  19.     //创建HttpURL连接
  20.     URL url = new URL(path);
  21.     HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  22.     conn.setConnectTimeout(5000);
  23.     conn.setRequestMethod(“GET”);
  24.     if(conn.getResponseCode() == 200){
  25.         int max = conn.getContentLength();
  26.         //设置进度条对话框的最大值
  27.         pb.setMax(max);
  28.         int count = 0;
  29.         InputStream is = conn.getInputStream();
  30.         FileOutputStream fos = new FileOutputStream(file);
  31.         byte[] buffer = new byte[1024];
  32.         int len = 0;
  33.         while((len = is.read(buffer)) != -1){
  34.             fos.write(buffer, 0, len);
  35.             //设置进度条对话框进度
  36.             count = count + len;
  37.             pb.setProgress(count);
  38.         }
  39.         is.close();
  40.         fos.close();
  41.     }
  42.     return file;
  43. }

 

4. 安装apk文件

 

  1. /**
  2.  * 安装apk文件流程
  3.  *
  4.  * a. 设置Action : Intent.ACTION_VIEW.
  5.  * b. 设置数据和类型 : 设置apk文件的uri 和 MIME类型
  6.  * c. 开启安装文件的Activity.
  7.  */
  8. protected void installApk() {
  9.     Intent intent = new Intent();
  10.     intent.setAction(Intent.ACTION_VIEW);
  11.     intent.setDataAndType(Uri.fromFile(apkFile), ”application/vnd.android.package-archive”);
  12.     startActivity(intent);
  13. }

 

五. 相关的源码

 

(1) 布局文件

splash.xml

 

 

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2. <RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
  3.     android:layout_width=”match_parent”
  4.     android:layout_height=”match_parent”
  5.     android:background=”@drawable/ivt_splash”
  6.     android:id=”@+id/splash_rl”>
  7.     <ProgressBar android:id=”@+id/pb”
  8.         android:layout_width=”wrap_content”
  9.         android:layout_height=”wrap_content”
  10.         android:layout_centerHorizontal=”true”
  11.         android:layout_alignParentBottom=”true”
  12.         android:layout_marginBottom=”30dip”/>
  13.     <TextView android:id=”@+id/tv_version”
  14.        android:layout_width=”wrap_content”
  15.         android:layout_height=”wrap_content”
  16.         android:layout_centerHorizontal=”true”
  17.         android:layout_above=”@id/pb”
  18.         android:layout_marginBottom=”60dip”
  19.         android:textSize=”30sp”
  20.         android:textColor=”#17A6E8″
  21.         android:text=”version”
  22.         />
  23. </RelativeLayout>

 

 

(2) Activity页面切换动画

 

main_in.xml

 

 

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2. <set xmlns:android=”http://schemas.android.com/apk/res/android”
  3.      >
  4.     <translate
  5.         android:fromXDelta=”100%p”
  6.         android:toXDelta=”0″
  7.         android:fromYDelta=”0″
  8.         android:toYDelta=”0″
  9.         android:duration=”200″
  10.         />
  11. </set>

 

 

splash_out.xml

 

 

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2. <set xmlns:android=”http://schemas.android.com/apk/res/android”
  3.      >
  4.     <translate
  5.         android:fromXDelta=”0″
  6.         android:toXDelta=”-100%p”
  7.         android:fromYDelta=”0″
  8.         android:toYDelta=”0″
  9.         android:duration=”200″
  10.         />
  11. </set>

 

 

(3) SplashActivity源码

 

SplashActivity.java

 

  1. public class SplashActivity extends Activity {
  2.     private static final String TAG = ”SplashActivity”;
  3.     public static final int ERROR_GET_UPDATEINOF = 0;
  4.     public static final int SUCESS_GET_UPDATEINOF = 1;
  5.     public static final int ERROR_DOWNLOAD_APK = 2;
  6.     public static final int SUCCESS_DOWNLOAD_APK = 3;
  7.     private static final String XML_FILE_DIRECTORY = ”updateinfo.xml”;
  8.     private static final String UPDATE_FOLDER_DIRECTORY = ”/webupdate/”;
  9.     private TextView tv_version;
  10.     private PackageManager pm;
  11.     private String version;
  12.     private UpdateInfo updateInfo;
  13.     private Dialog mUpdateInfoDialog;
  14.     private ProgressDialog mPb;
  15.     private File apkFile;
  16.     private RelativeLayout splash_rl;
  17.     private long time;
  18.     private SharedPreferences sp;
  19.     private int touchPositionX0;
  20.     private int touchPositionX1;
  21.     private Handler mHandler = new Handler(){
  22.         public void handleMessage(android.os.Message msg) {
  23.             switch (msg.what) {
  24.             /*
  25.              * 获取更新信息错误 , 在断网或者获取信息出现异常执行
  26.              * 提示一下, 之后进入主界面
  27.              */
  28.             case ERROR_GET_UPDATEINOF:
  29.                 ToastHint.getInstance().showHint(R.string.fail_to_get_updateinfo);
  30.                 loadMainUI();
  31.                 break;
  32.             /*
  33.              * 成功获取更新信息, 一般在成功从网上获取xml文件并解析出来
  34.              * 如果版本号相同, 说明不用更新, 直接进入主界面
  35.              * 如果版本号不同, 需要弹出更新对话框
  36.              */
  37.             case SUCESS_GET_UPDATEINOF:
  38.                 if(updateInfo.getVersion().equals(version)){
  39.                     loadMainUI();
  40.                 }else{
  41.                     showUpdateDialog();
  42.                 }
  43.                 break;
  44.             /*
  45.              * 下载apk文件出现错误, 中途断网 出现异常等情况
  46.              * 提示后进入主界面
  47.              */
  48.             case ERROR_DOWNLOAD_APK:
  49.                 mPb.dismiss();
  50.                 ToastHint.getInstance().showHint(R.string.fail_to_get_apk);
  51.                 loadMainUI();
  52.                 break;
  53.             /*
  54.              * 成功下载apk文件之后执行的操作
  55.              * 取消进度条对话框, 之后安装apk文件
  56.              */
  57.             case SUCCESS_DOWNLOAD_APK:
  58.                 mPb.dismiss();
  59.                 installApk();
  60.                 break;
  61.             default:
  62.                 break;
  63.             }
  64.         };
  65.     };
  66.     /**
  67.      * 创建Activity时调用
  68.      *
  69.      * ① 设置全屏显示, 由于是Splash界面, 因此不能有标题
  70.      * ② 设置布局, 版本号, 执行动画
  71.      * ③ 设置当前时间
  72.      * ④ 获取SharedPerference配置文件
  73.      * ⑤ 开启检查版本号线程, 后续操作都在改线程中操作
  74.      *
  75.      */
  76.     @Override
  77.     public void onCreate(Bundle savedInstanceState) {
  78.         super.onCreate(savedInstanceState);
  79.         //隐藏标题栏
  80.         requestWindowFeature(Window.FEATURE_NO_TITLE);
  81.         //隐藏状态栏
  82.         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
  83.                 WindowManager.LayoutParams.FLAG_FULLSCREEN);
  84.         //设置布局
  85.         setContentView(R.layout.splash);
  86.         /*
  87.          *  显示当前软件的版本号
  88.          *  获取布局中的TextView控件, 将版本号设置到这个TextView控件中
  89.          */
  90.         tv_version = (TextView) findViewById(R.id.tv_version);
  91.         version =getString(R.string.current_version) + ” ” + getVersion();
  92.         tv_version.setText(version);
  93.         /*
  94.          *  在界面设置一个动画, 用来表明正在运行
  95.          *  a. 获取布局
  96.          *  b. 创建一个动画对象
  97.          *  c. 将动画设置到布局中
  98.          */
  99.         splash_rl = (RelativeLayout) findViewById(R.id.splash_rl);
  100.         AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);
  101.         alphaAnimation.setDuration(2000);
  102.         splash_rl.setAnimation(alphaAnimation);
  103.         /*
  104.          * 这个时间值是用来控制Splash界面显示时间的
  105.          * 记录下这个值, 然后执行到下面, 如果时间差在3秒以内,
  106.          * 就执行下面的操作, 如果时间差不足3秒, 就Thread.sleep时间差
  107.          * 等够3秒在执行下面的操作
  108.          */
  109.         time = System.currentTimeMillis();
  110.         //从SharedPreference中获取一些配置
  111.         sp = getSharedPreferences(“config”, Context.MODE_PRIVATE);
  112.         //开启检查版本号线程
  113.         new Thread(new CheckVersionTask()).start();
  114.     }
  115.     private final class CheckVersionTask implements Runnable{
  116.         public void run() {
  117.             try {
  118.                 /*
  119.                  * 获取当前时间, 与onCreate方法中获取的时间进行比较
  120.                  * 如果不足3秒, 在等待够3秒之后在执行下面的操作
  121.                  */
  122.                 long temp = System.currentTimeMillis();
  123.                 if(temp - time < 3000){
  124.                     SystemClock.sleep(temp - time);
  125.                 }
  126.                 /*
  127.                  * 检查配置文件中的设置, 是否设置了自动更新;
  128.                  * 如果设置了自动更新, 就执行下面的操作,
  129.                  * 如果没有设置自动更新, 就直接进入主界面
  130.                  */
  131.                 boolean is_auto_update = sp.getBoolean(“is_auto_update”, true);
  132.                 if(!is_auto_update){
  133.                     loadMainUI();
  134.                     return;
  135.                 }
  136.                 /*
  137.                  * 获取更新信息
  138.                  * 如果信息不为null, 向handler发信息SUCESS_GET_UPDATEINOF, 执行后续操作
  139.                  * 如果信息为null, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作
  140.                  * 如果出现异常, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作
  141.                  */
  142.                 updateInfo = getUpdateInfo(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + XML_FILE_DIRECTORY);
  143.                 if(updateInfo != null){
  144.                     Message msg = new Message();
  145.                     msg.what = SUCESS_GET_UPDATEINOF;
  146.                     mHandler.sendMessage(msg);
  147.                 }else{
  148.                     Message msg = new Message();
  149.                     msg.what = ERROR_GET_UPDATEINOF;
  150.                     mHandler.sendMessage(msg);
  151.                 }
  152.             } catch (Exception e) {
  153.                 e.printStackTrace();
  154.                 Message msg = new Message();
  155.                 msg.what = ERROR_GET_UPDATEINOF;
  156.                 mHandler.sendMessage(msg);
  157.             }
  158.         }
  159.     }
  160.     /**
  161.      * 安装apk文件流程
  162.      *
  163.      * a. 设置Action : Intent.ACTION_VIEW.
  164.      * b. 设置数据和类型 : 设置apk文件的uri 和 MIME类型
  165.      * c. 开启安装文件的Activity.
  166.      */
  167.     protected void installApk() {
  168.         Intent intent = new Intent();
  169.         intent.setAction(Intent.ACTION_VIEW);
  170.         intent.setDataAndType(Uri.fromFile(apkFile), ”application/vnd.android.package-archive”);
  171.         startActivity(intent);
  172.     }
  173.     /**
  174.      * 弹出更新对话框
  175.      *
  176.      * a. 创建builder对象
  177.      * b. 设置标题
  178.      * c. 设置对话框显示信息
  179.      * d. 设置该对话框不可回退, 如果回退的话就会卡在本界面
  180.      * e. 设置确定按钮
  181.      * f. 设置取消按钮
  182.      * g. 创建对话框
  183.      * h. 显示对话框
  184.      *
  185.      * 确定按钮按下显示进度条对话框
  186.      * a. 创建一个进度条对话框
  187.      * b. 设置该对话框不能回退
  188.      * c. 设置进度条样式
  189.      * d. 设置进度条的信息
  190.      * e. 显示进度条对话框
  191.      * f. 开启一个线程, 下载apk文件
  192.      */
  193.     protected void showUpdateDialog() {
  194.         //创建builder对象
  195.         AlertDialog.Builder builder = new AlertDialog.Builder(this);
  196.         //设置标题
  197.         builder.setTitle(getString(R.string.update_dialog_tittle));
  198.         //设置对话框信息
  199.         builder.setMessage(updateInfo.getDescription());
  200.         //设置不可回退
  201.         builder.setCancelable(false);
  202.         //设置确定按钮
  203.         builder.setPositiveButton(getString(R.string.confirm), new DialogInterface.OnClickListener() {
  204.             public void onClick(DialogInterface dialog, int which) {
  205.                 //创建进度条对话框
  206.                 mPb = new ProgressDialog(SplashActivity.this);
  207.                 //设置进度条对话框不可回退
  208.                 mPb.setCancelable(false);
  209.                 //设置进度条对话框样式
  210.                 mPb.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
  211.                 //设置进度条对话框的信息
  212.                 mPb.setMessage(getString(R.string.update_dialog_messsage));
  213.                 //显示进度条对话框
  214.                 mPb.show();
  215.                 //开启显示进度条对话框线程
  216.                 new Thread(new DownloadApkTask()).start();
  217.             }
  218.         });
  219.         builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
  220.             public void onClick(DialogInterface dialog, int which) {
  221.                 loadMainUI();
  222.             }
  223.         });
  224.         //创建更新信息提示对话框
  225.         mUpdateInfoDialog = builder.create();
  226.         //显示更新信息提示对话框
  227.         mUpdateInfoDialog.show();
  228.     }
  229.     /**
  230.      * 在这个线程中主要执行downloadApk方法, 这个方法传入apk路径和进度条对话框
  231.      * 注意 : 下载的前提是sd卡的状态是挂载的
  232.      */
  233.     private final class DownloadApkTask implements Runnable{
  234.         public void run() {
  235.             if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
  236.                 try {
  237.                     SystemClock.sleep(2000);
  238.                     apkFile = downloadApk(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + updateInfo.url,mPb);
  239.                     Message msg = new Message();
  240.                     msg.what = SUCCESS_DOWNLOAD_APK;
  241.                     mHandler.sendMessage(msg);
  242.                 } catch (Exception e) {
  243.                     e.printStackTrace();
  244.                     Message msg = new Message();
  245.                     msg.what = ERROR_DOWNLOAD_APK;
  246.                     mHandler.sendMessage(msg);
  247.                 }
  248.             }
  249.         }
  250.     }
  251.     /**
  252.      * 下载apk更新文件
  253.      *
  254.      * a. 根据SD卡路径创建文件对象, 这个文件用来保存下载的文件
  255.      * b. 创建URL对象
  256.      * c. 创建HttpUrlConnection对象
  257.      * d. 设置链接对象超时时间
  258.      * e. 设置请求方式 get
  259.      * f. 如果请求成功执行下面的操作
  260.      *
  261.      * g. 通过链接对象获取网络资源的大小
  262.      * h. 将文件大小设置给进度条对话框
  263.      * i. 获取输入流, 并且读取输入流信息
  264.      * j. 根据读取到的字节数, 将已经读取的数据设置给进度条对话框
  265.      */
  266.     public File downloadApk(String path,ProgressDialog pb) throws Exception{
  267.         //创建本地文件对象
  268.         File file = new File(Environment.getExternalStorageDirectory(), getFileName(path));
  269.         //创建HttpURL连接
  270.         URL url = new URL(path);
  271.         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  272.         conn.setConnectTimeout(5000);
  273.         conn.setRequestMethod(“GET”);
  274.         if(conn.getResponseCode() == 200){
  275.             int max = conn.getContentLength();
  276.             //设置进度条对话框的最大值
  277.             pb.setMax(max);
  278.             int count = 0;
  279.             InputStream is = conn.getInputStream();
  280.             FileOutputStream fos = new FileOutputStream(file);
  281.             byte[] buffer = new byte[1024];
  282.             int len = 0;
  283.             while((len = is.read(buffer)) != -1){
  284.                 fos.write(buffer, 0, len);
  285.                 //设置进度条对话框进度
  286.                 count = count + len;
  287.                 pb.setProgress(count);
  288.             }
  289.             is.close();
  290.             fos.close();
  291.         }
  292.         return file;
  293.     }
  294.     private String getFileName(String path){
  295.         return path.substring(path.lastIndexOf(“/”) + 1);
  296.     }
  297.     private String getVersion() {
  298.         try {
  299.             pm = this.getPackageManager();
  300.             PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
  301.             return packageInfo.versionName;
  302.         } catch (Exception e) {
  303.             e.printStackTrace();
  304.         }
  305.         return null;
  306.     }
  307.     /**
  308.      * 获取更新信息
  309.      *      ① 根据字符串地址创建URL对象
  310.      *      ② 根据URL对象创建HttpURLConnection链接对象
  311.      *      ③ 设置链接对象5秒超时
  312.      *      ④ 设置链接对象获取的方式为get方式
  313.      *      ⑤ 如果成功连接, conn.getRequestCode值就是200, 此时就可以获取输入流
  314.      *      ⑥ 解析输入流获取更新信息
  315.      *
  316.      */
  317.     private UpdateInfo getUpdateInfo(String path){
  318.         try {
  319.             URL url = new URL(path);    //创建URL对象
  320.             //创建连接对象
  321.             HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  322.             //设置链接超时
  323.             conn.setConnectTimeout(5000);
  324.             //设置获取方式
  325.             conn.setRequestMethod(“GET”);
  326.             //如果连接成功, 获取输入流
  327.             if(conn.getResponseCode() == 200){
  328.                 InputStream is = conn.getInputStream();
  329.                 //解析输入流中的数据, 返回更新信息
  330.                 return parserUpdateInfo(is);
  331.             }
  332.         } catch (MalformedURLException e) {
  333.             e.printStackTrace();
  334.         } catch (ProtocolException e) {
  335.             e.printStackTrace();
  336.         } catch (IOException e) {
  337.             e.printStackTrace();
  338.         }
  339.         return null;
  340.     }
  341.     /**
  342.      * 获取更新信息
  343.      *      ① 创建pull解析器
  344.      *      ② 为解析器设置编码格式
  345.      *      ③ 获取解析事件
  346.      *      ④ 遍历整个xml文件节点, 获取标签元素内容
  347.      */
  348.     private UpdateInfo parserUpdateInfo(InputStream is){
  349.         try {
  350.             UpdateInfo updateInfo = null;
  351.             //1. 创建pull解析解析器
  352.             XmlPullParser parser = Xml.newPullParser();
  353.             //2. 设置解析编码
  354.             parser.setInput(is, ”UTF-8″);
  355.             //3. 获取解析器解事件, 如解析到文档开始 , 结尾, 标签等
  356.             int eventType = parser.getEventType();
  357.             //4. 在文档结束前一直解析
  358.             while (eventType != XmlPullParser.END_DOCUMENT) {
  359.                 switch (eventType) {
  360.                 //只解析标签
  361.                 case XmlPullParser.START_TAG:
  362.                     if (“updateInfo”.equals(parser.getName())) {
  363.                         //当解析到updateInfo标签的时候, 跟标签开始, 创建一个UpdateInfo对象
  364.                         updateInfo = new UpdateInfo();
  365.                     } else if (“version”.equals(parser.getName())) {
  366.                         //解析版本号标签
  367.                         updateInfo.setVersion(parser.nextText());
  368.                     } else if (“url”.equals(parser.getName())) {
  369.                         //解析url标签
  370.                         updateInfo.setUrl(parser.nextText());
  371.                     } else if (“description”.equals(parser.getName())) {
  372.                         //解析描述标签
  373.                         updateInfo.setDescription(parser.nextText());
  374.                     }
  375.                     break;
  376.                 default:
  377.                     break;
  378.                 }
  379.                 //每解析完一个元素, 就将解析标志位下移
  380.                 eventType = parser.next();
  381.             }
  382.             is.close();
  383.             return updateInfo;
  384.         } catch (XmlPullParserException e) {
  385.             e.printStackTrace();
  386.         } catch (IOException e) {
  387.             e.printStackTrace();
  388.         }
  389.         return null;
  390.     }
  391.     private void loadMainUI(){
  392.         Intent intent = new Intent(this,HomeActivity.class);
  393.         startActivity(intent);
  394.         finish();
  395.         overridePendingTransition(R.anim.main_in, R.anim.splash_out);
  396.     }
  397.     public class UpdateInfo {
  398.         private String version; //当前软件版本号
  399.         private String url;     //获取到的软件地址
  400.         private String description; //软件描述
  401.         public String getVersion() {
  402.             return version;
  403.         }
  404.         public void setVersion(String version) {
  405.             this.version = version;
  406.         }
  407.         public String getUrl() {
  408.             return url;
  409.         }
  410.         public void setUrl(String url) {
  411.             this.url = url;
  412.         }
  413.         public String getDescription() {
  414.             return description;
  415.         }
  416.         public void setDescription(String description) {
  417.             this.description = description;
  418.         }
  419.         @Override
  420.         public String toString() {
  421.             return ”UpdateInfo [version=" + version + ", url=" + url
  422.                     + ", description=" + description + "]“;
  423.         }
  424.     }
  425.     /**
  426.      *  设置触摸事件
  427.      *  在手指按下时记录x坐标值 , 在手指抬起的时候记录x坐标值 , 如果两个值相差超过100
  428.      *  那么跳转到主界面
  429.      * @see android.app.Activity#onTouchEvent(android.view.MotionEvent)
  430.      */
  431.     @Override
  432.     public boolean onTouchEvent(MotionEvent event) {
  433.         switch (event.getAction()) {
  434.             case MotionEvent.ACTION_DOWN :
  435.                 touchPositionX0 = (int) event.getX();
  436.                 break;
  437.             case MotionEvent.ACTION_UP :
  438.                 touchPositionX1 = (int) event.getX();
  439.                 if((touchPositionX0 - touchPositionX1) > 100)
  440.                     loadMainUI();
  441.                 touchPositionX0 = 0;
  442.                 touchPositionX1 = 0;
  443.                 break;
  444.         }
  445.         return true;
  446.     }
  447. }