【0052】【项目实战】-【手机安全卫士】-【1】

 【注】

【1】成员变量的命名:

【2】开启线程的两种方法

【1】手机防盗需求分析

【1.1】SplashActivity界面

(1)版本名称的展示,从清单配置文件中获取版本名称,PackageManager
(2)服务端新版本的检测(本地版本号<服务器版本号)
(3)展示logo

【1.2】手记防盗需求分析

【2】包名划分


    基础课程分包方式,每一个类包名,
    com.itheima.db
    com.itheima.db.dao 组件方式


    【1】业务逻辑划分包名
        icbc

        com.icbc.money
        com.icbc.meeting
        com.icbc.travel
    
    【2】组件划分(四大组件:activity,服务,广播,内容提供者(内容解析者))

        mobilesafe

        com.itheima.mobilesafe.activity
        com.itheima.mobilesafe.service


        数据库操作
        com.itheima.mobilesafe.db
        com.itheima.mobilesafe.db.dao

        工具类
        com.itheima.mobilesafe.utils

        自定义控件(android原有控件,不能满足需求),自定义组合控件
        com.itheima.mobilesafe.view
        com.itheima.mobilesafe.ui.widget

【3】【注意】项目上来之后首先要进行编码的更改,使用国际通用的utf-8编码方式

【是否作为一个库文件】

【4】svn提交代码&代码的下载:需要手动跟着做一遍

【4.1】首先安装服务器端和客户端的软件

【4.2】svn服务器创建仓库

【url地址的访问】

【4.3】将代码丢到代码仓库

【原则】自动编译生成的文件不需要提交;

 

 

 

 【有选择的add文件】

【对add的文件进行commit】

 

 【4.4】下载服务器上的源文件

 

【4.5】将项目导入eclipse中

 

【5】splash布局分析

【5.1】界面

【5.2】【注意】应用程序的包名是什么?

【5.3】AndroidManifest.xml清单文件的认识

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="com.itheima.mobilesafe74"
 4     android:versionCode="1"
 5     android:versionName="1.0" >
 6     <!-- android:versionCode 本地应用版本号1,版本号是2,有必要提示用户更新 -->
 7     <!-- android:versionName="1.0"  
 8             2.1.1:
 9             最后的一位:代表修复原有版本的bug
10             倒数第二位:更新部分功能
11             第一位:项目重大更新(代码重构,大部分功能添加,界面整体修改)-->
12             
13     <uses-sdk
14         android:minSdkVersion="8"
15         android:targetSdkVersion="17" />

【android:versionCode="1"】版本号;

【android:versionName="1.0"】 版本名称;

【注意】区分两者不一样;

【5.4】修改编译的版本sdk

【6】splash控件的创建

【6.1】修改类的包名

【6.2】将MainActivity移入重命名的包中并修改名称,作为启动的Activity界面;

 【6.3】布局

【相对布局或者帧布局】因为存在字符悬浮在Activity中的中央,所以使用这两种布局

【6.3.1】【布局修改名称】

【6.3.2】布局中背景图片的添加

【6.3.3】版本的描述

【6.4】文本框阴影效果

【说明】版本号中存在阴影

【美图秀秀作出的阴影效果】

【阴影效果的原理】文字向右向下平移几个像素,并且存在颜色

【注意】需要指定下面的参数:shadowRadius参数;

【6.5】进度条的添加

【说明】需要添加水平居中属性

【7】activity去头操作&保留高版本主题

【说明】需要去头

【第一种方式】存在缺点,如果多个Activity需要去头,需要多次书写该代码;

【在Android Studio中没有反应】

【第二种方式】通过清单配置文件,直接换掉整个应用的主题;因为修改的是父节点的配置主题,因此下面的子节点都会跟这整个主题的设置一致;

【在Android studio 中编译出错】

11-23 22:05:40.359: E/AndroidRuntime(3100): FATAL EXCEPTION: main
11-23 22:05:40.359: E/AndroidRuntime(3100): java.lang.RuntimeException: Unable to start activity ComponentInfo{it.oztaking.com.a01_mobilesafe/it.oztaking.com.activity.SplashActivity}: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2211)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.app.ActivityThread.access$600(ActivityThread.java:141)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.os.Handler.dispatchMessage(Handler.java:99)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.os.Looper.loop(Looper.java:137)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.app.ActivityThread.main(ActivityThread.java:5103)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at java.lang.reflect.Method.invokeNative(Native Method)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at java.lang.reflect.Method.invoke(Method.java:525)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at dalvik.system.NativeStart.main(Native Method)
11-23 22:05:40.359: E/AndroidRuntime(3100): Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.support.v7.app.AppCompatDelegateImplV9.createSubDecor(AppCompatDelegateImplV9.java:359)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.support.v7.app.AppCompatDelegateImplV9.ensureSubDecor(AppCompatDelegateImplV9.java:328)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.support.v7.app.AppCompatDelegateImplV9.setContentView(AppCompatDelegateImplV9.java:289)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:140)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at it.oztaking.com.activity.SplashActivity.onCreate(SplashActivity.java:13)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.app.Activity.performCreate(Activity.java:5133)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
11-23 22:05:40.359: E/AndroidRuntime(3100):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
11-23 22:05:40.359: E/AndroidRuntime(3100):     ... 11 more

