Android项目实战:手机安全卫士开发案例解析
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.1.5 下载服务端的apk文件

当程序连接服务器检测到新版本并弹出升级对话框的提示时,我们可以单击“下载”按钮将服务器端的最新apk下载到本地的Sdcard上。单击“取消”按钮时,直接进入当前版本程序的主界面。显示升级提示对话框的核心代码如下:

    /**
      * 显示升级提示的对话框
       */
protected void showUpdateDialog() {
        //创建对话框的构造器
         AlertDialog.Builder builder = new Builder(this);
        //设置对话框的提示内容
builder.setIcon(getResources().getDrawable(R.drawable.notification));
        //设置升级标题
         builder.setTitle("升级提示");
        //设置升级提示内容
         builder.setMessage(info.getDescription());
        //创建下载进度条
         pd = new ProgressDialog(SplashActivity.this);
        //设置进度条在显示时的提示消息
         pd.setMessage("正在下载");
        //指定显示下载进度条为水平形状
         pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        //设置升级按钮
        builder.setPositiveButton("升级", new OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                Log.i(TAG, "升级,下载" + info.getApkurl());
                //判断Sdcard是否存在
                 if (Environment.MEDIA_MOUNTED.equals(Environment
                        .getExternalStorageState())) {
                    pd.show();//显示下载进度条
                     //开启子线程下载apk
                    new Thread() {
                        public void run() {
                    //获取服务端新版本apk的下载地址
                      String path = info.getApkurl();
                    //获取最新apk的文件名
                      String filename = DownLoadUtil.getFilename(path);
                    //在Sdcard上创建一个文件
                      File file = new File(Environment
                        .getExternalStorageDirectory(), filename);
                    //得到下载后的apk文件
                      file = DownLoadUtil.getFile(path,
                            file.getAbsolutePath(), pd);
                    if (file != null) {
                    //向主线程发送下载成功的消息
                       Message msg = Message.obtain();
                    msg.what = DOWNLOAD_SUCCESS;
                    msg.obj = file;
                    handler.sendMessage(msg);
                    } else {
                    //向主线程发送下载失败的消息
                       Message msg = Message.obtain();
                    msg.what = DOWNLOAD_ERROR;
                    handler.sendMessage(msg);
                    }
                    pd.dismiss();//下载结束后,将下载的进度条关闭
                };
          }.start();
                } else {
        Toast.makeText(getApplicationContext(), "sd卡不可用",
                            1).show();
        loadMainUI();//进入程序主界面
                }
            }
        });
        builder.setNegativeButton("取消", new OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
              loadMainUI();
            }
      });
      builder.create().show();
    }

说明:由于该方法在主线程中执行,而下载文件又是一个比较耗时的操作,所以我们开启一个新的线程来执行该任务。而且,在下载完成后,我们必须调用 pd 对象的 dismiss()方法,否则该进度条不会自动消失。

下载apk的工具类位于“com.guoshisp.mobilesafe.utils”包下的 ownLoadUtil,该类的具体实现代码如下:

package com.guoshisp.mobilesafe.utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Environment;
/**
  * 下载的工具类:下载文件的路径;下载文件后保存的路径;关心进度条;上、下文
  */
public class DownLoadUtil {
    /**
    * 下载一个文件
     *
    * @param urlpath
    * 路径
     * @param filepath
    * 保存到本地的文件路径
     * @param pd
    * 进度条对话框
     * @return 下载后的apk
    */
    public static File getFile(String urlpath, String filepath,
            ProgressDialog pd) {
        try {
            URL url = new URL(urlpath);
            File file = new File(filepath);
            FileOutputStream fos = new FileOutputStream(file);
            HttpURLConnection conn = (HttpURLConnection) url. openConnection();
            //下载的请求是GET方式,conn的默认方式也是GET请求
             conn.setRequestMethod("GET");
            //服务端的响应时间
             conn.setConnectTimeout(5000);
            //获取服务端的文件总长度
             int max = conn.getContentLength();
            //将进度条的最大值设置为要下载的文件的总长度
             pd.setMax(max);
            //获取要下载的apk的文件的输入流
             InputStream is = conn.getInputStream();
            //设置一个缓存区
             byte[] buffer = new byte[1024];
             int len = 0;
             int process = 0;
             while ((len = is.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
                //每读取一次输入流,就刷新一次下载进度
                  process+=len;
                pd.setProgress(process);
                //设置睡眠时间,便于观察下载进度
                Thread.sleep(30);
            }
            //刷新缓存数据到文件中
              fos.flush();
            //关流
             fos.close();
            is.close();
            return file;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
    * 获取一个路径中的文件名。例如:mobilesafe.apk
    *
    * @param urlpath
    * @return
    */
    public static String getFilename(String urlpath) {
        return urlpath
                .substring(urlpath.lastIndexOf("/") + 1, urlpath.length());
    }
}

由于涉及对Sdcard的操作,所以我们需要在清单文件中配置相应的权限:

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission
    android:name="android.permission.MOUNT_UNMOUNT_ FILESYSTEMS" />

当再次运行并单击“升级”按钮时,界面效果如图1-10所示。

图1-10