【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界面,不停地在循环;
应该是出现主界面;
【源码及修改】
【安装出现的问题】因为签名不一致导致的问题;
【效果】点击取消之后进入到了主界面
浙公网安备 33010602011771号