精华内容
下载资源
问答
  • 为您提供安卓手机安全卫士源码 完整版下载, 安卓手机安全卫士源码基于黑马的手机安全项目源码作者又对功能做出了一些增删基本的功能有手机防盗、通讯黑名单、软件管理、进程管理、流量统计、手机杀毒、缓存清理、...
  • 手机安全卫士的需求分析 & 更新 & 打包

    1.手机防盗需求分析

    首先应用要有一个闪屏页面,闪屏页面(SplashActivity)会有以下的功能:

    • 版本名称的展示
    • 服务端新版本的检测
    • 展示logo

    而手机防盗功能,需要有以下的功能:

    • sim卡绑定:每一款手机都会有相应的卡的序列号(唯一性),一旦替换掉原有电话卡,序列号会发生改变
    • GPS追踪
    • 远程锁屏
    • 数据销毁

    防盗模块大体功能图如下:

    在这里插入图片描述

    2.手机卫士其余模块需求分析

    其他模块包括黑名单管理,软件管理、进程管理、流量统计、手机杀毒、缓存清理、高级工具、设置中心等模块,模块图大概如下所示:

    在这里插入图片描述

    高级工具的功能包括:

    • 归属地查询
    • 短信备份
    • 常用号码查询
    • 程序锁

    3.手机卫士的包名划分

    每一个类的包名,都应该按照相应的需求进行命名,一些常见的划分类型如下所示:

    • 按照组成方式:命名方式为com.itheima.db
    • 按照业务逻辑方式:命名方式为com.icbc.moneycom.icbc.meeting
    • 按照组件划分:主要以四大组件为主,包括Activity、Service、ContentProvider、BroadCastReceiver,命名方式为com.itheima.activitycom.itheima.service

    该工程我们将按组件划分的方式进行项目的实现

    4.SVN提交代码 & 代码的下载

    虽然目前主流的版本管理工具都使用Git,但这里我们尝试使用SVN,来实现代码仓库的托管以及相应的管理

    具体的操作流程可以参考网上,这里不再赘述

    5.SplashActivity布局分析

    1. 为了在后期维护中加上版本号等相关属性名,我们需要在manifest.xml中进行相应的配置,修改androidmanifest.xml,代码如下:
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.mobilesafe"
        android:versionCode="1"
        android:versionName="1.0.0">
        <!-- android:versionCode 本地应用版本号是1,版本号是2,有必要提示用户更新 -->
        <!-- android:versionName 本地应用版本名,假设版本名为1.0.0,它们所代表的意义如下:
                    第一位:项目有重大更新(代码重构,大部分功能添加,界面整体添加)
                    第二位:更新部分功能
                    第三位:一般代表修复原有版本的bug -->
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".activity.SplashActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    
    1. 在项目下新建activity包,存放activity,然后将MainActivity改名为SplashActivity,同时将布局文件修改为activity_splash.xml,作为闪屏页面
    2. 修改activity_splash.xml,使用相对布局,并使用相应的背景图片,代码如下:
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/launcher_bg"
        tools:context=".activity.SplashActivity">
    
        <TextView
            android:id="@+id/tv_version_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="版本名称"/>
    
    </RelativeLayout>
    

    6.文本框阴影效果

    为了美化闪屏页面的显示效果,这里我们进行一些文本框阴影效果的处理,预览图如下所示:

    在这里插入图片描述

    修改activity_splash.xml,给TextView控件添加实现阴影效果的标签(android:shadow),并且添加一个进度条组件,注意调整位置,代码如下:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/launcher_bg"
        tools:context=".activity.SplashActivity">
    
        <!-- android:shadowRadius="5" 表示阴影所在范围   -->
        <TextView
            android:id="@+id/tv_version_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:shadowDx="1"
            android:shadowDy="1"
            android:shadowColor="#f00"
            android:shadowRadius="5"
            android:text="版本名称"/>
    
        <ProgressBar
            android:layout_below="@id/tv_version_name"
            android:layout_centerHorizontal="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    
    </RelativeLayout>
    

    7.Activity去头操作 & 保留高版本主题

    因为SplashActivity是没有标题栏的,所以需要进行相应的设置,实现的方式有很多:

    • 代码中修改
    • manifest中修改
    • style中修改

    由于前两种方式都不灵活,这里采用第三种方式进行修改

    进入@style/Theme.AppCompat.Light.NoActionBar查看源码,我们可以发现该样式中控制标题栏是否显示的语句为:

    <item name="windowNoTitle">true</item>
    

    因为当前Android应用默认使用@style/AppTheme样式,所以只需要进入该样式并加入上面的语句,即可显示相同的效果,代码如下:

    <resources>
    
        <!-- Base application theme. -->
        <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
            <item name="windowNoTitle">true</item>
        </style>
    
    </resources>
    

    这样,就可以实现在AppTheme样式中没有标题栏的效果了。与此同时,也可以保留高版本的样式主题,一举两得

    8.获取版本名称并且展示

    由于版本号是非固定的,而是随着后期开发逐渐变化的,所以想要显示版本号就需要编写相应的逻辑

    1. 修改SplashActivity,新增initUI()方法,作为UI初始化的方法,代码如下:
    package com.example.mobilesafe.activity;
    
    import androidx.appcompat.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    
    public class SplashActivity extends AppCompatActivity {
    
        private TextView tv_version_name;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    }
    
    1. 修改SplashActivity,新增initData()方法,作为数据初始化的方法,并且再新增getVersionName()方法,作为获取版本号名称的逻辑封装,代码如下:
    package com.example.mobilesafe.activity;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    
    public class SplashActivity extends AppCompatActivity {
    
        private TextView tv_version_name;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    }
    
    

    注意:如果使用的是Android Studio作为IDE,这里要修改versionName时光修改manifest.xml时不生效,要同步修改build.gradle中的versionName才生效

    9.构建服务器端json & 无BOM编码

    这一步我们需要搭建服务器,以向服务器获取相应数据并进行更新,更新界面类似如下:

    在这里插入图片描述

    1. 修改SplashActivity,添加getVersionCode(),作为获取本地版本号的方法,放到initData()方法中,代码如下:
    package com.example.mobilesafe.activity;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地版本号
            mLocalVersionCode = getVersionCode();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    }
    
    1. 编写JSON文件,作为提醒客户端进行更新的相应文件,这里使用HiJson来进行相应的编写,注意最后要保存为json格式的不带BOM编码的UTF-8编码格式,因为该格式在Android2.2版本以后会出现解析上的问题,JSON代码如下:
    {
        "versionCode": "2",
        "versionDes": "2.0版本发布了!",
        "versionName": "2.0",
        "downloadUrl": "www.abc.com"
    }
    
    1. 将刚刚编写好的JSON文件放到Tomcat服务器中,这里为了方便测试,直接将该文件(作者这里将该文件起名为update74.json)放到webapps/ROOT目录下即可
    2. 启动Tomcat服务器,直接到bin目录下双击startup.bat,出现以下界面即可说明服务器启动成功,如图所示:

    在这里插入图片描述

    10.请求网络数据 & 测试

    上一节中我们编写了JSON文件,这里需要对该文件进行一个简单的测试,看是否能够调用

    1. 修改SplashActivity,添加checkVersion(),作为获取服务端版本号的方法,放到initData()方法中,注意这里的请求地址要改成你的电脑的IP地址或者Google推荐的10.0.2.2(仅限模拟器访问电脑上的TomCat),而非localhost,代码如下:
    package com.example.mobilesafe.activity;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    import com.example.mobilesafe.utils.StreamUtil;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地(客户端)版本号
            mLocalVersionCode = getVersionCode();
            // 4.获取服务端版本号(客户端发请求,服务端给响应(Json、Xml))
            /*
            Json中内容应该包括:
            1.更新版本的名称 versionName
            2.新版本的描述信息 versionDes
            3.服务器的版本号 versionCode
            4.新版本apk下载地址 downloadUrl
             */
            checkVersion();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 5.获取服务端版本号
         */
        private void checkVersion() {
            new Thread(){
                @Override
                public void run() {
                    // 发送请求,获取数据,参数则为请求json的链接地址
                    try {
                        // 1.封装url地址
                        URL url = new URL("http://10.0.2.2:8080/update74.json");
                        // 2.开启一个链接
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        // 3.设置常见请求参数(请求头)
                        connection.setConnectTimeout(2000); // 请求超时
                        connection.setReadTimeout(2000); // 读取超时
                        //connection.setRequestMethod("GET"); // 请求方式,默认是get请求方式
                        // 4.获取相应码,200为请求成功
                        if (connection.getResponseCode() == 200){
                            // 5.以流的形式将数据获取下来
                            InputStream is = connection.getInputStream();
                            // 6.将流转换成字符串(工具封装类)
                            String json = StreamUtil.streamToString(is);
                        }
                    }catch (MalformedURLException e) {
                        e.printStackTrace();
                    }catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }
    
    1. 在包下新建utils包,并在该包下新建StreamUtil,作为将流转换为字符串的工具类,代码如下:
    package com.example.mobilesafe.utils;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class StreamUtil {
    
        /**
         * 将流转换成字符串
         * @param is 流对象
         * @return 流转换成的字符串,返回null代表异常
         */
        public static String streamToString(InputStream is) {
            // 1.在读取的过程中,将读取的内容存储至缓存中,然后一次性的转换成字符串返回
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // 2.读取流,读到没有为止(循环)
            byte[] buffer = new byte[1024];
            // 3.记录读取内容的临时变量
            int temp = -1;
            try {
                while ((temp = is.read(buffer)) != -1){
                    bos.write(buffer,0,temp);
                }
                // 4.返回读取的数据
                return bos.toString();
            }catch (IOException e){
                e.printStackTrace();
            }finally {
                try {
                    is.close();
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }
    
    1. 由于涉及到网络操作,需要在manifest.xml中声明权限,代码如下:
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.mobilesafe"
        android:versionCode="1"
        android:versionName="1.0.0">
        <!-- android:versionCode 本地应用版本号是1,版本号是2,有必要提示用户更新 -->
        <!-- android:versionName 本地应用版本名,假设版本名为1.0.0,它们所代表的意义如下:
                    第一位:项目有重大更新(代码重构,大部分功能添加,界面整体添加)
                    第二位:更新部分功能
                    第三位:一般代表修复原有版本的bug -->
        
        <!-- 添加网络权限 -->
        <uses-permission android:name="android.permission.INTERNET"/>
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".activity.SplashActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    
    1. 运行应用,进行相应测试(可以使用Log类),输出结果如图所示:

    在这里插入图片描述

    11.json解析过程

    之前我们已经拿到了从流转化为字符串的文本,现在需要将这段文本转换为json格式

    修改SplashActivity,添加json解析的相应逻辑,这里使用JSONObject来解析,建议使用Log类打印日志,观察是否能够解析成功,代码如下:

    package com.example.mobilesafe.activity;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    import com.example.mobilesafe.utils.StreamUtil;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地(客户端)版本号
            mLocalVersionCode = getVersionCode();
            // 4.获取服务端版本号(客户端发请求,服务端给响应(Json、Xml))
            /*
            Json中内容应该包括:
            1.更新版本的名称 versionName
            2.新版本的描述信息 versionDes
            3.服务器的版本号 versionCode
            4.新版本apk下载地址 downloadUrl
             */
            checkVersion();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 5.获取服务端版本号
         */
        private void checkVersion() {
            new Thread(){
                @Override
                public void run() {
                    // 发送请求,获取数据,参数则为请求json的链接地址
                    try {
                        // 1.封装url地址
                        URL url = new URL("http://10.0.2.2:8080/update74.json");
                        // 2.开启一个链接
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        // 3.设置常见请求参数(请求头)
                        connection.setConnectTimeout(2000); // 请求超时
                        connection.setReadTimeout(2000); // 读取超时
                        //connection.setRequestMethod("GET"); // 请求方式,默认是get请求方式
                        // 4.获取相应码,200为请求成功
                        if (connection.getResponseCode() == 200){
                            // 5.以流的形式将数据获取下来
                            InputStream is = connection.getInputStream();
                            // 6.将流转换成字符串(工具封装类)
                            String json = StreamUtil.streamToString(is);
                            // 7.json解析
                            JSONObject jsonObject = new JSONObject(json);
                            String versionName = jsonObject.getString("versionName");
                            String versionDes = jsonObject.getString("versionDes");
                            String versionCode = jsonObject.getString("versionCode");
                            String downloadUrl = jsonObject.getString("downloadUrl");
                        }
                    }catch (MalformedURLException e) {
                        e.printStackTrace();
                    }catch (IOException e) {
                        e.printStackTrace();
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }
    

    12.断点调试json解析错误过程

    如果在解析json数据中,存在一些问题,那么可能会导致json的解析出错,这时候我们就需要通过断点调试的方式来进行排错

    具体排错过程可参考百度,这里不再详述

    13.消息机制 & 发送不同类型的消息

    因为在比对版本号之后需要弹出“更新”的对话框,由于是UI操作,而Android规定不能在子线程更新UI,所以这里我们需要使用消息机制来发送消息,通知主线程进行UI操作

    1. 修改SplashActivity,添加消息机制,并在checkVersion()方法中处理相应逻辑,代码如下:
    package com.example.mobilesafe.activity;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    import com.example.mobilesafe.utils.StreamUtil;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        /**
         * Handler对象
         */
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what){
                    case UPDATE_VERSION:
                        // 1.弹出对话框,提示用户更新
                        break;
                    case ENTER_HOME:
                        // 2.直接进入应用程序主界面
                        break;
                    case URL_ERROR:
                        // 3.弹出URL错误
                        break;
                    case IO_ERROR:
                        // 4.弹出IO错误
                        break;
                    case JSON_ERROR:
                        // 5.弹出JSON错误
                        break;
                    default:break;
                }
            }
        };
    
        /**
         * 更新新版本的状态码
         */
        private static final int UPDATE_VERSION = 100;
    
        /**
         * 进入应用程序主界面的状态码
         */
        private static final int ENTER_HOME = 101;
    
        /**
         * URL地址出错的状态码
         */
        private static final int URL_ERROR = 102;
    
        /**
         * IO操作出错的状态码
         */
        private static final int IO_ERROR = 103;
    
        /**
         * JSON解析出错的状态码
         */
        private static final int JSON_ERROR = 104;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地(客户端)版本号
            mLocalVersionCode = getVersionCode();
            // 4.获取服务端版本号(客户端发请求,服务端给响应(Json、Xml))
            /*
            Json中内容应该包括:
            1.更新版本的名称 versionName
            2.新版本的描述信息 versionDes
            3.服务器的版本号 versionCode
            4.新版本apk下载地址 downloadUrl
             */
            checkVersion();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 5.获取服务端版本号
         */
        private void checkVersion() {
            // 发送请求,获取数据,参数则为请求json的链接地址
            new Thread(){
                @Override
                public void run() {
                    // 0.获取message对象
                    Message msg = Message.obtain();
                    try {
                        // 1.封装url地址
                        URL url = new URL("http://10.0.2.2:8080/update74.json");
                        // 2.开启一个链接
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        // 3.设置常见请求参数(请求头)
                        connection.setConnectTimeout(2000); // 请求超时
                        connection.setReadTimeout(2000); // 读取超时
                        //connection.setRequestMethod("GET"); // 请求方式,默认是get请求方式
                        // 4.获取相应码,200为请求成功
                        if (connection.getResponseCode() == 200){
                            // 5.以流的形式将数据获取下来
                            InputStream is = connection.getInputStream();
                            // 6.将流转换成字符串(工具封装类)
                            String json = StreamUtil.streamToString(is);
                            // 7.json解析
                            JSONObject jsonObject = new JSONObject(json);
                            String versionName = jsonObject.getString("versionName");
                            String versionDes = jsonObject.getString("versionDes");
                            String versionCode = jsonObject.getString("versionCode");
                            String downloadUrl = jsonObject.getString("downloadUrl");
                            // 8.比对版本号(服务器版本号 > 本地版本号,提示用户更新)
                            if (Integer.parseInt(versionCode) > mLocalVersionCode){
                                // 9.提示用户更新,弹出对话框(UI),需要使用到消息机制
                                msg.what = UPDATE_VERSION;
                            }else {
                                // 10.不需要更新,直接进入应用程序主界面
                                msg.what = ENTER_HOME;
                            }
                        }
                    }catch (MalformedURLException e) {
                        e.printStackTrace();
                        msg.what = URL_ERROR;
                    }catch (IOException e) {
                        e.printStackTrace();
                        msg.what = IO_ERROR;
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                        msg.what = JSON_ERROR;
                    }finally {
                        // 11.发送消息
                        mHandler.sendMessage(msg);
                    }
                }
            }.start();
        }
    }
    
    1. 修改SplashActivity,添加enterHome(),作为进入应用程序主界面的方法,同时在activity包下创建HomeActivity,作为主界面,暂时套用EmptyAcivity模板即可,代码如下:
    package com.example.mobilesafe.activity;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.Intent;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    import com.example.mobilesafe.utils.StreamUtil;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        /**
         * Handler对象
         */
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what){
                    case UPDATE_VERSION:
                        // 1.弹出对话框,提示用户更新
                        break;
                    case ENTER_HOME:
                        // 2.直接进入应用程序主界面
                        enterHome();
                        break;
                    case URL_ERROR:
                        // 3.弹出URL错误
                        break;
                    case IO_ERROR:
                        // 4.弹出IO错误
                        break;
                    case JSON_ERROR:
                        // 5.弹出JSON错误
                        break;
                    default:break;
                }
            }
        };
    
        /**
         * 更新新版本的状态码
         */
        private static final int UPDATE_VERSION = 100;
    
        /**
         * 进入应用程序主界面的状态码
         */
        private static final int ENTER_HOME = 101;
    
        /**
         * URL地址出错的状态码
         */
        private static final int URL_ERROR = 102;
    
        /**
         * IO操作出错的状态码
         */
        private static final int IO_ERROR = 103;
    
        /**
         * JSON解析出错的状态码
         */
        private static final int JSON_ERROR = 104;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地(客户端)版本号
            mLocalVersionCode = getVersionCode();
            // 4.获取服务端版本号(客户端发请求,服务端给响应(Json、Xml))
            /*
            Json中内容应该包括:
            1.更新版本的名称 versionName
            2.新版本的描述信息 versionDes
            3.服务器的版本号 versionCode
            4.新版本apk下载地址 downloadUrl
             */
            checkVersion();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 5.获取服务端版本号
         */
        private void checkVersion() {
            // 发送请求,获取数据,参数则为请求json的链接地址
            new Thread(){
                @Override
                public void run() {
                    // 0.获取message对象
                    Message msg = Message.obtain();
                    try {
                        // 1.封装url地址
                        URL url = new URL("http://10.0.2.2:8080/update74.json");
                        // 2.开启一个链接
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        // 3.设置常见请求参数(请求头)
                        connection.setConnectTimeout(2000); // 请求超时
                        connection.setReadTimeout(2000); // 读取超时
                        //connection.setRequestMethod("GET"); // 请求方式,默认是get请求方式
                        // 4.获取相应码,200为请求成功
                        if (connection.getResponseCode() == 200){
                            // 5.以流的形式将数据获取下来
                            InputStream is = connection.getInputStream();
                            // 6.将流转换成字符串(工具封装类)
                            String json = StreamUtil.streamToString(is);
                            // 7.json解析
                            JSONObject jsonObject = new JSONObject(json);
                            String versionName = jsonObject.getString("versionName");
                            String versionDes = jsonObject.getString("versionDes");
                            String versionCode = jsonObject.getString("versionCode");
                            String downloadUrl = jsonObject.getString("downloadUrl");
                            // 8.比对版本号(服务器版本号 > 本地版本号,提示用户更新)
                            if (Integer.parseInt(versionCode) > mLocalVersionCode){
                                // 9.提示用户更新,弹出对话框(UI),需要使用到消息机制
                                msg.what = UPDATE_VERSION;
                            }else {
                                // 10.不需要更新,直接进入应用程序主界面
                                msg.what = ENTER_HOME;
                            }
                        }
                    }catch (MalformedURLException e) {
                        e.printStackTrace();
                        msg.what = URL_ERROR;
                    }catch (IOException e) {
                        e.printStackTrace();
                        msg.what = IO_ERROR;
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                        msg.what = JSON_ERROR;
                    }finally {
                        // 11.发送消息
                        mHandler.sendMessage(msg);
                    }
                }
            }.start();
        }
    
        /**
         * 6.进入应用程序的主界面
         */
        private void enterHome() {
            Intent intent = new Intent(this, HomeActivity.class);
            startActivity(intent);
            finish(); // 开启新界面后,将导航界面销毁掉
        }
    }
    
    1. 修改SplashActivity,修改checkVersion()方法,为了优化视觉效果,将引导页面跳转到主页面的时间设置为4秒,代码如下:
    package com.example.mobilesafe.activity;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.Intent;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    import com.example.mobilesafe.utils.StreamUtil;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        /**
         * Handler对象
         */
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what){
                    case UPDATE_VERSION:
                        // 1.弹出对话框,提示用户更新
                        break;
                    case ENTER_HOME:
                        // 2.直接进入应用程序主界面
                        enterHome();
                        break;
                    case URL_ERROR:
                        // 3.弹出URL错误
                        break;
                    case IO_ERROR:
                        // 4.弹出IO错误
                        break;
                    case JSON_ERROR:
                        // 5.弹出JSON错误
                        break;
                    default:break;
                }
            }
        };
    
        /**
         * 更新新版本的状态码
         */
        private static final int UPDATE_VERSION = 100;
    
        /**
         * 进入应用程序主界面的状态码
         */
        private static final int ENTER_HOME = 101;
    
        /**
         * URL地址出错的状态码
         */
        private static final int URL_ERROR = 102;
    
        /**
         * IO操作出错的状态码
         */
        private static final int IO_ERROR = 103;
    
        /**
         * JSON解析出错的状态码
         */
        private static final int JSON_ERROR = 104;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地(客户端)版本号
            mLocalVersionCode = getVersionCode();
            // 4.获取服务端版本号(客户端发请求,服务端给响应(Json、Xml))
            /*
            Json中内容应该包括:
            1.更新版本的名称 versionName
            2.新版本的描述信息 versionDes
            3.服务器的版本号 versionCode
            4.新版本apk下载地址 downloadUrl
             */
            checkVersion();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 5.获取服务端版本号
         */
        private void checkVersion() {
            // 发送请求,获取数据,参数则为请求json的链接地址
            new Thread(){
                @Override
                public void run() {
                    // 0.获取message对象
                    Message msg = Message.obtain();
                    long startTime = System.currentTimeMillis();// 获取时间戳
                    try {
                        // 1.封装url地址
                        URL url = new URL("http://10.0.2.2:8080/update74.json");
                        // 2.开启一个链接
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        // 3.设置常见请求参数(请求头)
                        connection.setConnectTimeout(2000); // 请求超时
                        connection.setReadTimeout(2000); // 读取超时
                        //connection.setRequestMethod("GET"); // 请求方式,默认是get请求方式
                        // 4.获取相应码,200为请求成功
                        if (connection.getResponseCode() == 200){
                            // 5.以流的形式将数据获取下来
                            InputStream is = connection.getInputStream();
                            // 6.将流转换成字符串(工具封装类)
                            String json = StreamUtil.streamToString(is);
                            // 7.json解析
                            JSONObject jsonObject = new JSONObject(json);
                            String versionName = jsonObject.getString("versionName");
                            String versionDes = jsonObject.getString("versionDes");
                            String versionCode = jsonObject.getString("versionCode");
                            String downloadUrl = jsonObject.getString("downloadUrl");
                            // 8.比对版本号(服务器版本号 > 本地版本号,提示用户更新)
                            if (Integer.parseInt(versionCode) > mLocalVersionCode){
                                // 9.提示用户更新,弹出对话框(UI),需要使用到消息机制
                                msg.what = UPDATE_VERSION;
                            }else {
                                // 10.不需要更新,直接进入应用程序主界面
                                msg.what = ENTER_HOME;
                            }
                        }
                    }catch (MalformedURLException e) {
                        e.printStackTrace();
                        msg.what = URL_ERROR;
                    }catch (IOException e) {
                        e.printStackTrace();
                        msg.what = IO_ERROR;
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                        msg.what = JSON_ERROR;
                    }finally {
                        // 11.指定睡眠时间,请求网络的时长超过4秒则不做处理,若小于4秒,则强制让其睡眠满4秒
                        long endTime = System.currentTimeMillis();
                        if (endTime - startTime < 4000){
                            try {
                                Thread.sleep(4000 - (endTime - startTime));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        // 12.发送消息
                        mHandler.sendMessage(msg);
                    }
                }
            }.start();
        }
    
        /**
         * 6.进入应用程序的主界面
         */
        private void enterHome() {
            Intent intent = new Intent(this, HomeActivity.class);
            startActivity(intent);
            finish(); // 开启新界面后,将导航界面销毁掉
        }
    }
    

    14.弹出对话框

    上面我们增加了相应的消息机制,这一节来继续完善机制,并添加相应的UI操作

    1. 在utils包下新建ToastUtil,作为弹出对话框的工具类,方便之后调用,代码如下:
    package com.example.mobilesafe.utils;
    
    import android.content.Context;
    import android.widget.Toast;
    
    public class ToastUtil {
    
        /**
         * Toast打印
         * @param ctx 上下文
         * @param msg 打印文本内容
         */
        public static void show(Context ctx,String msg){
            Toast.makeText(ctx, msg,Toast.LENGTH_SHORT).show();
        }
    }
    
    
    1. 修改SplashActivity,在Handler中添加每个分支的处理逻辑,使用刚刚编写好的工具类,代码如下:
    package com.example.mobilesafe.activity;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.Intent;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.example.mobilesafe.R;
    import com.example.mobilesafe.utils.StreamUtil;
    import com.example.mobilesafe.utils.ToastUtil;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        /**
         * Handler对象
         */
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what){
                    case UPDATE_VERSION:
                        // 1.弹出对话框,提示用户更新
                        break;
                    case ENTER_HOME:
                        // 2.直接进入应用程序主界面
                        enterHome();
                        break;
                    case URL_ERROR:
                        // 3.弹出URL错误
                        ToastUtil.show(SplashActivity.this,"url异常");
                        enterHome();
                        break;
                    case IO_ERROR:
                        // 4.弹出IO错误
                        ToastUtil.show(SplashActivity.this,"IO异常");
                        enterHome();
                        break;
                    case JSON_ERROR:
                        // 5.弹出JSON错误
                        ToastUtil.show(SplashActivity.this,"json异常");
                        enterHome();
                        break;
                    default:break;
                }
            }
        };
    
        /**
         * 更新新版本的状态码
         */
        private static final int UPDATE_VERSION = 100;
    
        /**
         * 进入应用程序主界面的状态码
         */
        private static final int ENTER_HOME = 101;
    
        /**
         * URL地址出错的状态码
         */
        private static final int URL_ERROR = 102;
    
        /**
         * IO操作出错的状态码
         */
        private static final int IO_ERROR = 103;
    
        /**
         * JSON解析出错的状态码
         */
        private static final int JSON_ERROR = 104;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地(客户端)版本号
            mLocalVersionCode = getVersionCode();
            // 4.获取服务端版本号(客户端发请求,服务端给响应(Json、Xml))
            /*
            Json中内容应该包括:
                1.更新版本的名称 versionName
                2.新版本的描述信息 versionDes
                3.服务器的版本号 versionCode
                4.新版本apk下载地址 downloadUrl
             */
            checkVersion();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 5.获取服务端版本号
         */
        private void checkVersion() {
            // 发送请求,获取数据,参数则为请求json的链接地址
            new Thread(){
                @Override
                public void run() {
                    // 0.获取message对象
                    Message msg = Message.obtain();
                    long startTime = System.currentTimeMillis();// 获取时间戳
                    try {
                        // 1.封装url地址
                        URL url = new URL("http://10.0.2.2:8080/update74.json");
                        // 2.开启一个链接
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        // 3.设置常见请求参数(请求头)
                        connection.setConnectTimeout(2000); // 请求超时
                        connection.setReadTimeout(2000); // 读取超时
                        //connection.setRequestMethod("GET"); // 请求方式,默认是get请求方式
                        // 4.获取相应码,200为请求成功
                        if (connection.getResponseCode() == 200){
                            // 5.以流的形式将数据获取下来
                            InputStream is = connection.getInputStream();
                            // 6.将流转换成字符串(工具封装类)
                            String json = StreamUtil.streamToString(is);
                            // 7.json解析
                            JSONObject jsonObject = new JSONObject(json);
                            String versionName = jsonObject.getString("versionName");
                            String versionDes = jsonObject.getString("versionDes");
                            String versionCode = jsonObject.getString("versionCode");
                            String downloadUrl = jsonObject.getString("downloadUrl");
                            // 8.比对版本号(服务器版本号 > 本地版本号,提示用户更新)
                            if (Integer.parseInt(versionCode) > mLocalVersionCode){
                                // 9.提示用户更新,弹出对话框(UI),需要使用到消息机制
                                msg.what = UPDATE_VERSION;
                            }else {
                                // 10.不需要更新,直接进入应用程序主界面
                                msg.what = ENTER_HOME;
                            }
                        }
                    }catch (MalformedURLException e) {
                        e.printStackTrace();
                        msg.what = URL_ERROR;
                    }catch (IOException e) {
                        e.printStackTrace();
                        msg.what = IO_ERROR;
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                        msg.what = JSON_ERROR;
                    }finally {
                        // 11.指定睡眠时间,请求网络的时长超过4秒则不做处理,若小于4秒,则强制让其睡眠满4秒
                        long endTime = System.currentTimeMillis();
                        if (endTime - startTime < 4000){
                            try {
                                Thread.sleep(4000 - (endTime - startTime));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        // 12.发送消息
                        mHandler.sendMessage(msg);
                    }
                }
            }.start();
        }
    
        /**
         * 6.进入应用程序的主界面
         */
        private void enterHome() {
            Intent intent = new Intent(this, HomeActivity.class);
            startActivity(intent);
            finish(); // 开启新界面后,将导航界面销毁掉
        }
    }
    
    1. 修改SplashActivity,添加showUpdateDialog(),作为显示更新对话框的方法,代码如下:
    package com.example.mobilesafe.activity;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AlertDialog;
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    import com.example.mobilesafe.utils.StreamUtil;
    import com.example.mobilesafe.utils.ToastUtil;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        /**
         * 更新时描述信息
         */
        private String mVersionDes;
    
        /**
         * Handler对象
         */
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what){
                    case UPDATE_VERSION:
                        // 1.弹出对话框,提示用户更新
                        showUpdateDialog();
                        break;
                    case ENTER_HOME:
                        // 2.直接进入应用程序主界面
                        enterHome();
                        break;
                    case URL_ERROR:
                        // 3.弹出URL错误
                        ToastUtil.show(SplashActivity.this,"url异常");
                        enterHome();
                        break;
                    case IO_ERROR:
                        // 4.弹出IO错误
                        ToastUtil.show(SplashActivity.this,"IO异常");
                        enterHome();
                        break;
                    case JSON_ERROR:
                        // 5.弹出JSON错误
                        ToastUtil.show(SplashActivity.this,"json异常");
                        enterHome();
                        break;
                    default:break;
                }
            }
        };
    
        /**
         * 更新新版本的状态码
         */
        private static final int UPDATE_VERSION = 100;
    
        /**
         * 进入应用程序主界面的状态码
         */
        private static final int ENTER_HOME = 101;
    
        /**
         * URL地址出错的状态码
         */
        private static final int URL_ERROR = 102;
    
        /**
         * IO操作出错的状态码
         */
        private static final int IO_ERROR = 103;
    
        /**
         * JSON解析出错的状态码
         */
        private static final int JSON_ERROR = 104;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地(客户端)版本号
            mLocalVersionCode = getVersionCode();
            // 4.获取服务端版本号(客户端发请求,服务端给响应(Json、Xml))
            /*
            Json中内容应该包括:
                1.更新版本的名称 versionName
                2.新版本的描述信息 versionDes
                3.服务器的版本号 versionCode
                4.新版本apk下载地址 downloadUrl
             */
            checkVersion();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 5.获取服务端版本号
         */
        private void checkVersion() {
            // 发送请求,获取数据,参数则为请求json的链接地址
            new Thread(){
                @Override
                public void run() {
                    // 0.获取message对象
                    Message msg = Message.obtain();
                    long startTime = System.currentTimeMillis();// 获取时间戳
                    try {
                        // 1.封装url地址
                        URL url = new URL("http://10.0.2.2:8080/update74.json");
                        // 2.开启一个链接
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        // 3.设置常见请求参数(请求头)
                        connection.setConnectTimeout(2000); // 请求超时
                        connection.setReadTimeout(2000); // 读取超时
                        //connection.setRequestMethod("GET"); // 请求方式,默认是get请求方式
                        // 4.获取相应码,200为请求成功
                        if (connection.getResponseCode() == 200){
                            // 5.以流的形式将数据获取下来
                            InputStream is = connection.getInputStream();
                            // 6.将流转换成字符串(工具封装类)
                            String json = StreamUtil.streamToString(is);
                            // 7.json解析
                            JSONObject jsonObject = new JSONObject(json);
                            String versionName = jsonObject.getString("versionName");
                            mVersionDes = jsonObject.getString("versionDes");
                            String versionCode = jsonObject.getString("versionCode");
                            String downloadUrl = jsonObject.getString("downloadUrl");
                            // 8.比对版本号(服务器版本号 > 本地版本号,提示用户更新)
                            if (Integer.parseInt(versionCode) > mLocalVersionCode){
                                // 9.提示用户更新,弹出对话框(UI),需要使用到消息机制
                                msg.what = UPDATE_VERSION;
                            }else {
                                // 10.不需要更新,直接进入应用程序主界面
                                msg.what = ENTER_HOME;
                            }
                        }
                    }catch (MalformedURLException e) {
                        e.printStackTrace();
                        msg.what = URL_ERROR;
                    }catch (IOException e) {
                        e.printStackTrace();
                        msg.what = IO_ERROR;
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                        msg.what = JSON_ERROR;
                    }finally {
                        // 11.指定睡眠时间,请求网络的时长超过4秒则不做处理,若小于4秒,则强制让其睡眠满4秒
                        long endTime = System.currentTimeMillis();
                        if (endTime - startTime < 4000){
                            try {
                                Thread.sleep(4000 - (endTime - startTime));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        // 12.发送消息
                        mHandler.sendMessage(msg);
                    }
                }
            }.start();
        }
    
        /**
         * 6.进入应用程序的主界面
         */
        private void enterHome() {
            Intent intent = new Intent(this, HomeActivity.class);
            startActivity(intent);
            finish(); // 开启新界面后,将导航界面销毁掉
        }
    
        /**
         * 7.弹出更新对话框
         */
        private void showUpdateDialog() {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setIcon(R.drawable.ic_launcher); // 设置左上角图标
            builder.setTitle("版本更新"); // 设置标题
            builder.setMessage(mVersionDes); // 设置描述内容
            builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // 下载apk,需要apk的链接地址,即downloadUrl
                }
            });// 积极按钮,“是”
            builder.setNegativeButton("稍后再说", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // 取消对话框,进入主界面
                    enterHome();
                }
            });// 消极按钮,“否”
            builder.show();
        }
    }
    

    15.xUtils说明 & 下载方法使用

    完善了消息机制后,接下来我们就需要处理“下载”逻辑的相关代码,这里的下载逻辑使用“多线程下载”,即将某个文件划分成3份,每一份都有相应的线程去做下载,记录当前的下载位置,下一次就在当前记录的下载位置继续下载

    为了便于下载逻辑的实现,这里还使用到一个框架:xUtils,这是aFinal框架的前身,感兴趣的读者可以去百度查阅

    xUtils的集成只需要直接导入jar包,并且添加相应权限即可,这里不再详述

    修改SplashActivity,修改showUpdateDialog()方法,新建downloadApk()方法来完善下载逻辑,代码如下:

    package com.example.mobilesafe.activity;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AlertDialog;
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    import com.example.mobilesafe.utils.StreamUtil;
    import com.example.mobilesafe.utils.ToastUtil;
    import com.lidroid.xutils.HttpUtils;
    import com.lidroid.xutils.exception.HttpException;
    import com.lidroid.xutils.http.ResponseInfo;
    import com.lidroid.xutils.http.callback.RequestCallBack;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        /**
         * 更新时描述信息
         */
        private String mVersionDes;
    
        /**
         * 更新时的URL
         */
        private String mDownloadUrl;
    
        private static final String tag = "SplashActivity";
    
        /**
         * Handler对象
         */
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what){
                    case UPDATE_VERSION:
                        // 1.弹出对话框,提示用户更新
                        showUpdateDialog();
                        break;
                    case ENTER_HOME:
                        // 2.直接进入应用程序主界面
                        enterHome();
                        break;
                    case URL_ERROR:
                        // 3.弹出URL错误
                        ToastUtil.show(SplashActivity.this,"url异常");
                        enterHome();
                        break;
                    case IO_ERROR:
                        // 4.弹出IO错误
                        ToastUtil.show(SplashActivity.this,"IO异常");
                        enterHome();
                        break;
                    case JSON_ERROR:
                        // 5.弹出JSON错误
                        ToastUtil.show(SplashActivity.this,"json异常");
                        enterHome();
                        break;
                    default:break;
                }
            }
        };
    
        /**
         * 更新新版本的状态码
         */
        private static final int UPDATE_VERSION = 100;
    
        /**
         * 进入应用程序主界面的状态码
         */
        private static final int ENTER_HOME = 101;
    
        /**
         * URL地址出错的状态码
         */
        private static final int URL_ERROR = 102;
    
        /**
         * IO操作出错的状态码
         */
        private static final int IO_ERROR = 103;
    
        /**
         * JSON解析出错的状态码
         */
        private static final int JSON_ERROR = 104;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地(客户端)版本号
            mLocalVersionCode = getVersionCode();
            // 4.获取服务端版本号(客户端发请求,服务端给响应(Json、Xml))
            /*
            Json中内容应该包括:
                1.更新版本的名称 versionName
                2.新版本的描述信息 versionDes
                3.服务器的版本号 versionCode
                4.新版本apk下载地址 downloadUrl
             */
            checkVersion();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 5.获取服务端版本号
         */
        private void checkVersion() {
            // 发送请求,获取数据,参数则为请求json的链接地址
            new Thread(){
                @Override
                public void run() {
                    // 0.获取message对象
                    Message msg = Message.obtain();
                    long startTime = System.currentTimeMillis();// 获取时间戳
                    try {
                        // 1.封装url地址
                        URL url = new URL("http://10.0.2.2:8080/update74.json");
                        // 2.开启一个链接
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        // 3.设置常见请求参数(请求头)
                        connection.setConnectTimeout(2000); // 请求超时
                        connection.setReadTimeout(2000); // 读取超时
                        //connection.setRequestMethod("GET"); // 请求方式,默认是get请求方式
                        // 4.获取相应码,200为请求成功
                        if (connection.getResponseCode() == 200){
                            // 5.以流的形式将数据获取下来
                            InputStream is = connection.getInputStream();
                            // 6.将流转换成字符串(工具封装类)
                            String json = StreamUtil.streamToString(is);
                            // 7.json解析
                            JSONObject jsonObject = new JSONObject(json);
                            String versionName = jsonObject.getString("versionName");
                            mVersionDes = jsonObject.getString("versionDes");
                            String versionCode = jsonObject.getString("versionCode");
                            mDownloadUrl = jsonObject.getString("downloadUrl");
                            // 8.比对版本号(服务器版本号 > 本地版本号,提示用户更新)
                            if (Integer.parseInt(versionCode) > mLocalVersionCode){
                                // 9.提示用户更新,弹出对话框(UI),需要使用到消息机制
                                msg.what = UPDATE_VERSION;
                            }else {
                                // 10.不需要更新,直接进入应用程序主界面
                                msg.what = ENTER_HOME;
                            }
                        }
                    }catch (MalformedURLException e) {
                        e.printStackTrace();
                        msg.what = URL_ERROR;
                    }catch (IOException e) {
                        e.printStackTrace();
                        msg.what = IO_ERROR;
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                        msg.what = JSON_ERROR;
                    }finally {
                        // 11.指定睡眠时间,请求网络的时长超过4秒则不做处理,若小于4秒,则强制让其睡眠满4秒
                        long endTime = System.currentTimeMillis();
                        if (endTime - startTime < 4000){
                            try {
                                Thread.sleep(4000 - (endTime - startTime));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        // 12.发送消息
                        mHandler.sendMessage(msg);
                    }
                }
            }.start();
        }
    
        /**
         * 6.进入应用程序的主界面
         */
        private void enterHome() {
            Intent intent = new Intent(this, HomeActivity.class);
            startActivity(intent);
            finish(); // 开启新界面后,将导航界面销毁掉
        }
    
        /**
         * 7.弹出更新对话框
         */
        private void showUpdateDialog() {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setIcon(R.drawable.ic_launcher); // 设置左上角图标
            builder.setTitle("版本更新"); // 设置标题
            builder.setMessage(mVersionDes); // 设置描述内容
            builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // 下载apk,需要apk的链接地址,即downloadUrl
                    downloadApk();
                }
            });// 积极按钮,“是”
            builder.setNegativeButton("稍后再说", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // 取消对话框,进入主界面
                    enterHome();
                }
            });// 消极按钮,“否”
            builder.show();
        }
    
        /**
         * 8.APK下载
         */
        private void downloadApk() {
            // 需要apk下载链接地址,放置apk的所在路径
            // 1.判断sd卡是否可用,是否挂载
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                // 2.获取sd卡路径
                String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "mobilesafe74.apk";
                // 3.发送请求,获取Apk,并且放置到指定路径(下载地址,下载应用的放置位置,)
                HttpUtils httpUtils = new HttpUtils();
                httpUtils.download(mDownloadUrl, path, new RequestCallBack<File>() {
                    @Override
                    public void onSuccess(ResponseInfo<File> responseInfo) {
                        // 下载成功(下载过后的放置在sd卡中apk)
                        Log.i(tag,"下载成功!");
                        File file = responseInfo.result;
                    }
    
                    @Override
                    public void onFailure(HttpException e, String s) {
                        // 下载失败
                        Log.i(tag,"下载失败!");
                    }
    
                    @Override
                    public void onStart() {
                        // 刚刚开始下载
                        Log.i(tag,"刚刚开始下载!");
                        super.onStart();
                    }
    
                    @Override
                    public void onLoading(long total, long current, boolean isUploading) {
                        // 下载过程(下载文件大小,当前的下载位置,是否正在下载)
                        Log.i(tag,"下载中......文件大小为" + total + "当前的下载位置为" + current);
                        super.onLoading(total, current, isUploading);
                    }
                });
            }
        }
    }
    

    16.打包生成apk & 维护到服务器

    下载逻辑完善的同时,我们需要将该(高版本)的应用打包成apk,并且维护到服务器上

    打包的过程可以参考百度,这里仅作简单陈述:

    1. 生成签名文件,并且指定所在位置
    2. 使用生成的签名文件,给工程打包生成一个apk

    打包成功后,只需要放置到跟之前的json文件同一个tomcat中的目录,即可将apk文件维护到服务器上

    随后,修改json文件,将路径改成如图所示(apk文件因人而异,最好配置完成后可以去网页上测试一下,看能否访问到这个文件),运行文件,进行相应测试:

    在这里插入图片描述

    17.签名文件说明 & 包名说明

    下载完成后,就应该提示用户安装这个apk,但是在安装后会提示“已安装了存在签名冲突的同名数据包”,所以我们需要分析签名文件和包名

    1. 修改SplashActivity,修改downloadApk()方法,新建installApk()方法来完善安装apk逻辑(使用隐式意图来启动系统的apk安装界面),代码如下:
    package com.example.mobilesafe.activity;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AlertDialog;
    import androidx.appcompat.app.AppCompatActivity;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    import com.example.mobilesafe.utils.StreamUtil;
    import com.example.mobilesafe.utils.ToastUtil;
    import com.lidroid.xutils.HttpUtils;
    import com.lidroid.xutils.exception.HttpException;
    import com.lidroid.xutils.http.ResponseInfo;
    import com.lidroid.xutils.http.callback.RequestCallBack;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        /**
         * 更新时描述信息
         */
        private String mVersionDes;
    
        /**
         * 更新时的URL
         */
        private String mDownloadUrl;
    
        private static final String tag = "SplashActivity";
    
        /**
         * Handler对象
         */
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what){
                    case UPDATE_VERSION:
                        // 1.弹出对话框,提示用户更新
                        showUpdateDialog();
                        break;
                    case ENTER_HOME:
                        // 2.直接进入应用程序主界面
                        enterHome();
                        break;
                    case URL_ERROR:
                        // 3.弹出URL错误
                        ToastUtil.show(SplashActivity.this,"url异常");
                        enterHome();
                        break;
                    case IO_ERROR:
                        // 4.弹出IO错误
                        ToastUtil.show(SplashActivity.this,"IO异常");
                        enterHome();
                        break;
                    case JSON_ERROR:
                        // 5.弹出JSON错误
                        ToastUtil.show(SplashActivity.this,"json异常");
                        enterHome();
                        break;
                    default:break;
                }
            }
        };
    
        /**
         * 更新新版本的状态码
         */
        private static final int UPDATE_VERSION = 100;
    
        /**
         * 进入应用程序主界面的状态码
         */
        private static final int ENTER_HOME = 101;
    
        /**
         * URL地址出错的状态码
         */
        private static final int URL_ERROR = 102;
    
        /**
         * IO操作出错的状态码
         */
        private static final int IO_ERROR = 103;
    
        /**
         * JSON解析出错的状态码
         */
        private static final int JSON_ERROR = 104;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地(客户端)版本号
            mLocalVersionCode = getVersionCode();
            // 4.获取服务端版本号(客户端发请求,服务端给响应(Json、Xml))
            /*
            Json中内容应该包括:
                1.更新版本的名称 versionName
                2.新版本的描述信息 versionDes
                3.服务器的版本号 versionCode
                4.新版本apk下载地址 downloadUrl
             */
            checkVersion();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 5.获取服务端版本号
         */
        private void checkVersion() {
            // 发送请求,获取数据,参数则为请求json的链接地址
            new Thread(){
                @Override
                public void run() {
                    // 0.获取message对象
                    Message msg = Message.obtain();
                    long startTime = System.currentTimeMillis();// 获取时间戳
                    try {
                        // 1.封装url地址
                        URL url = new URL("http://10.0.2.2:8080/update74.json");
                        // 2.开启一个链接
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        // 3.设置常见请求参数(请求头)
                        connection.setConnectTimeout(2000); // 请求超时
                        connection.setReadTimeout(2000); // 读取超时
                        //connection.setRequestMethod("GET"); // 请求方式,默认是get请求方式
                        // 4.获取相应码,200为请求成功
                        if (connection.getResponseCode() == 200){
                            // 5.以流的形式将数据获取下来
                            InputStream is = connection.getInputStream();
                            // 6.将流转换成字符串(工具封装类)
                            String json = StreamUtil.streamToString(is);
                            // 7.json解析
                            JSONObject jsonObject = new JSONObject(json);
                            String versionName = jsonObject.getString("versionName");
                            mVersionDes = jsonObject.getString("versionDes");
                            String versionCode = jsonObject.getString("versionCode");
                            mDownloadUrl = jsonObject.getString("downloadUrl");
                            // 8.比对版本号(服务器版本号 > 本地版本号,提示用户更新)
                            if (Integer.parseInt(versionCode) > mLocalVersionCode){
                                // 9.提示用户更新,弹出对话框(UI),需要使用到消息机制
                                msg.what = UPDATE_VERSION;
                            }else {
                                // 10.不需要更新,直接进入应用程序主界面
                                msg.what = ENTER_HOME;
                            }
                        }
                    }catch (MalformedURLException e) {
                        e.printStackTrace();
                        msg.what = URL_ERROR;
                    }catch (IOException e) {
                        e.printStackTrace();
                        msg.what = IO_ERROR;
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                        msg.what = JSON_ERROR;
                    }finally {
                        // 11.指定睡眠时间,请求网络的时长超过4秒则不做处理,若小于4秒,则强制让其睡眠满4秒
                        long endTime = System.currentTimeMillis();
                        if (endTime - startTime < 4000){
                            try {
                                Thread.sleep(4000 - (endTime - startTime));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        // 12.发送消息
                        mHandler.sendMessage(msg);
                    }
                }
            }.start();
        }
    
        /**
         * 6.进入应用程序的主界面
         */
        private void enterHome() {
            Intent intent = new Intent(this, HomeActivity.class);
            startActivity(intent);
            finish(); // 开启新界面后,将导航界面销毁掉
        }
    
        /**
         * 7.弹出更新对话框
         */
        private void showUpdateDialog() {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setIcon(R.drawable.ic_launcher); // 设置左上角图标
            builder.setTitle("版本更新"); // 设置标题
            builder.setMessage(mVersionDes); // 设置描述内容
            builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // 下载apk,需要apk的链接地址,即downloadUrl
                    downloadApk();
                }
            });// 积极按钮,“是”
            builder.setNegativeButton("稍后再说", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // 取消对话框,进入主界面
                    enterHome();
                }
            });// 消极按钮,“否”
            builder.show();
        }
    
        /**
         * 8.APK下载
         */
        private void downloadApk() {
            // 需要apk下载链接地址,放置apk的所在路径
            // 1.判断sd卡是否可用,是否挂载
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                // 2.获取sd卡路径
                String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "app-release.apk";
                Log.i(tag,"路径为:" + path);
                // 3.发送请求,获取Apk,并且放置到指定路径(下载地址,下载应用的放置位置,回调方法)
                HttpUtils httpUtils = new HttpUtils();
                httpUtils.download(mDownloadUrl, path, new RequestCallBack<File>() {
                    @Override
                    public void onSuccess(ResponseInfo<File> responseInfo) {
                        // 下载成功(下载过后的放置在sd卡中apk)
                        Log.i(tag,"下载成功!");
                        File file = responseInfo.result;
                        installApk(file);
                    }
    
                    @Override
                    public void onFailure(HttpException e, String s) {
                        // 下载失败
                        Log.i(tag,"下载失败!");
                        e.printStackTrace();
                    }
    
                    @Override
                    public void onStart() {
                        // 刚刚开始下载
                        Log.i(tag,"刚刚开始下载!");
                        super.onStart();
                    }
    
                    @Override
                    public void onLoading(long total, long current, boolean isUploading) {
                        // 下载过程(下载文件大小,当前的下载位置,是否正在下载)
                        Log.i(tag,"下载中......文件大小为" + total + "当前的下载位置为" + current);
                        super.onLoading(total, current, isUploading);
                    }
                });
            }
        }
    
        /**
         * 9.APK安装
         * @param file 安装文件
         */
        private void installApk(File file) {
            // 系统应用界面,源码,安装apk入口
            Intent intent = new Intent("android.intent.action.VIEW");
            intent.addCategory("android.intent.category.DEFAULT");
            // 文件作为数据源
            // intent.setData(Uri.fromFile(file));
            // 设置安装的类型
            // intent.setType("application/vnd.android.package-archive");
            intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
            startActivity(intent);
        }
    }
    
    1. 运行后,安装时会报错,原因如下:

      1. 包名一致(√):由于该下载会下载一个同签名,同包名的APK文件,就会导致之前提示的错误,这里需要的更新操作要覆盖掉旧的apk,所以需要保持包名一致
      2. 签名一致(×):直接在模拟器上运行的应用(旧版本),签名文件为debug.keystore,而新版本的签名文件在打包的过程中使用的是新的,所以实质上是不一致的
    2. 所以,为了保持签名一致,我们需要让旧版本的签名文件也跟新版本的签名文件一致,所以对旧版本专门进行一次打包,然后再安装到模拟器上,即可完成正常的安装

    18.安装过程中点击取消

    为了防止用户点击回退后出现bug,所以需要进一步完善相关逻辑

    1. 修改SplashActivity,修改showUpdateDialog()方法,添加按下回退按钮的事件监听,代码如下:
    package com.example.mobilesafe.activity;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AlertDialog;
    import androidx.appcompat.app.AppCompatActivity;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    import com.example.mobilesafe.utils.StreamUtil;
    import com.example.mobilesafe.utils.ToastUtil;
    import com.lidroid.xutils.HttpUtils;
    import com.lidroid.xutils.exception.HttpException;
    import com.lidroid.xutils.http.ResponseInfo;
    import com.lidroid.xutils.http.callback.RequestCallBack;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        /**
         * 更新时描述信息
         */
        private String mVersionDes;
    
        /**
         * 更新时的URL
         */
        private String mDownloadUrl;
    
        private static final String tag = "SplashActivity";
    
        /**
         * Handler对象
         */
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what){
                    case UPDATE_VERSION:
                        // 1.弹出对话框,提示用户更新
                        showUpdateDialog();
                        break;
                    case ENTER_HOME:
                        // 2.直接进入应用程序主界面
                        enterHome();
                        break;
                    case URL_ERROR:
                        // 3.弹出URL错误
                        ToastUtil.show(SplashActivity.this,"url异常");
                        enterHome();
                        break;
                    case IO_ERROR:
                        // 4.弹出IO错误
                        ToastUtil.show(SplashActivity.this,"IO异常");
                        enterHome();
                        break;
                    case JSON_ERROR:
                        // 5.弹出JSON错误
                        ToastUtil.show(SplashActivity.this,"json异常");
                        enterHome();
                        break;
                    default:break;
                }
            }
        };
    
        /**
         * 更新新版本的状态码
         */
        private static final int UPDATE_VERSION = 100;
    
        /**
         * 进入应用程序主界面的状态码
         */
        private static final int ENTER_HOME = 101;
    
        /**
         * URL地址出错的状态码
         */
        private static final int URL_ERROR = 102;
    
        /**
         * IO操作出错的状态码
         */
        private static final int IO_ERROR = 103;
    
        /**
         * JSON解析出错的状态码
         */
        private static final int JSON_ERROR = 104;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地(客户端)版本号
            mLocalVersionCode = getVersionCode();
            // 4.获取服务端版本号(客户端发请求,服务端给响应(Json、Xml))
            /*
            Json中内容应该包括:
                1.更新版本的名称 versionName
                2.新版本的描述信息 versionDes
                3.服务器的版本号 versionCode
                4.新版本apk下载地址 downloadUrl
             */
            checkVersion();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 5.获取服务端版本号
         */
        private void checkVersion() {
            // 发送请求,获取数据,参数则为请求json的链接地址
            new Thread(){
                @Override
                public void run() {
                    // 0.获取message对象
                    Message msg = Message.obtain();
                    long startTime = System.currentTimeMillis();// 获取时间戳
                    try {
                        // 1.封装url地址
                        URL url = new URL("http://10.0.2.2:8080/update74.json");
                        // 2.开启一个链接
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        // 3.设置常见请求参数(请求头)
                        connection.setConnectTimeout(2000); // 请求超时
                        connection.setReadTimeout(2000); // 读取超时
                        //connection.setRequestMethod("GET"); // 请求方式,默认是get请求方式
                        // 4.获取相应码,200为请求成功
                        if (connection.getResponseCode() == 200){
                            // 5.以流的形式将数据获取下来
                            InputStream is = connection.getInputStream();
                            // 6.将流转换成字符串(工具封装类)
                            String json = StreamUtil.streamToString(is);
                            // 7.json解析
                            JSONObject jsonObject = new JSONObject(json);
                            String versionName = jsonObject.getString("versionName");
                            mVersionDes = jsonObject.getString("versionDes");
                            String versionCode = jsonObject.getString("versionCode");
                            mDownloadUrl = jsonObject.getString("downloadUrl");
                            // 8.比对版本号(服务器版本号 > 本地版本号,提示用户更新)
                            if (Integer.parseInt(versionCode) > mLocalVersionCode){
                                // 9.提示用户更新,弹出对话框(UI),需要使用到消息机制
                                msg.what = UPDATE_VERSION;
                            }else {
                                // 10.不需要更新,直接进入应用程序主界面
                                msg.what = ENTER_HOME;
                            }
                        }
                    }catch (MalformedURLException e) {
                        e.printStackTrace();
                        msg.what = URL_ERROR;
                    }catch (IOException e) {
                        e.printStackTrace();
                        msg.what = IO_ERROR;
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                        msg.what = JSON_ERROR;
                    }finally {
                        // 11.指定睡眠时间,请求网络的时长超过4秒则不做处理,若小于4秒,则强制让其睡眠满4秒
                        long endTime = System.currentTimeMillis();
                        if (endTime - startTime < 4000){
                            try {
                                Thread.sleep(4000 - (endTime - startTime));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        // 12.发送消息
                        mHandler.sendMessage(msg);
                    }
                }
            }.start();
        }
    
        /**
         * 6.进入应用程序的主界面
         */
        private void enterHome() {
            Intent intent = new Intent(this, HomeActivity.class);
            startActivity(intent);
            finish(); // 开启新界面后,将导航界面销毁掉
        }
    
        /**
         * 7.弹出更新对话框
         */
        private void showUpdateDialog() {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setIcon(R.drawable.ic_launcher); // 设置左上角图标
            builder.setTitle("版本更新"); // 设置标题
            builder.setMessage(mVersionDes); // 设置描述内容
            builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // 下载apk,需要apk的链接地址,即downloadUrl
                    downloadApk();
                }
            });// 积极按钮,“是”
            builder.setNegativeButton("稍后再说", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // 取消对话框,进入主界面
                    enterHome();
                }
            });// 消极按钮,“否”
            builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
                @Override
                public void onCancel(DialogInterface dialog) {
                    // 按下回退后,进入主界面,然后隐藏对话框
                    enterHome();
                    dialog.dismiss();
                }
            });// 回退按钮
            builder.show();
        }
    
        /**
         * 8.APK下载
         */
        private void downloadApk() {
            // 需要apk下载链接地址,放置apk的所在路径
            // 1.判断sd卡是否可用,是否挂载
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                // 2.获取sd卡路径
                String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "app-release.apk";
                Log.i(tag,"路径为:" + path);
                // 3.发送请求,获取Apk,并且放置到指定路径(下载地址,下载应用的放置位置,回调方法)
                HttpUtils httpUtils = new HttpUtils();
                httpUtils.download(mDownloadUrl, path, new RequestCallBack<File>() {
                    @Override
                    public void onSuccess(ResponseInfo<File> responseInfo) {
                        // 下载成功(下载过后的放置在sd卡中apk)
                        Log.i(tag,"下载成功!");
                        File file = responseInfo.result;
                        installApk(file);
                    }
    
                    @Override
                    public void onFailure(HttpException e, String s) {
                        // 下载失败
                        Log.i(tag,"下载失败!");
                        e.printStackTrace();
                    }
    
                    @Override
                    public void onStart() {
                        // 刚刚开始下载
                        Log.i(tag,"刚刚开始下载!");
                        super.onStart();
                    }
    
                    @Override
                    public void onLoading(long total, long current, boolean isUploading) {
                        // 下载过程(下载文件大小,当前的下载位置,是否正在下载)
                        Log.i(tag,"下载中......文件大小为" + total + "当前的下载位置为" + current);
                        super.onLoading(total, current, isUploading);
                    }
                });
            }
        }
    
        /**
         * 9.APK安装
         * @param file 安装文件
         */
        private void installApk(File file) {
            // 系统应用界面,源码,安装apk入口
            Intent intent = new Intent("android.intent.action.VIEW");
            intent.addCategory("android.intent.category.DEFAULT");
            // 文件作为数据源
            // intent.setData(Uri.fromFile(file));
            // 设置安装的类型
            // intent.setType("application/vnd.android.package-archive");
            intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
            startActivity(intent);
        }
    }
    
    1. 修改SplashActivity,若在安装新的apk时点击“取消”按钮,就应该回到原来的activity(SplashActivity),然后再跳转到HomeActivity,完善这个逻辑,逻辑如图所示:

      [转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5ufpjYKq-1590936149776)(C:\Users\moxitao\AppData\Roaming\Typora\typora-user-images\image-20200423174346272.png)]

      代码如下:

    package com.example.mobilesafe.activity;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.appcompat.app.AlertDialog;
    import androidx.appcompat.app.AppCompatActivity;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.widget.TextView;
    
    import com.example.mobilesafe.R;
    import com.example.mobilesafe.utils.StreamUtil;
    import com.example.mobilesafe.utils.ToastUtil;
    import com.lidroid.xutils.HttpUtils;
    import com.lidroid.xutils.exception.HttpException;
    import com.lidroid.xutils.http.ResponseInfo;
    import com.lidroid.xutils.http.callback.RequestCallBack;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class SplashActivity extends AppCompatActivity {
    
        /**
         * 文本控件
         */
        private TextView tv_version_name;
    
        /**
         * 本地版本号
         */
        private int mLocalVersionCode;
    
        /**
         * 更新时描述信息
         */
        private String mVersionDes;
    
        /**
         * 更新时的URL
         */
        private String mDownloadUrl;
    
        private static final String tag = "SplashActivity";
    
        /**
         * Handler对象
         */
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what){
                    case UPDATE_VERSION:
                        // 1.弹出对话框,提示用户更新
                        showUpdateDialog();
                        break;
                    case ENTER_HOME:
                        // 2.直接进入应用程序主界面
                        enterHome();
                        break;
                    case URL_ERROR:
                        // 3.弹出URL错误
                        ToastUtil.show(SplashActivity.this,"url异常");
                        enterHome();
                        break;
                    case IO_ERROR:
                        // 4.弹出IO错误
                        ToastUtil.show(SplashActivity.this,"IO异常");
                        enterHome();
                        break;
                    case JSON_ERROR:
                        // 5.弹出JSON错误
                        ToastUtil.show(SplashActivity.this,"json异常");
                        enterHome();
                        break;
                    default:break;
                }
            }
        };
    
        /**
         * 更新新版本的状态码
         */
        private static final int UPDATE_VERSION = 100;
    
        /**
         * 进入应用程序主界面的状态码
         */
        private static final int ENTER_HOME = 101;
    
        /**
         * URL地址出错的状态码
         */
        private static final int URL_ERROR = 102;
    
        /**
         * IO操作出错的状态码
         */
        private static final int IO_ERROR = 103;
    
        /**
         * JSON解析出错的状态码
         */
        private static final int JSON_ERROR = 104;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
    
            // 初始化UI
            initUI();
    
            // 初始化数据
            initData();
        }
    
        /**
         * 1.初始化UI
         */
        private void initUI() {
            tv_version_name = findViewById(R.id.tv_version_name);
        }
    
        /**
         * 2.初始化数据
         */
        private void initData() {
            // 1.获取应用版本名称
            String versionName = getVersionName();
            // 2.将应用版本名称设置到文本控件中
            tv_version_name.setText("版本名称:" + versionName);
            // 3.获取本地(客户端)版本号
            mLocalVersionCode = getVersionCode();
            // 4.获取服务端版本号(客户端发请求,服务端给响应(Json、Xml))
            /*
            Json中内容应该包括:
                1.更新版本的名称 versionName
                2.新版本的描述信息 versionDes
                3.服务器的版本号 versionCode
                4.新版本apk下载地址 downloadUrl
             */
            checkVersion();
        }
    
        /**
         * 3.获取版本应用名称(在清单文件中)
         * @return 版本名称
         */
        private String getVersionName() {
            // 1.获取包管理对象packageManager 
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本名称
                return packageInfo.versionName;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            // 4.抛出异常
            return null;
        }
    
        /**
         * 4.获取版本号(在清单文件中)
         * @return 版本号
         */
        private int getVersionCode() {
            // 1.获取包管理对象packageManager
            PackageManager pm = getPackageManager();
            // 2.从包管理对象中,获取指定包名的基本信息(版本名称,版本号),第二个参数传0代表获取基本信息
            try {
                PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 3.获取并返回版本编号
                return packageInfo.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 5.获取服务端版本号
         */
        private void checkVersion() {
            // 发送请求,获取数据,参数则为请求json的链接地址
            new Thread(){
                @Override
                public void run() {
                    // 0.获取message对象
                    Message msg = Message.obtain();
                    long startTime = System.currentTimeMillis();// 获取时间戳
                    try {
                        // 1.封装url地址
                        URL url = new URL("http://10.0.2.2:8080/update74.json");
                        // 2.开启一个链接
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        // 3.设置常见请求参数(请求头)
                        connection.setConnectTimeout(2000); // 请求超时
                        connection.setReadTimeout(2000); // 读取超时
                        //connection.setRequestMethod("GET"); // 请求方式,默认是get请求方式
                        // 4.获取相应码,200为请求成功
                        if (connection.getResponseCode() == 200){
                            // 5.以流的形式将数据获取下来
                            InputStream is = connection.getInputStream();
                            // 6.将流转换成字符串(工具封装类)
                            String json = StreamUtil.streamToString(is);
                            // 7.json解析
                            JSONObject jsonObject = new JSONObject(json);
                            String versionName = jsonObject.getString("versionName");
                            mVersionDes = jsonObject.getString("versionDes");
                            String versionCode = jsonObject.getString("versionCode");
                            mDownloadUrl = jsonObject.getString("downloadUrl");
                            // 8.比对版本号(服务器版本号 > 本地版本号,提示用户更新)
                            if (Integer.parseInt(versionCode) > mLocalVersionCode){
                                // 9.提示用户更新,弹出对话框(UI),需要使用到消息机制
                                msg.what = UPDATE_VERSION;
                            }else {
                                // 10.不需要更新,直接进入应用程序主界面
                                msg.what = ENTER_HOME;
                            }
                        }
                    }catch (MalformedURLException e) {
                        e.printStackTrace();
                        msg.what = URL_ERROR;
                    }catch (IOException e) {
                        e.printStackTrace();
                        msg.what = IO_ERROR;
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                        msg.what = JSON_ERROR;
                    }finally {
                        // 11.指定睡眠时间,请求网络的时长超过4秒则不做处理,若小于4秒,则强制让其睡眠满4秒
                        long endTime = System.currentTimeMillis();
                        if (endTime - startTime < 4000){
                            try {
                                Thread.sleep(4000 - (endTime - startTime));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        // 12.发送消息
                        mHandler.sendMessage(msg);
                    }
                }
            }.start();
        }
    
        /**
         * 6.进入应用程序的主界面
         */
        private void enterHome() {
            Intent intent = new Intent(this, HomeActivity.class);
            startActivity(intent);
            finish(); // 开启新界面后,将导航界面销毁掉
        }
    
        /**
         * 7.弹出更新对话框
         */
        private void showUpdateDialog() {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setIcon(R.drawable.ic_launcher); // 设置左上角图标
            builder.setTitle("版本更新"); // 设置标题
            builder.setMessage(mVersionDes); // 设置描述内容
            builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // 下载apk,需要apk的链接地址,即downloadUrl
                    downloadApk();
                }
            });// 积极按钮,“是”
            builder.setNegativeButton("稍后再说", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // 取消对话框,进入主界面
                    enterHome();
                }
            });// 消极按钮,“否”
            builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
                @Override
                public void onCancel(DialogInterface dialog) {
                    // 按下回退后,进入主界面,然后隐藏对话框
                    enterHome();
                    dialog.dismiss();
                }
            });// 回退按钮
            builder.show();
        }
    
        /**
         * 8.APK下载
         */
        private void downloadApk() {
            // 需要apk下载链接地址,放置apk的所在路径
            // 1.判断sd卡是否可用,是否挂载
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                // 2.获取sd卡路径
                String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "app-release.apk";
                Log.i(tag,"路径为:" + path);
                // 3.发送请求,获取Apk,并且放置到指定路径(下载地址,下载应用的放置位置,回调方法)
                HttpUtils httpUtils = new HttpUtils();
                httpUtils.download(mDownloadUrl, path, new RequestCallBack<File>() {
                    @Override
                    public void onSuccess(ResponseInfo<File> responseInfo) {
                        // 下载成功(下载过后的放置在sd卡中apk)
                        Log.i(tag,"下载成功!");
                        File file = responseInfo.result;
                        installApk(file);
                    }
    
                    @Override
                    public void onFailure(HttpException e, String s) {
                        // 下载失败
                        Log.i(tag,"下载失败!");
                        e.printStackTrace();
                    }
    
                    @Override
                    public void onStart() {
                        // 刚刚开始下载
                        Log.i(tag,"刚刚开始下载!");
                        super.onStart();
                    }
    
                    @Override
                    public void onLoading(long total, long current, boolean isUploading) {
                        // 下载过程(下载文件大小,当前的下载位置,是否正在下载)
                        Log.i(tag,"下载中......文件大小为" + total + "当前的下载位置为" + current);
                        super.onLoading(total, current, isUploading);
                    }
                });
            }
        }
    
        /**
         * 9.APK安装
         * @param file 安装文件
         */
        private void installApk(File file) {
            // 系统应用界面,源码,安装apk入口
            Intent intent = new Intent("android.intent.action.VIEW");
            intent.addCategory("android.intent.category.DEFAULT");
            // 文件作为数据源
            // intent.setData(Uri.fromFile(file));
            // 设置安装的类型
            // intent.setType("application/vnd.android.package-archive");
            intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
            startActivityForResult(intent,0);
        }
    
        /**
         * 10.开启一个Activity后,返回结果
         * @param requestCode
         * @param resultCode
         * @param data
         */
        @Override
        protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
            enterHome();
            super.onActivityResult(requestCode, resultCode, data);
        }
    }
    

    整个更新的流程如图所示:
    在这里插入图片描述

    展开全文
  • android手机安全卫士

    2012-04-13 00:11:05
    1、通过0权限上传下载数据,重启手机等案例,深入讲解android沙箱,安全机制和权限模型。 2、通过分析恶意代码的提权漏洞,讲解如何维护系统的安全。 3、通过linux键盘驱动案例的讲解,分析盗号木马的原理及其实现...
  • 手机安全卫士_day1.rar 手机安全卫士_day2.rar 手机安全卫士_day3.rar 手机安全卫士_day4.rar 手机安全卫士_day5.rar 手机安全卫士_day6.rar 手机安全卫士_day7.rar 手机安全卫士_day8.rar 手机安全卫士_day...
    手机安全卫士_day1.rar
    
    手机安全卫士_day2.rar
    手机安全卫士_day3.rar
    手机安全卫士_day4.rar
    手机安全卫士_day5.rar
    手机安全卫士_day6.rar
    手机安全卫士_day7.rar
    手机安全卫士_day8.rar

    手机安全卫士_day9.rar


    下载地址:http://www.javaxxz.com/thread-255470-1-1.html

    展开全文
  • android 手机安全卫士工程源代码 共大家学习使用 代码中有很多注解方便大家看懂学习
  • 手机安全卫士源码

    2017-03-07 12:38:50
    主要功能:1、软件更新与下载。2、手机防盗。3、骚扰拦截。4、软件管家。5、进程管理。6、流量统计。7、手机杀毒。8、系统加速。9、常用工具。所有功能皆可实现,且界面已经优化,是一款完整且可使用的手机软件
  • Android手机安全卫士--自动更新2,接上一次代码,实现自动服务器端apk软件自动下载,并自动安装效果。更多详细信息请参考 http://blog.csdn.net/qq_20889581?viewmode=contents 文明的小流氓的博客
  • 手机安全卫士03

    2016-05-06 23:21:00
    手机安全卫士03 今天做的东西有点乱,之后再来整理 一、Splash界面 软件下载 Xutils 框架 (四大模块) – BitmapUtils – HttpUtils – DBUtils – ViewUtils 进度条展示 调用系统的安装器安装软件...

    手机安全卫士03

    今天做的东西有点乱,之后再来整理

    一、Splash界面

    1. 软件下载
      Xutils 框架 (四大模块)
      – BitmapUtils
      – HttpUtils
      – DBUtils
      – ViewUtils
    2. 进度条展示

    3. 调用系统的安装器安装软件
      1.升级apk : 检测包名一致 , 版本号大于当前的版本 , 签名要一致

    二、Home 主页

    1. 搭建Home界面
      • GridView的使用
        -
    2. 文本框字幕滚动
    3. LogUtil 日志工具类
    4. ## 三、自定义控件
    5. 好处:
      1. 自定义功能 TextView –拓展: 可获取焦点的TextView
      2. 自定义界面
      3. 自定义属性
    6. 如何自定义

      1. 完全自定义控件
        继承View,实现自定义 ,实现 onDraw :绘制 ,onMesure :测量 onTouchEvent :触摸事件
      2. 继承某个Widget(小部件),实现自定义控件
        FocusableTextView extends TextView

      3. 组合自定义控件
        多个控件组合在一起 ,实现控件的自定义

    四、设置中心

    1. 开启或屏蔽自动更新
    2. 组合自定义控件实现 自动更新设置
    3. 自定义属性
      在布局文件中实现系统没有提供的属性 ,即自定义属性
      • 查看系统属性如何自定义
      • 定义自定的属性
      • copy 全路径
      • 在布局中使用自定义属性
        引用命名空间
        第一步:
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <declare-styleable name="com.example.safephone.view.SettingView">
            <attr name="setting_title" format="string" />
            <attr name="des_on" format="string" />
            <attr name="des_off" format="string" />
        </declare-styleable>
    
    </resources>

    第二步:
    有个前提条件:需要在xml文件中配置自定义 xmlns:jaytang=”http://schemas.android.com/apk/res/com.example.safephone”

     - 在代码A、存放数据信息
    //1、打开Preferences,名称为setting,如果存在则打开它,否则创建新的Preferences
    SharedPreferences settings = getSharedPreferences(“setting”, 0);
    //2、让setting处于编辑状态
    SharedPreferences.Editor editor = settings.edit();
    //3、存放数据
    editor.putString(“name”,”ATAAW”);
    editor.putString(“URL”,”ATAAW.COM”);
    //4、完成提交
    editor.commit();
    
    B、读取数据信息
    
    //1、获取Preferences
    SharedPreferences settings = getSharedPreferences(“setting”, 0);
    //2、取出数据
    String name = settings.getString(“name”,”默认值”);
    String url = setting.getString(“URL”,”default”);
    //以上就是Android中SharedPreferences的使用方法,其中创建的Preferences文件存放位置可以在Eclipse中查看:
    DDMS->File Explorer /<package name>/shared_prefs/setting.xml 中应用属性

    转载于:https://www.cnblogs.com/Tesi1a/p/7624141.html

    展开全文
  • 下载地址:http://download.csdn.net/detail/lengyanyue2014/7993363 运行截图: 源码目录 │ .classpath │ .project │ address.db │ AndroidManifest.xml │ ...

    下载地址:http://download.csdn.net/detail/lengyanyue2014/7993363


    运行截图:





    源码目录

    │  .classpath

    │  .project
    │  address.db
    │  AndroidManifest.xml
    │  lint.xml
    │  proguard.cfg
    │  project.properties
    │  文件列表生成工具.bat
    │  目录列表.txt
    │  
    ├─.settings
    │      org.eclipse.jdt.core.prefs
    │      
    ├─bin
    │  │  AndroidManifest.xml
    │  │  classes.dex
    │  │  com.mobilesafe.SplashActivity.apk
    │  │  jarlist.cache
    │  │  resources.ap_
    │  │  
    │  ├─aidl
    │  │  ├─android
    │  │  │  └─telephony
    │  │  │          NeighboringCellInfo.aidl
    │  │  │          
    │  │  └─com
    │  │      └─android
    │  │          └─internal
    │  │              └─telephony
    │  │                      ITelephony.aidl
    │  │                      
    │  ├─classes
    │  │  │  address.db
    │  │  │  
    │  │  ├─android
    │  │  │  └─telephony
    │  │  │          NeighboringCellInfo.aidl
    │  │  │          
    │  │  └─com
    │  │      ├─android
    │  │      │  └─internal
    │  │      │      └─telephony
    │  │      │              ITelephony$Stub$Proxy.class
    │  │      │              ITelephony$Stub.class
    │  │      │              ITelephony.aidl
    │  │      │              ITelephony.class
    │  │      │              
    │  │      └─mobilesafe
    │  │          │  ApplicationInfo2.class
    │  │          │  BuildConfig.class
    │  │          │  CallSmsSafeActivity$1.class
    │  │          │  CallSmsSafeActivity$MyListAdapter.class
    │  │          │  CallSmsSafeActivity$ViewHolder.class
    │  │          │  CallSmsSafeActivity.class
    │  │          │  CheckVersionTask$1.class
    │  │          │  CheckVersionTask$2.class
    │  │          │  CheckVersionTask$3.class
    │  │          │  CheckVersionTask$4$1.class
    │  │          │  CheckVersionTask$4.class
    │  │          │  CheckVersionTask.class
    │  │          │  ConfigSetup1Activity.class
    │  │          │  ConfigSetup2Activity.class
    │  │          │  ConfigSetup3Activity.class
    │  │          │  DragViewActivity$MyTouchListener.class
    │  │          │  DragViewActivity.class
    │  │          │  LostProtectActivity.class
    │  │          │  LostProtectSettingActivity$CheckBoxChangeListener.class
    │  │          │  LostProtectSettingActivity.class
    │  │          │  MainScreenActivity$1.class
    │  │          │  MainScreenActivity$2.class
    │  │          │  MainScreenActivity.class
    │  │          │  R$array.class
    │  │          │  R$attr.class
    │  │          │  R$drawable.class
    │  │          │  R$id.class
    │  │          │  R$layout.class
    │  │          │  R$menu.class
    │  │          │  R$string.class
    │  │          │  R$style.class
    │  │          │  R.class
    │  │          │  SelectContactActivity$1.class
    │  │          │  SelectContactActivity.class
    │  │          │  ShowAppActivity$1.class
    │  │          │  ShowAppActivity$2.class
    │  │          │  ShowAppActivity$AppDetailLinster$1.class
    │  │          │  ShowAppActivity$AppDetailLinster.class
    │  │          │  ShowAppActivity.class
    │  │          │  ShowAppDetailActivity.class
    │  │          │  SlidingdrawerActivity$1.class
    │  │          │  SlidingdrawerActivity$2.class
    │  │          │  SlidingdrawerActivity$3.class
    │  │          │  SlidingdrawerActivity$MyAdapter.class
    │  │          │  SlidingdrawerActivity$ViewHolder.class
    │  │          │  SlidingdrawerActivity.class
    │  │          │  SplashActivity$1.class
    │  │          │  SplashActivity$2.class
    │  │          │  SplashActivity$3.class
    │  │          │  SplashActivity$4.class
    │  │          │  SplashActivity$5$1.class
    │  │          │  SplashActivity$5.class
    │  │          │  SplashActivity$CheckVersionTask.class
    │  │          │  SplashActivity$CopyDBTask.class
    │  │          │  SplashActivity.class
    │  │          │  SttingCenterActivity$1.class
    │  │          │  SttingCenterActivity.class
    │  │          │  TaskManagerActivity$1.class
    │  │          │  TaskManagerActivity$2.class
    │  │          │  TaskManagerActivity$TaskItemClickLinstener$1.class
    │  │          │  TaskManagerActivity$TaskItemClickLinstener.class
    │  │          │  TaskManagerActivity.class
    │  │          │  ToolsActivity$1.class
    │  │          │  ToolsActivity$2.class
    │  │          │  ToolsActivity$3.class
    │  │          │  ToolsActivity.class
    │  │          │  
    │  │          ├─adapter
    │  │          │      MainGridViewAdapter.class
    │  │          │      ProcessListAdapter$ViewHolder.class
    │  │          │      ProcessListAdapter.class
    │  │          │      SelectContactListAdapter$ViewHolder.class
    │  │          │      SelectContactListAdapter.class
    │  │          │      ShowAppListAdapter$ViewHolder.class
    │  │          │      ShowAppListAdapter.class
    │  │          │      
    │  │          ├─bean
    │  │          │      ContactBean.class
    │  │          │      DetailProceess.class
    │  │          │      PackagesInfo.class
    │  │          │      UpdateInfo.class
    │  │          │      
    │  │          ├─db
    │  │          │  │  DBOpenHelper.class
    │  │          │  │  
    │  │          │  └─dao
    │  │          │          BlackNumberDao.class
    │  │          │          FileService.class
    │  │          │          
    │  │          ├─net
    │  │          │  └─download
    │  │          │          DownloadProgressListener.class
    │  │          │          DownloadThread.class
    │  │          │          FileDownloader.class
    │  │          │          
    │  │          ├─receiver
    │  │          │      BootCompleteReceiver.class
    │  │          │      CallNumberReceiver.class
    │  │          │      SmsReceiver.class
    │  │          │      
    │  │          ├─service
    │  │          │      AddressService.class
    │  │          │      ContactInfoService.class
    │  │          │      DownloadAPKService.class
    │  │          │      ShowAddressService$1.class
    │  │          │      ShowAddressService$CallStateListener$CallLogChangeLinster.class
    │  │          │      ShowAddressService$CallStateListener.class
    │  │          │      ShowAddressService.class
    │  │          │      
    │  │          ├─test
    │  │          │      testGetNumbers.class
    │  │          │      TestUpdateInfo.class
    │  │          │      
    │  │          ├─ui
    │  │          │      MyToast.class
    │  │          │      
    │  │          └─util
    │  │                  GPSInfoManager$GPSListener.class
    │  │                  GPSInfoManager.class
    │  │                  IntentUtils.class
    │  │                  MD5Encoder.class
    │  │                  PackagesInfo.class
    │  │                  TrafficDataUtil.class
    │  │                  UpdateInfoParser.class
    │  │                  
    │  └─res
    │      ├─drawable
    │      │      app.png
    │      │      atools.png
    │      │      callmsgsafe.png
    │      │      detail.png
    │      │      dialing_dial.png
    │      │      exit_icon.png
    │      │      icon5.png
    │      │      ic_menu_friendslist.png
    │      │      netmanager.png
    │      │      noprotecting.png
    │      │      notification.png
    │      │      protecting.png
    │      │      safe.png
    │      │      settings.png
    │      │      sysapp.png
    │      │      sysoptimize.png
    │      │      taskmanager.png
    │      │      telephone_meseesage.png
    │      │      top_bg.png
    │      │      trojan.png
    │      │      update_edition.png
    │      │      userapp.png
    │      │      
    │      ├─drawable-hdpi
    │      │      icon.png
    │      │      
    │      ├─drawable-ldpi
    │      │      icon.png
    │      │      
    │      └─drawable-mdpi
    │              icon.png
    │              
    ├─gen
    │  ├─com
    │  │  ├─android
    │  │  │  └─internal
    │  │  │      └─telephony
    │  │  │              ITelephony.java
    │  │  │              
    │  │  └─mobilesafe
    │  │          BuildConfig.java
    │  │          R.java
    │  │          
    │  └─android
    │      └─telephony
    ├─Lib
    │      pinyin4j-2.5.0.jar
    │      
    ├─pinyindb
    │      pinyin_gwoyeu_mapping.xml
    │      pinyin_mapping.xml
    │      unicode_to_hanyu_pinyin.txt
    │      
    ├─res
    │  ├─drawable
    │  │      addressbackground.xml
    │  │      addressicon.JPG
    │  │      app.png
    │  │      atools.png
    │  │      callmsgsafe.png
    │  │      detail.png
    │  │      dialing_dial.png
    │  │      exit_icon.png
    │  │      float_box.xml
    │  │      icon5.png
    │  │      ic_menu_friendslist.png
    │  │      jbshape.xml
    │  │      logo2.jpg
    │  │      netmanager.png
    │  │      noprotecting.png
    │  │      notification.png
    │  │      protecting.png
    │  │      safe.png
    │  │      settings.png
    │  │      sysapp.png
    │  │      sysoptimize.png
    │  │      taskmanager.png
    │  │      telephone_meseesage.png
    │  │      top_bg.png
    │  │      trojan.png
    │  │      update_edition.png
    │  │      userapp.png
    │  │      
    │  ├─drawable-hdpi
    │  │      icon.png
    │  │      
    │  ├─drawable-ldpi
    │  │      icon.png
    │  │      
    │  ├─drawable-mdpi
    │  │      icon.png
    │  │      
    │  ├─layout
    │  │      add_blacknumber.xml
    │  │      app_detial.xml
    │  │      app_list.xml
    │  │      app_list_item.xml
    │  │      call_records.xml
    │  │      call_smssafe_item.xml
    │  │      call_sms_safe.xml
    │  │      configstep1.xml
    │  │      configstep2.xml
    │  │      configstep3.xml
    │  │      contact.xml
    │  │      contact_info_list_item.xml
    │  │      contact_list_item.xml
    │  │      dialog.xml
    │  │      dragview.xml
    │  │      firstentrydialog.xml
    │  │      ipdailsetting.xml
    │  │      lost_protect_setting.xml
    │  │      main.xml
    │  │      main_gv_item.xml
    │  │      main_screen.xml
    │  │      newwork_traffic.xml
    │  │      normalentrydialog.xml
    │  │      phone_info.xml
    │  │      pop_overlay.xml
    │  │      queryaddress.xml
    │  │      quick_setting.xml
    │  │      selectcontact.xml
    │  │      show_phone_address.xml
    │  │      splash_activity.xml
    │  │      stting_center.xml
    │  │      task_list_item.xml
    │  │      task_manager.xml
    │  │      tools.xml
    │  │      traffic_item.xml
    │  │      
    │  ├─menu
    │  │      menu.xml
    │  │      
    │  ├─values
    │  │      config.xml
    │  │      prompt.xml
    │  │      strings.xml
    │  │      style.xml
    │  │      version.xml
    │  │      
    │  └─drawable-xhdpi
    ├─src
    │  │  address.db
    │  │  
    │  ├─android
    │  │  └─telephony
    │  │          NeighboringCellInfo.aidl
    │  │          
    │  └─com
    │      ├─android
    │      │  └─internal
    │      │      └─telephony
    │      │              ITelephony.aidl
    │      │              
    │      └─mobilesafe
    │          │  ApplicationInfo2.java
    │          │  CallSmsSafeActivity.java
    │          │  CheckVersionTask.java
    │          │  ConfigSetup1Activity.java
    │          │  ConfigSetup2Activity.java
    │          │  ConfigSetup3Activity.java
    │          │  DragViewActivity.java
    │          │  LostProtectActivity.java
    │          │  LostProtectSettingActivity.java
    │          │  MainScreenActivity.java
    │          │  SelectContactActivity.java
    │          │  ShowAppActivity.java
    │          │  ShowAppDetailActivity.java
    │          │  SlidingdrawerActivity.java
    │          │  SplashActivity.java
    │          │  SttingCenterActivity.java
    │          │  TaskManagerActivity.java
    │          │  ToolsActivity.java
    │          │  
    │          ├─adapter
    │          │      MainGridViewAdapter.java
    │          │      ProcessListAdapter.java
    │          │      SelectContactListAdapter.java
    │          │      ShowAppListAdapter.java
    │          │      
    │          ├─bean
    │          │      ContactBean.java
    │          │      DetailProceess.java
    │          │      PackagesInfo.java
    │          │      UpdateInfo.java
    │          │      
    │          ├─db
    │          │  │  DBOpenHelper.java
    │          │  │  
    │          │  └─dao
    │          │          BlackNumberDao.java
    │          │          FileService.java
    │          │          
    │          ├─net
    │          │  └─download
    │          │          DownloadProgressListener.java
    │          │          DownloadThread.java
    │          │          FileDownloader.java
    │          │          
    │          ├─receiver
    │          │      BootCompleteReceiver.java
    │          │      CallNumberReceiver.java
    │          │      SmsReceiver.java
    │          │      
    │          ├─service
    │          │      AddressService.java
    │          │      ContactInfoService.java
    │          │      DownloadAPKService.java
    │          │      ShowAddressService.java
    │          │      
    │          ├─test
    │          │      testGetNumbers.java
    │          │      TestUpdateInfo.java
    │          │      
    │          ├─ui
    │          │      MyToast.java
    │          │      
    │          └─util
    │                  GPSInfoManager.java
    │                  IntentUtils.java
    │                  MD5Encoder.java
    │                  PackagesInfo.java
    │                  TrafficDataUtil.java
    │                  UpdateInfoParser.java
    │                  
    └─assets
    展开全文
  • 版本升级,涉及到提示用户升级、下载 apk、安装 apk 三部分
  • 这个是模仿了一下常见的手机安全卫士的应用的源码的,基本是实现了常用的功能的了,不过有的还是不够完善,大家可以多多研究一下吧。 Screenshot_2014-05-11-03-44-01.png (287.72 KB, 下载次数: 0) ...
  • 关于手机安全卫士开发详解

    千次阅读 2016-11-07 20:45:06
    手机安全卫士 1 初始化界面的搭建 1.1 界面UI 界面的ui主要完成的是背景图片的显示,以及版本号的显示,其中版本号是需要动态获取显示的。 主要实现:由于布局的特点选择相对布局,在RelativeLayout中设置背景...
  • Android手机安全卫士2.0

    2014-06-03 15:02:55
    手机安全卫士是本人在自学安卓期间所做的一个项目,项目素材来自网上,通过观察金山手机卫士以及一些网上的资料做出来的,历时2个月。下面上图这个安全卫士所用到的技术(1)使用HTTPURLConnection连接Tomcat服务器,...
  • 基于Android平台的手机安全卫士的设计与实现 附源码和文档。 做毕业设计,课程设计或者正在学习相关技术知识的朋友可以下载资源学习。想继续学习相关知识的可以关注我。 系统主要功能: 手机防盗 程序锁 通讯卫士 ...
  • 手机安全卫士07

    2015-07-16 09:01:51
    (1)获取手机内存,手机SD卡内存 tv_avail_rom=(TextView) findViewById(R.id.tv_avail_rom); tv_avail_sd=(TextView) findViewById(R.id.tv_avail_sd); long avail_sd = Environment.getExternalStorageDir
  • 手机安全卫士第一天

    2015-12-29 10:00:33
    一、代码组织结构 1、根据业务逻辑划分 办公软件 出差 com.xbmu.travel工资 ...网盘 上传 com.xbmu.upload下载 com.xbmu.download分析 com.xbmu.share 2、根据功能模块划分(Android开发推荐此方法) Activity
  • 手机安全卫士02

    2016-05-05 19:55:00
    写Splash界面 1.展示公司的logo,(展示版本号) *展示logo,广告,宣传 done *检测出版本 done *初始化项目 ,拷贝assert 数据文件...**检测版本(服务器有新版本,弹出框提示是否更新,下载apk再进入) ...
  • 手机安全卫士第四天

    2016-01-12 14:49:47
    一、归属地数据库介绍 & 数据库拷贝 号码归属地查询常用两种做法: 第一种:联网查询。 第二种:把数据库放在本地。...这里我们使用已经下载好的小米数据库,使用SQLite工具打开: 观察data1、data2表的设计
  • android:text="手机卫士,有最新版本了,小伙伴们感觉来下载,先下载有优先大礼包" android:textColor="@color/black" android:textSize="16sp" /> android:id="@+id/gv_home" android:layout_width="match_...
  • 手机安全卫士10

    2015-07-19 20:45:46
    //手机2G/3G下载的总流量 TrafficStats.getTotalTxBytes();//手机全部网络接口,包括WiFi,3g,2g上传的总流量 TrafficStats.getTotalRxBytes();//手机全部网络接口,包括WiFi,3g,2g下载的总流量 (2)只能从下往...
  • Android项目实战-手机安全卫士
  • 手机安全卫士01

    2015-06-16 15:55:42
    // 用来管理手机的APK PackageManager pm = getPackageManager(); try { // 得到知道APK的功能清单文件 PackageInfo info = pm.getPackageInfo(getPackageName(), 0); return info.versionName; }...
  • 手机安全卫士第九天

    2016-05-13 01:41:27
    手机安全卫士第九天实现的功能: 手机杀毒功能 手机杀毒的原理:每个应用程序都会有对应的MD5值,我们只需要拿到应用程序的MD5值然后与病毒数据库中的值进行对比,若发现有相同的MD5值则判定为病毒文件,如...
  • 手机安全卫士的简介

    2012-04-13 23:47:00
    经过一个多月的时间,终于把手机安全卫士的代码写完了,虽然界面很丑,但是核心代码都是对的,而且都是源代码,特此共享下载链接是 http://download.csdn.net/detail/jinlong_lou/4222073 同时欢迎android爱好者...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,380
精华内容 1,352
关键字:

下载手机安全卫士