【边做项目边学Android】手机安全卫士04_02:从服务器下载并安装新版本安装包

文件下载

1. 下载文件业务类

下载文件的操作也属于业务方法,所以在com.liuhao.mobilesafe.engine中创建一个DownloadFileTask下载文件的类。

其中的getFile方法,用于从服务器文件路径上下载文件至本地文件目录。

package com.liuhao.mobilesafe.engine;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class DownloadFileTask {

    /**
     * @param path
     *            服务器文件路径
     * @param filepath
     *            本地文件路径
     * @return 本节文件对象
     * @throws Exception
     */
    public static File getFile(String path, String filepath) throws Exception {

        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setReadTimeout(5000);

        // 读取数据没有异常
        if (conn.getResponseCode() == 200) {
            InputStream is = conn.getInputStream();// 获取文件输入流
            File file = new File(filepath);// 本地文件对象
            FileOutputStream fos = new FileOutputStream(file);//本地文件输出流
            byte[] buffer = new byte[1024];
            int length = 0;
            while ((length = is.read(buffer)) != -1) {
                fos.write(buffer, 0, length);
            }
            fos.flush();
            fos.close();
            is.close();

            return file;
        }

        return null;
    }

} 

2.使用下载文件类

在用户点击“确定”后,会进行下载。

其中定义了一个进度条,用来显示下载过程:

private ProgressDialog pd;// 进度条

pd = new ProgressDialog(this);
pd.setMessage("正在下载,请耐心等待。o(∩_∩)o");// 设置进度条显示的内容

builder.setPositiveButton("确定", new OnClickListener() { // 设置用户选择确定时的按键操作

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Log.i(TAG, "下载pak文件:" + info.getApkurl());

                        // 判断sd卡是否可用
                        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

                            // 调用子线程进行下载
                            DownloadFileThreadTask task = new DownloadFileThreadTask(
                                    info.getApkurl(), Environment.getExternalStorageDirectory().getPath() + "/aanew.apk");
                            pd.show();// 显示下载进度条
                            new Thread(task).start();// 启动子线程
                        } else {
                            Toast.makeText(getApplicationContext(), "sd卡不可用",
                                    Toast.LENGTH_LONG).show();
                            loadMainUI();
                        }
                    }
                });

// 子线程,用于下载文件,因为下载文件比较耗时
    private class DownloadFileThreadTask implements Runnable {

        private String path;// 服务器路径
        private String filepath;// 本地文件路径

        public DownloadFileThreadTask(String path, String filepath) {
            this.path = path;
            this.filepath = filepath;
        }

        @Override
        public void run() {
            try {
                File file = DownloadFileTask.getFile(path, filepath);
                Log.i(TAG, "下载更新apk成功");
                pd.dismiss();

            } catch (Exception e) {
                e.printStackTrace();
                Toast.makeText(getApplicationContext(), "下载文件失败",
                        Toast.LENGTH_LONG).show();
                pd.dismiss();
                loadMainUI();
            }
        }

    }

3.添加权限

由于下载文件需要对sd卡进行读写,因袭需要sd卡的权限:<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

写外部存储设备的权限:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

4.配置服务器端的apk文件(高版本的)

将当前版本号改为2.0,生成一个apk安装包,放到之前指定的目录(%TOMCAT_HOME%\webapps\ROOT),然后再改回来。


异常处理:

ERROR/AndroidRuntime(1540): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application

我们在ActivityGroup或者TabActivity中的子Activity创建Dialog若使用以下的代码

progressDialog = new ProgressDialog(XXX.this)  

创建就会出现如下Exception:

ERROR/AndroidRuntime(6362): android.view.WindowManager$BadTokenException: Unable to add window -- token [email protected] is not valid; is your activity running?

而该使用:

progressDialog = new ProgressDialog(getParent())  

原因分析:

因为new对话框的时候,参数content 指定成了this,即指向当前子Activity的content。但子Activity是动态创建的,不能保证一直存在。其父Activity的content是稳定存在的,所以有下面的解决办法。