【第三种方法】使用原理的主题,只配置其中的某一项;

  实际通过清单文件配置(方法2)的原理如下:配置了item选项;

【改进】只需要在自己的主题配置同样的一行item就可以了;

【这样做的优点】既可以继续使用高版本的AppThem中的效果,还可以完成“去头”的功能;

其实:在AppThem中的进度条较细,效果比较好看;而no_title的主题样式中的进度条较粗;通过这样的方法,既可以

保留高版本的效果,又可以达到“去头”的效果;

【最终的效果】

【8】获取版本名称并且展示

 1     /**
 2      * 获取版本名称:清单文件中
 3      * @return    应用版本名称    返回null代表异常
 4      */
 5     private String getVersionName() {
 6         //1,包管理者对象packageManager
 7         PackageManager pm = getPackageManager();
 8         //2,从包的管理者对象中,获取指定包名的基本信息(版本名称,版本号),传0代表获取基本信息
 9         try {
10             PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
11             //3,获取版本名称
12             return packageInfo.versionName;
13         } catch (Exception e) {
14             e.printStackTrace();
15         }
16         return null;
17     }
18 
19     /**
20      * 初始化UI方法    alt+shift+j
21      */
22     private void initUI() {
23         tv_version_name = (TextView) findViewById(R.id.tv_version_name);
24     }

1     /**
2      * 获取数据方法
3      */
4     private void initData() {
5         //1,应用版本名称
6         tv_version_name.setText("版本名称:"+getVersionName());
7     }
 1     @Override
 2     protected void onCreate(Bundle savedInstanceState) {
 3         super.onCreate(savedInstanceState);
 4         //去除掉当前activity头title
 5 //        requestWindowFeature(Window.FEATURE_NO_TITLE);
 6         setContentView(R.layout.activity_splash);
 7         
 8         //初始化UI
 9         initUI();
10         //初始化数据
11         initData();
12     }

【9】构建服务端json&utf-8无BOM编码

【功能】进度条检查更新,比对版本号(服务器版本号大于本地版本号,提示用户更新)

【9.1】需要首先获取本地的版本号,至少从1开始,如果是0,则说明存在问题;

 1     /**
 2      * 返回版本号
 3      * @return    
 4      * 非0 则代表获取成功
 5      */
 6     private int getVersionCode() {
 7         //1,包管理者对象packageManager
 8         PackageManager pm = getPackageManager();
 9         //2,从包的管理者对象中,获取指定包名的基本信息(版本名称,版本号),传0代表获取基本信息
10         try {
11             PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);
12             //3,获取版本名称
13             return packageInfo.versionCode;
14         } catch (Exception e) {
15             e.printStackTrace();
16         }
17         return 0;
18     }
 1     /**
 2      * 获取数据方法
 3      */
 4     private void initData() {
 5         //1,应用版本名称
 6         tv_version_name.setText("版本名称:"+getVersionName());
 7         //检测(本地版本号和服务器版本号比对)是否有更新,如果有更新,提示用户下载(member)
 8         //2,获取本地版本号
 9         mLocalVersionCode = getVersionCode();
10         //3,获取服务器版本号(客户端发请求,服务端给响应,(json,xml))
11         //http://www.oxxx.com/update74.json?key=value  返回200 请求成功,流的方式将数据读取下来
12         //json中内容包含:
13         /* 更新版本的版本名称
14          * 新版本的描述信息
15          * 服务器版本号
16          * 新版本apk下载地址*/
17         checkVersion();
18     }

【9.2】【约定json包的数据结构,对于现在更新的功能,需要包含四个功能即可】

//http://www.oxxx.com/update74.json?key=value  返回200 请求成功,流的方式将数据读取下来
        //json中内容包含:
        /* 更新版本的版本名称
         * 新版本的描述信息
         * 服务器版本号
         * 新版本apk下载地址*/

 

【json包】

【大括号】代表是一个json的对象;

【中括号】代表是一个json的数组;

【逗号】每条信息写完之后需要增加逗号结尾;

 

【json校验】

 

 

【保存json数据】

【将json文件放到tomcat服务器】

【开启tomcat服务器】

【说明】如果点击Tomcat界面的"x",服务器是没有关闭的;

【登录访问服务器上的json文件】

【9.3】请求网络数据&测试

【9.3.1】

 1 /**
 2      * 获取数据方法
 3      */
 4     private void initData() {
 5         //1,应用版本名称
 6         tv_version_name.setText("版本名称:"+getVersionName());
 7         //检测(本地版本号和服务器版本号比对)是否有更新,如果有更新,提示用户下载(member)
 8         //2,获取本地版本号
 9         mLocalVersionCode = getVersionCode();
10         //3,获取服务器版本号(客户端发请求,服务端给响应,(json,xml))
11         //http://www.oxxx.com/update74.json?key=value  返回200 请求成功,流的方式将数据读取下来
12         //json中内容包含:
13         /* 更新版本的版本名称
14          * 新版本的描述信息
15          * 服务器版本号
16          * 新版本apk下载地址*/
17         checkVersion();
18     }

 【注意】

【方式1】使用主机ip访问

【方式2】使用域名的方式,如www.baidu.com;这种方式之后可以不用再更改ip地址了;这是服务端的程序员做的工作;

【方式3】只限于模拟器和主机上的tomcat访问

【9.3.2】设置请求方式、获取响应码

 1     /**
 2      * 检测版本号
 3      */
 4     private void checkVersion() {
 5         new Thread(){
 6             public void run() {
 7                 //发送请求获取数据,参数则为请求json的链接地址
 8                 //http://192.168.13.99:8080/update74.json    测试阶段不是最优
 9                 //仅限于模拟器访问电脑tomcat
10                 Message msg = Message.obtain();
11                 long startTime = System.currentTimeMillis();
12                 try {
13                     //1,封装url地址
14                     URL url = new URL("http://10.0.2.2:8080/update74.json");
15                     //2,开启一个链接
16                     HttpURLConnection connection = (HttpURLConnection) url.openConnection();
17                     //3,设置常见请求参数(请求头)
18                     
19                     //请求超时
20                     connection.setConnectTimeout(2000);
21                     //读取超时
22                     connection.setReadTimeout(2000);
23                     
24                     //默认就是get请求方式,
25 //                    connection.setRequestMethod("POST");
26                     
27                     //4,获取请求成功响应码
28                     if(connection.getResponseCode() == 200){
29                         //5,以流的形式,将数据获取下来
30                         InputStream is = connection.getInputStream();
31                         //6,将流转换成字符串(工具类封装)
32                         String json = StreamUtil.streamToString(is);

【9.3.3】stream转换为string

新建StreamUtil类并书写静态方法streamToString,将流转化为String;

【源码】/mobilesafe74/src/com/itheima/mobilesafe74/utils/StreamUtil.java

 1 package com.itheima.mobilesafe74.utils;
 2 
 3 import java.io.ByteArrayOutputStream;
 4 import java.io.IOException;
 5 import java.io.InputStream;
 6 
 7 public class StreamUtil {
 8     /**
 9      * 流转换成字符串
10      * @param is    流对象
11      * @return        流转换成的字符串    返回null代表异常
12      */
13     public static String streamToString(InputStream is) {
14         //1,在读取的过程中,将读取的内容存储值缓存中,然后一次性的转换成字符串返回
15         ByteArrayOutputStream bos = new ByteArrayOutputStream();
16         //2,读流操作,读到没有为止(循环)
17         byte[] buffer = new byte[1024];
18         //3,记录读取内容的临时变量
19         int temp = -1;
20         try {
21             while((temp = is.read(buffer))!=-1){
22                 bos.write(buffer, 0, temp);
23             }
24             //返回读取数据
25             return bos.toString();
26         } catch (IOException e) {
27             e.printStackTrace();
28         }finally{
29             try {
30                 is.close();
31                 bos.close();
32             } catch (IOException e) {
33                 e.printStackTrace();
34             }
35         }
36         return null;
37     }
38 }

 

【输出查看接收的json数据是否正确】

【9.4】json解析过程

【原理】看见什么,解析什么

【良好的习惯】解析的字段直接复制粘贴,不要手敲,容易出错;

【源码】

【注意】确保tomcat服务器是开启的状态;

【解析的文字】

【9.5】断点调试json解析错过程

【使用工具】使用debug工具调试

【打断点】

【出现下面的框之后不要点击】

 

【出错结果的显示】直接从执行结果跳转到了异常,中间的代码没有显示;

【10】消息机制发送不同类型消息

 

【创建常量】常量变为大写的,并且给其赋值为常量的值;

10 消息机制发送不同类型消息

10.1 本地apk版本号和服务器版本号的对比

【本地和服务器的版本号的获取】

 

10.2【消息机制的创建】

【注意】匿名内部类的方法名不能写错;

 

【正确的写法】

 

【消息的携带】

 

【对消息的处理】

 

【消息处理分配为成员方法】

 

10.3【主界面class文件生成】

 

【主界面的清单文件的配置】

 

【在主界面拿自己已有的布局文件】

 

【创建主界面的布局文件】

 

10.4 导航界面跳转到主界面

【说明】在导航界面跳转完成之后,需要将导航界面销毁(调用finish()方法即可)

 

10.5 splash界面跳转到主界面的测试

【说明】splash跳转到主界面的触发是版本号一致才会跳转,因此需要将本地版本号和服务器版本号配置为一致;

 

【程序触发执行的步骤】①->④

 

 

 

 

【出现的问题】在程序执行之后瞬间跳转到主界面,没有在splash界面停留;

 

【原因】代码执行的太快

【解决办法】进行延时:延时4秒钟;

延时的时间是由两部分构成的:网络的延时和自己定的时间,总时间不要超过4秒钟;

 

【记录代码的运行时间】

 

【源码】

 

【效果】

 

11. 弹出对话框

【说明】对异常情况作出处理

【写代码注意】

【1】在调用弹吐司方法时,先写.show()方法;

【2】写流代码也是,先写close();

【3】数据库也一样,先写关闭数据库的方法;

11.1 工具类的封装

 

 

 

【下面获取上下文环境也可以】

 

 

11.2 异常的界面进入

【说明】即使存在异常,也不能阻止用户进入到程序的主界面;

 

12.更新版本的处理

【版本更新的流程图】

 

12.1 对话框的弹出

 

【注意】对话框的信息的弹出的参数的传递应该是this(指向的是splash界面),不能是上下文;

 

 

【报错】

 

【正确的写法】

 

12.2 弹出对话框的界面的效果

 

12.3 界面更新的内容的获取

 

 

12.4 界面更新的按钮的设置

 

 

【效果】

 

点击稍后再说之后跳转到了home界面;

 

13. xutils说明-多线程断点续传

 

【简介参考网址】

http://blog.csdn.net/smartbetter/article/details/51866792

14.下载方法使用

 

 

14.1 添加jar包

 

 

14.2 添加权限

 

14.3 对象的创建和使用

 

14.3.1 下载地址的获取

 

 

14.3.2 参数callback

 

14.4.4 下载方法的实现

 

    protected 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();

            //4,发送请求,传递参数(下载地址,下载应用放置位置)

            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 arg0, String arg1) {

                    Log.i(tag, "下载失败");

                    //下载失败

                }

                //刚刚开始下载方法

                @Override

                public void onStart() {

                    Log.i(tag, "刚刚开始下载");

                    super.onStart();

                }

               

                //下载过程中的方法(下载apk总大小,当前的下载位置,是否正在下载)

                @Override

                public void onLoading(long total, long current,boolean isUploading) {

                    Log.i(tag, "下载中........");

                    Log.i(tag, "total = "+total);

                    Log.i(tag, "current = "+current);

                    super.onLoading(total, current, isUploading);

                }

            });

           

        }

    }

15.打包生成APK

15.1打包生成APK

 

 

 

 

 

 

15.2 将程序放到服务器端

 

 

 

15.3 测试下载

 

 

 

【说明】会多次调用下载中的这个方法

 

 

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

16.1提示用户安装下载的apk文件

 

16.2 安装apk的系统界面 ,使用隐式意图打开界面

【查看apk安装的源码文件】

 

 

16.3 源码

 

17.程序安装出现的问题

 

 

 

【问题的解决】-重新打包生成的apk的签名文件

新生成1.0程序的签名文件和包名和2.0程序是一致的;

 

 

【使用adb安装】

 

 

【说明】更新之后的新的程序

 

18.注意的问题-非常重要

 

19.安装过程中点击取消的代码逻辑

19.1【点击返回上一步的逻辑】

 

【源码】进入到主界面

 

19.2 在安装界面点击取消之后的逻辑

 

【源码】现在情况是点击取消之后,进入到了splash界面,不停地在循环;

应该是出现主界面;

 

【源码及修改】

 

 

【安装出现的问题】因为签名不一致导致的问题;

 

【效果】点击取消之后进入到了主界面

 

posted @ 2017-11-22 23:18  OzTaking  阅读(573)  评论(0)    收藏  举报