• [转]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

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    Vue过滤器使用
    vue基础
    lesson
    lesson
    lesson
    lesson
    rm 命令详解
    alias 命令详解
    cd 命令详解
    cut 命令详解
  • 原文地址:https://www.cnblogs.com/vip-ygh/p/4002286.html
Copyright © 2020-2023  润新知