若ActivityGroup中嵌套ActivityGroup,嵌套多少就该使用多少个getParent()。

为什么要使用getParent我们可以从柯元旦的《Android内核剖析》中第十章”Ams内部原理“中的ActivityGroup的内部机制来理解:

TabActivity的父类是ActivityGroup,而ActivityGroup的父类是Activity。因此从Ams的角度来看,ActivityGroup与普通的Activity没有什么区别,其生命周期包括标准的start,stop,resume,destroy等,而且系统中只允许同时允许一个ActivityGroup.但ActivityGroup内部有一个重要成员变量,其类型为LocalActivityManager,该类的最大特点在于它可以访问应用进程的主类,即ActivityThread类。Ams要启动某个Activity或者赞同某个Activity都是通过ActivityThread类执行的,而LocalActivityManager类就意味着可以通过它来装载不同的Activity,并控制Activity的不同的状态。注意,这里是装载,而不是启动,这点很重要。所谓的启动,一般是指会创建一个进程(如果所在的应用经常还不存在)运行该Activity,而装载仅仅是指把该Activity作为一个普通类进行加载,并创建一个该类的对象而已,而该类的任何函数都没有被运行。装载Activity对象的过程对AmS来讲是完全不可见的,那些嵌入的Activity仅仅贡献了自己所包含的Window窗口而已。而子Activity的不同状态是通过moveToState来处理的。

所以子Activity不是像普通的Activity一样,它只是提供Window而已,所以在创建Dialog时就应该使用getParent获取ActivityGroup真正的Activity,才可以加Dialog加入Activity中。

参考:

http://aijiawang-126-com.iteye.com/blog/1717368

下载部署完毕后,运行效果

 


文件安装(下载完成后自动安装)(知识点:Intent

/**
* 安装apk文件
* @param file
*/
private void install(File file){
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");

    finish();// 终结当前Activity
    startActivity(intent);// 激活新的Activity
} 


让当前Activity延时2秒再判断是否需要更新(知识点:Handler)

private String versiontext;

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);

        if (isNeedUpdate(versiontext)) {
            Log.i(TAG, "弹出升级对话框");
            showUpdateDialog();
        }
    }
}; 

onCreate方法中:

