文章目录

1.手机防盗需求分析

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

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

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

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

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

Android开发实战《手机安全卫士》——1.手机安全卫士的需求分析 & 更新 & 打包_安全卫士

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

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

Android开发实战《手机安全卫士》——1.手机安全卫士的需求分析 & 更新 & 打包_android_02

高级工具的功能包括:

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

3.手机卫士的包名划分

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

  • 按照组成方式:命名方式为​​com.itheima.db​
  • 按照业务逻辑方式:命名方式为​​com.icbc.money​​​、​​com.icbc.meeting​
  • 按照组件划分:主要以四大组件为主,包括Activity、Service、ContentProvider、BroadCastReceiver,命名方式为​​com.itheima.activity​​​、​​com.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.文本框阴影效果

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

Android开发实战《手机安全卫士》——1.手机安全卫士的需求分析 & 更新 & 打包_xml_03

修改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">"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编码

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

Android开发实战《手机安全卫士》——1.手机安全卫士的需求分析 & 更新 & 打包_android_04

  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,出现以下界面即可说明服务器启动成功,如图所示:

Android开发实战《手机安全卫士》——1.手机安全卫士的需求分析 & 更新 & 打包_xml_05

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类),输出结果如图所示:

Android开发实战《手机安全卫士》——1.手机安全卫士的需求分析 & 更新 & 打包_Android_06

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文件因人而异,最好配置完成后可以去网页上测试一下,看能否访问到这个文件),运行文件,进行相应测试:

Android开发实战《手机安全卫士》——1.手机安全卫士的需求分析 & 更新 & 打包_安全卫士_07

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,而新版本的签名文件在打包的过程中使用的是新的,所以实质上是不一致的
  1. 所以,为了保持签名一致,我们需要让旧版本的签名文件也跟新版本的签名文件一致,所以对旧版本专门进行一次打包,然后再安装到模拟器上,即可完成正常的安装

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,完善这个逻辑,逻辑如图所示:
  2. Android开发实战《手机安全卫士》——1.手机安全卫士的需求分析 & 更新 & 打包_xml_08

  3. 代码如下:
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开发实战《手机安全卫士》——1.手机安全卫士的需求分析 & 更新 & 打包_初始化_09