// 让当前Activity延时两秒钟,再去检查更新
new Thread(){
    public void run() {
        try {
            sleep(2000);
            handler.sendEmptyMessage(0);// 向主线程发送一条空消息
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    };
}.start(); 

设置下载进度条显示下载进度(知识点:ProgressDialog

在下载任务类DownloadFileTask的getFile()方法中添加一个ProgressDialog作为参数,在下载过程中对其进行设置。

/**
* @param path
*            服务器文件路径
* @param filepath
*            本地文件路径
* @param pd
*               进度条,用以显示下载进度
* @return 本地文件对象
* @throws Exception
*/
public static File getFile(String path, String filepath, ProgressDialog pd) throws Exception {

    URL url = new URL(path);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.setReadTimeout(5000);

    // 读取数据没有异常
    if (conn.getResponseCode() == 200) {
        int total = conn.getContentLength();// 获取内容的总长度
        pd.setMax(total);
        InputStream is = conn.getInputStream();// 获取文件输入流
        File file = new File(filepath);// 本地文件对象
        FileOutputStream fos = new FileOutputStream(file);//本地文件输出流
        byte[] buffer = new byte[1024];
        int length = 0;
        int process = 0;// 当前进度
        while ((length = is.read(buffer)) != -1) {
            fos.write(buffer, 0, length);
            process += length;
            pd.setProgress(process);// 设置当前进度
            Thread.sleep(50);
        }
        fos.flush();
        fos.close();
        is.close();

        return file;
    }

    return null;
} 

由于默认的ProgressDialog是不显示下载进度的,因此需要进行设置。

pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);// 默认情况下不显示进度,这个设置用于显示进度

效果:

时间: 2024-11-02 18:39:15

【边做项目边学Android】手机安全卫士04_02:从服务器下载并安装新版本安装包的相关文章

【边做项目边学Android】手机安全卫士01:splash界面ui

手机安全卫士项目是跟着黑马的视频做的. splash是飞洒.飞溅的意思,主要是用于完成一个产品logo显示,期间可以: 后台完成数据库初始化的操作 联网访问服务器,获取服务器最新信息(升级提示) 不同的日期显示出来不同logo,判断当前系统时间,素材一般从服务器上下载下来. 判断时间,根据不同时间显示不同的加载页面 布局文件:splash.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayou

【边做项目边学Android】手机安全卫士03:获取更新的服务器配置,显示更新对话框

配置应用程序在手机桌面显示的名称和图标-AndroidManifest.xml: <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.liuhao.mobilesafe" android:versionCode="

【边做项目边学Android】手机安全卫士04_01:界面(Activity)之间的切换,Activity和任务栈

上一回说到,用户选择是否升级,若用户选择不升级,那么就要进入程序的主界面.下面要做的是从splash界面跳转到main界面. MainActivity创建 1.首先新建MainActivity: package com.liuhao.mobilesafe.ui; import com.liuhao.mobilesafe.R; import android.app.Activity; import android.os.Bundle; public class MainActivity exten

【边做项目边学Android】手机安全卫士09-手机防盗界面设置向导1

本次主要做手机防盗界面的设置向导功能界面的设计. 需求: 当用户进入手机防盗界面时,判断用户是否已经进行过设置向导: 如果用户已经设置过手机防盗,则不再提示用户进入手机向导 若还没有设置,则提示用户进入设置向导界面. 具体实现: 1.当用户输入"手机防盗"密码正确时,进行判断用户是否进行过设置向导 /** * 判断用户是否进行过设置向导 * @return */ private boolean isSetup(){ return sp.getBoolean("isAlread

【边做项目边学Android】手机安全卫士10-设置向导之绑定SIM卡

上回主要做了设置向导界面的界面设计,主要涉及到界面的布局和一些控件的使用.这次要做设置向导界面的功能具体实现. 首先,4个界面分别是(重复度很大,这里就不再贴到正文中了) /mobilesafe/res/layout/setup_wizard1.xml /mobilesafe/res/layout/setup_wizard2.xml /mobilesafe/res/layout/setup_wizard3.xml /mobilesafe/res/layout/setup_wizard4.xml

【边做项目边学Android】手机安全卫士05_1:程序主界面

主界面布局(知识点:GridView) mainscreen.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height=

【边做项目边学Android】手机安全卫士07-手机防盗之进入限制

上次写到在进入手机但·防盗界面时需要有密码限制,首先第一次进入时会弹出对话框提示用户设置密码:再次进入时会要求用户输入密码:这次来具体实现上述功能. 首次登录,设置密码 首先,我们的密码是保存在SharePreference中的"password"字段里的,在登录时后台需要校验该字段是否已经设置了密码,若未设置则弹出对话框让用户设置,否则要用户输入密码进入手机防盗界面: 校验是否设置了密码 @Override protected void onCreate(Bundle savedIn

【边做项目边学Android】手机安全卫士08-一些布局和显示的细节:State List

我们注意到有些应用里的按钮在点击时的显示状态和普通状态是不一样的,比如: 普通状态下: 选中状态下: 那这种效果是如何实现的呢?在Android系统中提供给我们一种方便与实现这种功能的方法即:state list drawable. StateListDrawable是在XML中定义的drawable对象,我们可以通过设置不同item下的图片来显示不同状态,这取决于 drawable对象的状态.例如,一个Button控件可以有不同的状态(点击.聚焦等),通过一系列的drawable对象,可以为每

【边做项目边学Android】手机安全卫士11-设置向导之设置安全号码

这次主要实现设置安全号码的功能,即当发现手机SIM卡信息发生改变时,会自动给安全号码发送一条报警短信.主要包括选择联系人功能.涉及到的知识点包括:带返回值的Intent,ListView数据适配器. 选择联系人功能 界面 用ListView来对读取的联系人进行展示 /mobilesafe/res/layout/select_contact.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayou