尚硅谷15天Android基础笔记

前言:建议入门Android的小伙伴作为复习的时候用到,因为涉及比较多的细节知识,不利于你们的前期学习,视频地址:15天精讲精练Android核心技术

Android系统的基本了解

Android整体架构图:Android系统架构—–Android的系统体系架构 - xiaoluo501395377 - 博客园

手机尺寸的相关概念:手机尺寸相关的概念 +尺寸单位+关于颜色 - D(a/e)mon - 博客园
尚硅谷15天Android基础笔记_第1张图片

解决模拟器创建或启动问题:
尚硅谷15天Android基础笔记_第2张图片

Lunix常用命令:
尚硅谷15天Android基础笔记_第3张图片

常见的异常处理:

常见的异常:

  1. NullPointerException
    原因: 调用值为null的对象的方法或属性
  2. ClassCastException
    原因: 执行强制转换, 但类型匹配
  3. ActivityNotFoundException:
    原因: Activity没有找到, 很可能没有注册或注册不正确

基本常见异常的一般分析步骤:

  1. 确定异常类型: 从下向上找, 最好能找到cause by
  2. 确定出异常的行号: 找到当前应用的代码行号, 双击定位
  3. 分析, 打印Log, debug调试

四大组件

Activity

0.Activity是四大组件中唯一能与用户进行直接交互的应用组件
1.Intent(意图)是Activity,Service以及BroadcastReceiver这三大组件进行通信的信使
2.在Android中,系统用Task Stack (Back Stack)结构来存储管理启动的Activity对象
3.一个应用启动,系统就会为其创建一个对应的Task Stack来存储并管理该应用的Activity对象
4.只有最上面的任务栈的栈顶的Activity才能显示在窗口中
5.回调方法都是在主线程执行的
6.Activity只是控制和管理View, 真正显示和处理事件的是View本身来完成

面试题:在安卓中哪些用到反射?
1.配置文本中配置全类名(Activity,也就解释了为什么我们不需要使用new创建Activity啦)
2.布局文件定义标签(比如TextView)
3.显示意图:Intent(Context content,class c)

Service

  • 1.Service是一个应用组件, 它用来在后台完成一个时间跨度比较大的工作且没有关联任何界面
  • 2.服务的特点:
    • Service在后台运行,不用与用户进行交互
    • 即使应用退出, 服务也不会停止. (因为应用退出了,但Android系统分配的进程即内存区还在,所以Service还可以运行,但如果你把近期任务清理了,那么这个操作会把应用进程杀死,这样做的话应用才是真正死亡)
    • 在默认情况下,Service运行在应用程序进程的主线程(UI线程)中,如果需要在Service中处理一些网络连接等
    • 耗时的操作,那么应该将这些任务放在分线程中处理,避免阻塞用户界面

Service分类:

  • Local Service(本地服务)
    • Service对象与Serive的启动者在同个进程中运行, 两者的通信是进程内通信
1. startService(intent)
    第一次调用 : -->构造方法()-->onCreate()-->onStartCommand()
    (重要)后面再调用 : -->onStartCommand()
    stopService() : -->onDestory()
2. bindService(intent, serviceConnection)
    调用 : -->构造方法()-->onCreate()-->onBind()-->onServiceConnected()
    unbindService(): (中有当前Activity与Service连接)-->onUnbind()-->onDestroy()
  • Remote Service(远程服务)
    • Service对象与Service的启动者不在同一个进程中运行, 这时存在一个进程间通信的问题, Android专门为此设计了AIDL来实现进程间通信
AIDL
  • AIDL 每个应用程序都运行在自己的独立进程中,并且可以启动另一个应用进程的服务,而且经常需要在不同的进程间传递数据对象。
  • 在Android平台,一个进程不能直接访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元(比如说int ,被序列化的对象),并且有序的通过进程边界(即写的顺序跟读的顺序是一样的)。
  • 编写AIDL需要注意:
    • 1.接口名和aidl文件名相同.
    • 2.接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static.
    • 3.Aidl默认支持的类型包话java基本类型(int,long,boolean等)和(String,List,Map,
      CharSequence),使用这些类型时不需要import声明.对于List和Map中的元素类型必须是
      Aidl支持的类型.如果使用自定义类型作为参数或返回值,自定义类型必须实现Parcelable接口(其中重写的方法writeToParcel用作将当前对象的属性数据写到Parcel包对象中(也就是打包), public static final Parcelable.Creator<自定义类型> CREATOR = new Parcelable.Creator<>() {}用作添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口,其中这个方法内部有一个解包: 读取包中的数据并封装成对象).
    • 4.自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中.
    • 5.在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数.
    • 6.Java原始类型默认的标记为in,不能为其它标记.

1.Android Studio中如何创建AIDL - 技术丶从积累开始 - 博客园
2.AIDL-小白成长记-入门视频教程-慕课网

面试题

  • 如何区别Service与Activity?

    • Activity:
      • Activity对应一个界面
      • 应用退出, Activity对象就会死亡
      • 应用再次进入, 启动的Activity对象是重新创建的
    • Service
      • 不与任何界面关联
      • 应用退出, Service仍在运行
      • 应用再次进入, 启动的Service还是前面运行的Service对象(Service只有一个对象)
  • 区别Service与Thread

    • Service
      • 用来在后台完成一个时间跨度比较大的工作的应用组件
      • Service的生命周期方法运行在主线程, 如果Service想做持续时间比较长的工作, 需要启动一个分线程(Thread)
      • 应用退出: Service不会停止
      • 应用再次进入: 可以与正在运行的Service进行通信
    • Thread
      • 用来开启一个分线程的类, 做一个长时间的工作
      • Thread对象的run()在分线程执行
      • 应用退出: Thread不会停止,
      • 应用再次进入: 不能再控制前面启动的Thread对象

BroadcastReceiver

广播接收者的生命周期是很短的,一般是10s内,即处理完它的onReceive()方法就死亡

理解广播与广播接收器

  • 广播事件处理属于系统级的事件处理(一般事件处理是属于View级的事件处理)
  • 一个应用可以在发生特定事件时发送Broadcast, 系统中任何应用只要注册了对应Receiver就会接收到此Broadcast
  • 一个应用如果对某个广播感兴趣, 就可以注册对应的Receiver来接收广播
  • 广播事件机制是应用程序(进程间)之间通信的一种手段
  • 动态广播跟静态广播注册的对象不同:动态注册的是对象,而静态注册的是类

相关API

  • Context

    • sendBroadcast(Intent intent) : 发送一般广播
    • sendOrderedBroadcast(Intent intent) : 发送有序广播
    • registerReceiver(receiver, intentFilter) : 注册广播接收器
    • unRegisterReceiver(receiver) : 解注册广播接收器
  • BroadcastReceiver

    • onReceive(Context context, Intent intent) : 接收到广播的回调
    • abortBroadcast() : 中断广播的继续传播
    • boolean isOrderedBroadcast() : 判断是否是有序广播

区别静态注册与动态注册

尚硅谷15天Android基础笔记_第4张图片

区别无序广播跟有序广播

尚硅谷15天Android基础笔记_第5张图片

ContentProvider

概念

  • ContentProvider是四大应用组件之一
  • 当前应用使用ContentProvider将数据库表数据操作暴露给其它应用访问
  • 其它应用需要使用ContentResolver来调用ContentProvider的方法
  • 它们之间的调用是通过Uri来进行交流的

1.ContentProvider 浅谈 - 推酷
2.ContentProvider从入门到精通 - 简书

UI

UI全称user interface, 意为:用户界面
1.UI由View和ViewGroup组成
2.View类是所有视图(包括ViewGroup)的根基类
3.View在屏幕上占据一片矩形区域, 并会在上面进行内容绘制
4.ViewGroup包含一些View或ViewGroup, 用于控制子View的布局

响应事件:

当用户通过手指触摸UI时, 系统会自动创建对应的Event对象(事件源一般是视图对象,而不是视图类)
Android中提供了多种方式拦截处理不同类型的事件
视图本身就可以处理发生在该视图上的事件

尚硅谷15天Android基础笔记_第6张图片

Android提供了很多不同类型的事件监听器接口

View.OnClickListener:  onClick() 
View.OnLongClickListener: onLongClick()
View.OnTouchListener: onTouch() 
View.OnCreateContextMenuListener: onCreateContextMenu()
View.OnFocusChangeListener:  onFocusChange()
View.OnKeyListener:  onKey()

给视图添加事件监听的方式:view.seton…Listener(listener),其中listener可以为this,匿名内部类以及成员变量

1.子线程不可以直接更新UI(Toast也是其中一种UI),因为子线程缺少Looper.prepare()方法,
2.与进度相关的控件之所以可以在子线程更新UI,是因为这类控件在它源码内置了已经添加了Handler消息机制
3.runOnUiThread可以在主线程或子线程执行,但它参数的匿名内部类Runnable中的run方法一定是在主线程执行的
4.图片imageView中的background背景图片显示的效果=src前景图片+属性fitXY
5.gravity控制是当前视图的内容/子view,layout_gravity控制当前视图自己

获得日历对象

//创建日历对象
Calendar calendar = Calendar.getInstance();
//得到当前的年月日
final int year = calendar.get(Calendar.YEAR);//得到年份
final int monthOfYear = calendar.get(Calendar.MONTH);//月
final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);//得到日
int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY); //得到小时
int minute = calendar.get(Calendar.MINUTE); //得到分钟

RelativeLayout

  • 1.相对布局: 用来控制其子View以相对定位的方式进行布局显示
  • 2.相对布局是最灵活, 最强大,也是学习难度最大的布局
  • 3.相对布局相关属性比较多:

    • 兄弟视图之间: 同方向对齐, 反方向对齐
      尚硅谷15天Android基础笔记_第7张图片

    • 与父视图之间: 同方向对齐, 居中
      尚硅谷15天Android基础笔记_第8张图片

    • 全部布局都有的属性
      尚硅谷15天Android基础笔记_第9张图片

属性的划分

  • 针对任何View的属性
    • 常用的最基本属性
    • 内边距属性 padding
    • 外边距属性 margin
  • 只针对RelativeLayout的属性
    • 反方向对齐属性 to/above/below
    • 同方向对齐属性 align
    • 相对父视图的属性 alignparent/center
  • 只针对LinearLayout的属性
    • 权重属性 weight
    • 方向属性 oritation

ListView

  • ListView是一种用来显示多个可滑动项(Item)列表的的ViewGroup,但它不可以添加子view了
  • 需要使用Adapter将集合数据和每一个Item所对应的布局动态适配到ListView中显示
  • 显示列表: listView.setAdapter(adapter)
  • 更新列表: adapter.notifyDataSetChanged()
    尚硅谷15天Android基础笔记_第10张图片

适配器

  • ArrayAdapter: 显示最简单的列表(文本)
    集合数据为List或String[]
  • SimpleAdapter: 显示复杂的列表
    集合数据必须是List

ListView引发的问题

  • 问题1: 对于联网获取列表数据, 如果数据量太大(比如超过100000条甚至更多), 一次获取出来显示, 太慢太耗流量: 第四层优化(分页)
    • 1.Android的ListView分页功能-慕课网
    • 自己做: 通过Scroll监听listView.setonScrollListener(scrollListener), 当到达底部时加载下一页列表数据并显示
    • 使用第三方开源框架: Android-PullToRefresh或其它
    • 2.Android的ListView下拉刷新-慕课网
  • 问题2: 对于联网获取列表数据, 如果包含图片数据, 每次都请求获取显示, 太慢太耗流量: 第三层优化(三级缓存)

RecyclerView

  • 明日之星-RecyclerView-慕课网

样式(Style)

  • 理解: 多个视图属性的集合, 在写布局时, 当多个视图有不少相同的属性时, 可以把这些相同的属性放在一起在styles.xml中定义成一个Style, 而在布局文件中使用@style/style_name统一引用
  • 作用: 复用视图标签属性
  • 目标: 针对的是窗口中的某些视图

主题(theme)

  • 理解:
    • 主题的本质也是style
    • 在styles.xml中定义, 在manifest.xml中引用
  • 作用: 复用视图标签属性
  • 目标: 针对整个应用或某个Activity的界面

事件机制

MotionEvent触摸事件

  • 1.MotionEvent : 触屏事件

    • int ACTION_DOWN=0 : 代表down
    • Int ACTION_MOVE=2 ; 代表move
    • Int ACTION_UP=1 : 代表up
    • getAction() : 得到事件类型值
    • getX() : 得到事件发生的x轴坐标(相对于当前视图)
    • getRawX() :得到事件发生的x轴坐标(相对于屏幕左顶点)
    • getY() : 得到事件发生的y轴坐标(相对于当前视图)
    • getRawY() :得到事件发生的y轴坐标(相对于屏幕左顶点)
  • 2.Activity

    • boolean dispatchTouchEvent(MotionEvent event) : 分发事件
    • boolean onTouchEvent(MotionEvent event) : 处理事件的回调
  • 3.View
    • boolean dispatchTouchEvent(MotionEvent event) : 分发事件
    • boolean onTouchEvent(MotionEvent event) : 处理事件的回调方法
    • void setOnTouchListener(OnTouchListener l) : 设置事件监听器
    • void setOnClickListener(OnClickListener l) : 设置点击监听
    • void setOnLongClickListener(OnLongClickListener l) : 设置长按监听
    • void setOnCreateContextMenuListener(OnCreateContextMenuListener l) : 用于创建菜单
  • 4.ViewGroup
    • boolean dispatchTouchEvent(MotionEvent ev) : 分发事件
    • boolean onInterceptTouchEvent(MotionEvent ev) : 拦截事件的回调方法(在分发的时候发生的)

触摸事件的分发与处理

  • 1.事件产生的顺序为: down–>move–>move…—>up
  • 2.事件对象被系统创建后, 首先会调用对应Activity对象的dispatchTouchEvent()进行分发
  • 3.down在分发给视图对象的过程中要确定消费者(onTouchEvent()返回true),如果都返回false, 那事件的消费者只能是Activity了
  • 4.后面的move和up事件, 将事件分发给消费者(可能是视图对象,也可能是Activity)处理, 如果视图不消费, 直接交给Activity处理消费
  • 5.每个事件都需要有一个消费者

1.Android事件分发机制完全解析,带你从源码的角度彻底理解(上) - 郭霖的专栏 - 博客频道 - CSDN.NET
2.关于 android 的 view.getLeft(), getRight(), getTop(), getBottom() 的一些疑惑(坑)解答 - 指尖下的幽灵 - 博客园

KeyEvent按键操作

  • 1.操作的基本类型
    • down : 手指按下
    • up : 手指从按键上离开
  • 2.按键操作的顺序: downdowndown…—>up
  • 3.对按键的任何一个操作, 系统都会创建一个KeyEvent对象来对应这个操作
  • 4.按键的长按监听: down之后一定时间还没有up时会触发长按监听回调
相关API
  • KeyEvent
    • int ACTION_DOWN = 0 : 标识down的常量
    • int ACTION_UP = 1 : 标识up的常量
    • int getAction() : 得到事件类型
    • int getKeyCode() : 得到按键的keycode(唯一标识)
    • startTracking() : 追踪事件, 用于长按监听
  • Activity
    • boolean dispatchKeyEvent(KeyEvent event) : 分发事件
    • boolean onKeyDown(int keyCode, KeyEvent event) : 按下按键的回调
    • boolean onKeyUp(int keyCode, KeyEvent event) : 松开按键的回调
    • boolean onKeyLongPress(int keyCode, KeyEvent event) : 长按按键的回调

自定义控件

  • View类是所有用来构建用户界面的组件的基类,
  • ViewGroup是View的一个子类,是各种布局(比如FrameLayout)的基类,因为继承了ViewManager接口,实现了 添加子ViewaddView(View v),删除子ViewremoveView(View v),更新子View的布局updateViewLayout(View v)
  • 因为ViewGroup实现了ViewManager才可以用子View,但View类没有实现该接口,所以View类都没有子View的
  • setContentView(R.layout.activity_main);其实是设置id为content的布局(布局是FrameLayout)的子View,即是把activity_main作为一个子View
  • 每一个应用最初的View是DecorView(父类是FrameLayout)
  • Activity只是控制和管理View, 真正显示和处理事件的是View本身来完成

View生命周期

  • 创建对象
    • new MyView(context)-调用的是构造方法Xxx(Context context)
    • 加载布局文件-必须有自定义View的全类名标签-调用的是构造方法Xxx(Context context, AttributeSet set)
    • onFinishInflate()只有加载布局文件才会调用,即只有执行构造方法Xxx(Context context, AttributeSet set)或者Xxx(Context context, AttributeSet set,int defaultStyle)才会执行这方法,重写这方法是为了得到子View对象
    • onAttachedToWindow() 两种创建对象(new还是布局)方法都会调用,重写这方法是为了得到子View对象
    • Activity的onResume()执行之后才会进入后面的流程-测量,布局,绘制,事件处理以及死亡
  • 测量-计算并确定视图的大小(width/height)
    • measure(),系统在此方法中测量计算出当前视图的宽高,由于这个方法是final类型,所以不可以被重写
    • onMeasure()-mearure()方法调用它
    • 当mearure()中计算出的视图的宽高就会调用此方法, 在此方法默认保存的视图宽高
    • 重写它, 做我们自己的工作, 比如得到当前视图测量的宽高, 保存我们指定的宽度
  • 布局-确定视图显示的坐标(left, top, right, bottom)
    • layout(l, t, r, b) 只会调用视图对象的此方法, 指定其新的显示位置,不会重写此方法,作用在自己身上
    • onLayout()重写它, 在layout()的过程中, 如果视图的位置/强制重新布局就会调用此方法,作用在子View (一般ViewGroup重写这个方法控制子View位置)
    • 强制重新布局-view.requestLayout()
  • 绘制-画出视图的样子
    • draw()
      • 绘制视图通用的部分
      • 确定绘制的流程
      • 一般不会重写此方法
    • onDraw()
      • 重写此方法,绘制自己需要的样子(View一般会重写,但ViewGroup不会重写)
      • 一些具体的View类(TextView/ImageView)都重写了此方法
    • 强制重绘
      • invalidate()只能在主线程执行
      • postInvalidate()可以在主线程或分线程执行
  • 事件处理
    • 流程方法
      • dispatchTouchEvent()分发事件,从外向里一层一层分发, 分发到事件发生的最里面的视图对象
      • boolean onInterceptTouchEvent()拦截请求, 只有return true才拦截成功,如果事件被拦截,事件不会再向内层分发, 交给当前的视图处理
      • boolean onTouchEvent()处理事件,消费事件的话需要条件: return true
      • requestDisallowInterceptTouchEvent(true)反拦截
      • view.getParent().requestDisallowInterceptTouchEvent(true)
    • 事件机制
      • 分发 将TouchEvent对象从Activity对象开始, 由外向内分发给对应的布局和子View对象
      • 处理
      • 回调OnTouchListener的boolean onTouch()
      • 回调boolean onTouchEvent()
      • 消费 回调方法返回true
      • 拦截 onInterceptTouchEvent()执行返回true,如果返回true, TouchEvent就不会再传入子View对象
      • 反拦截 view.getParent().requestDisallowInterceptTouchEvent(true),使父View不能再拦截, 事件就会分发到当前View对象
  • 死亡
    • 什么时候会死亡?
    • 视图对象被移除
    • Activity死亡之前
    • 流程方法
    • onDetachedFromWindow()

数据存储

尚硅谷15天Android基础笔记_第11张图片

SharedPreferences存储

  • 1.SP存储专门用来存储一些单一的小数据
  • 2.存储数据的类型: boolean, float, int, long, String
  • 3.数据保存的路径: /data/data/packageName/shared_prefs/yyy.xml
  • 4.可以设置数据只能是当前应用读取, 而别的应用不可以
  • 5.应用卸载时会删除此数据

sp存储简解:android SharedPreferences的用法 - sangxb - 博客园

手机内部file存储

  • 1.应用运行需要的一些较大的数据或图片可以用文件保存的手机内部
  • 2.文件类型: 任意
  • 3.数据保存的路径: /data/data/projectPackage/files/
  • 4.可以设置数据只能是当前应用读取, 而别的应用不可以
  • 5.应用卸载时会删除此数据

步骤:
1.读取文件
FileInputStream fis = openFileInput(“logo.png”);
2.保存文件
FileOutputStream fos = openFileOutput(“logo.png”, MODE_PRIVATE)
3.得到files文件夹对象
File filesDir = getFilesDir();
4.操作asserts下的文件
得到AssetManager : context.getAssets();
读取文件: InputStream open(filename);
5.加载图片文件:Bitmap BitmapFactory.decodeFile(String pathName) // .bmp/.png/.jpg

注意:
1.Drawable是可以描绘的图片,不等同与Bitmap,因为Drawable还包括shape等
2.bitmap对应的是bmp,jpg,png格式的图片

事例代码:
1.把assets文件夹的图片存储到包名下的files目录

        //1. 得到InputStream-->读取assets下的logo.png
            //得到AssetManager 
        AssetManager manager = getAssets();
            //读取文件
        InputStream is = manager.open("logo.png");
        //2. 得到OutputStream-->/data/data/packageName/files/logo.png
        FileOutputStream fos = openFileOutput("logo.png", Context.MODE_PRIVATE);
        //3. 边读边写
        byte[] buffer = new byte[1024];
        int len = -1;
        while((len=is.read(buffer))!=-1) {
            fos.write(buffer, 0, len);
        }
        fos.close();
        is.close();
        //4. 提示
        Toast.makeText(this, "保存完成", 0).show();

2.把包名内的files文件夹下log.png显示在ImageView上

        //图片的路径 /data/data/packageName/files/logo.png
        //1. 得到图片文件的路径
            // /data/data/packageName/files
        String filesPath = getFilesDir().getAbsolutePath();
        String imagePath = filesPath+"/logo.png";
        //2. 读取加载图片文件得到bitmap对象
        Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
        //3. 将其设置到imageView中显示
        //ImageView iv_if = (ImageView) findViewById(R.id.iv_if);
        iv_if.setImageBitmap(bitmap);

手机外部file存储

  • 1.应用运行用到的数据文件(如图片)可以保存到sd卡中
  • 2.文件类型: 任意
    • 数据保存的路径:
      • 路径1: /storage/sdcard/Android/data/packageName/files/(使用getExternalFilesDir)
      • 路径2: /storage/sdcard/xxx/(使用Environment.getExternalStorageDirectory())
        路径1 :其它应用可以访问,应用卸载时删除
        路径2 : 其它应用可以访问, 应用卸载时不会删除
        必须保证sd卡挂载在手机上才能读写, 否则不能操作

Environment : 操作SD卡的工具类

  • 1.得到SD卡的状态:Environment.getExternalStorageState()
  • 2.得到SD卡的路径:Environment.getExternalStorageDirectory()
    SD卡可读写的挂载状态值:Environment.MEDIA_MOUNTED
  • 3.context. getExternalFilesDir():
    得到/mnt/sdcard/Android/data/pageckage_name/files/xxx.txt
  • 4.操作SD卡的权限:android.permission.WRITE_EXTERNAL_STORAGE
    public void save(View v) throws IOException {
        //1. 判断sd卡状态, 如果是挂载的状态才继续, 否则提示
        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            //2. 读取输入的文件名/内容
            String fileName = et_of_name.getText().toString();
            String content = et_of_content.getText().toString();
            //3. 得到指定文件的OutputStream
                //1).得到sd卡下的files路径
            String filesPath = getExternalFilesDir(null).getAbsolutePath();
                //2).组成完整路径
            String filePath = filesPath+"/"+fileName;
                //3). 创建FileOutputStream
            FileOutputStream fos = new FileOutputStream(filePath);
            //4. 写数据 
            fos.write(content.getBytes("utf-8"));
            fos.close();
            //5. 提示
            Toast.makeText(this, "保存完成", 0).show();
        } else {
            Toast.makeText(this, "sd卡没有挂载", 0).show();
        }

    }

    public void read(View v) throws Exception {

        // 1. 判断sd卡状态, 如果是挂载的状态才继续, 否则提示
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            // 2. 读取输入的文件名
            String fileName = et_of_name.getText().toString();
            // 3. 得到指定文件的InputStream
                // 1).得到sd卡下的files路径
            String filesPath = getExternalFilesDir(null).getAbsolutePath();
                // 2).组成完整路径
            String filePath = filesPath + "/" + fileName;
                // 3). 创建FileInputStream
            FileInputStream fis = new FileInputStream(filePath);
            // 4. 读取数据, 成String
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = -1;
            while((len=fis.read(buffer))!=-1) {
                baos.write(buffer, 0, len);
            }
            String content = baos.toString();

            // 5. 显示
            et_of_content.setText(content);
        } else {
            Toast.makeText(this, "sd卡没有挂载", 0).show();
        }
    }

    //  /storage/sdcard/atguigu/xxx.txt
    public void save2(View v) throws IOException {
        //1. 判断sd卡状态, 如果是挂载的状态才继续, 否则提示
        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            //2. 读取输入的文件名/内容
            String fileName = et_of_name.getText().toString();
            String content = et_of_content.getText().toString();
            //3. 得到指定文件的OutputStream
                //1). /storage/sdcard/
            String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath();
                //2). /storage/sdcard/atguigu/(创建文件夹)
            File file = new File(sdPath+"/atguigu");
            if(!file.exists()) {
                file.mkdirs();//创建文件夹
            }
                //3). /storage/sdcard/atguigu/xxx.txt
            String filePath = sdPath+"/atguigu/"+fileName;
                //4). 创建输出流
            FileOutputStream fos = new FileOutputStream(filePath);
            //4. 写数据 
            fos.write(content.getBytes("utf-8"));
            fos.close();
            //5. 提示
            Toast.makeText(this, "保存完成", 0).show();
        } else {
            Toast.makeText(this, "sd卡没有挂载", 0).show();
        }
    }

    public void read2(View v) throws Exception {
        // 1. 判断sd卡状态, 如果是挂载的状态才继续, 否则提示
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            // 2. 读取输入的文件名
            String fileName = et_of_name.getText().toString();
            // 3. 得到指定文件的InputStream
            String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath();
            String filePath = sdPath+"/atguigu/"+fileName;
            FileInputStream fis = new FileInputStream(filePath);
            // 4. 读取数据, 成String
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = -1;
            while((len=fis.read(buffer))!=-1) {
                baos.write(buffer, 0, len);
            }
            String content = baos.toString();
            fis.close();
            // 5. 显示
            et_of_content.setText(content);
        } else {
            Toast.makeText(this, "sd卡没有挂载", 0).show();
        }
    }

选择内部文件与外部文件存储考虑的要素:
1). 存储空间的大小(内部文件小一点)
2). 是否是私有的(内部文件私有而且卸载时候自动删除,外部文件一般对外开放而且应用卸载时候与其无关)
3). 应用卸载是否自动删除
一般包名下的都是会自动删除的,无论是内部还是外部文件:
即: /data/data/packageName/shared_prefs/yyy.xml
/storage/sdcard/Android/data/packageName/files/

sqilte数据库存储

  • 概念:应用运行需要保存一系列有一定结构的数据, 比如说公司员工信息
  • 文件类型: .db
  • 数据保存的路径: /data/data/projectPackage/databases/xxx.db
  • 默认情况下其它应用不能访问, 当前应用可以通过ContentProvider提供其它应用操作
  • 应用卸载时会删除此数据

Sqlite数据库命令行

adb shell  进入系统根目录

cd data/data/…/databases : 进入包含数据库文件的文件夹下

sqlite3 contacts2.db : 使用sqlite3命令连接指定的数据库文件, 进入连接模式

 .help : 查看命令列表

.tables : 查看所有表的列表

执行insert/delete/update/select语句

.exit : 退出数据库连接模式

Ctrl + C : 直接退出sell模式

Sqlite建表

  • Sqlite操作数据库的sql语句基本与mysql一样, 但需要注意下面2个点:
    最大的不同在于创建表时可以不用指定字段类型, Sqlite可以适时的自动转换, 但除varchar类型外最好指定类型
  • Sqlite中的主键最名称建议使用_id
相关API
  • SQLiteOpenHelper: 数据库操作的抽象帮助类
    尚硅谷15天Android基础笔记_第12张图片

  • SqliteDatabase: 代表与数据库的连接的类
    尚硅谷15天Android基础笔记_第13张图片

  • Cursor : 包含所有查询结果记录的结果集对象(光标,游标)
    尚硅谷15天Android基础笔记_第14张图片

远程服务器存储

  • 对于联网的APP来说, 可能需要通过请求向服务器提交请求数据, 也可能需要从服务器端获取数据显示
  • 如何编码实现客户端与服务器端的交互呢?
    • JDK内置的原生API
    • HttpUrlConnection
    • Android内置的包装API
    • HttpClient 浏览器(Android6.0已经废弃了)
    • 异步网络请求框架
    • Volley:特别适合数据量不大但是通信频繁的场景: 带图片的列表
    • Xutils
    • 注意:
    • 访问网络, 需要声明权限: android.permission.INTERNET
    • 访问网络的程序必须在分线程执行

Volley

  • RequestQueue : 请求队列, 会自动执行队列中的请求

    • Volley. newRequestQueue(context) : 创建一个请求队列
    • addReqeust(Request reqeust) : 将请求添加到请求队列
  • Request: 代表请求的接口

    • StringRequest : 获取字符串结果的请求
    • JsonRequest : 获取Json数据结果的请求
    • ImageRequest : 获取图片结果的请求

Android Volley完全解析(一),初识Volley的基本用法 - 郭霖的专栏 - 博客频道 - CSDN.NET

消息机制与异步任务

消息机制

原理图一:
尚硅谷15天Android基础笔记_第15张图片

原理图二:
尚硅谷15天Android基础笔记_第16张图片

Message进程间通信的数据载体

    携带数据
     public int what;

     public int arg1; 
     public int arg2;
     public Object obj;

     long when;  //保存消息需要被处理的时间
     Handler target; //处理消息的handler
     Runnable callback;  //可以用来处理消息的回调对象
     Message next; //保存下一个message对象的引用(用来实现链表)
     static Message sPool; //消息池(缓存可复用的message对象)

     Message obtain() 使用了sPool

Handler-进程间通信的工具

  • 作用:发送消息,处理消息,移除消息
    sendMessage(Message msg)
    sendMessageDelayed(Message msg, long delayMillis)
    sendMessageAtTime(Message msg, long uptimeMillis) 
     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//发送消息的handler就是处理消息的handler
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
      }



   public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
           message.callback.run();//如果message有回调监听对象, 让它处理消息
        } else {
            if (mCallback != null) {
        //如果Handler中设置了回调监听器, 就调用它的处理消息的方法
                if (mCallback.handleMessage(msg)) {
            //如果返回true, 就结束
            //如果返回false, 还会调用handleMessage()
                    return;
                }
            }
        //如果上面都没有处理, 调用此方法处理
            handleMessage(msg);
        }
    }

Handler主要用于做什么工作?
1.线程间通信(子线程间切换到主线程运行)
2.延迟的工作
3.定时循环工作

MessageQueue -存储消息的以message的when排序优先级队列

  • 存储消息的以message的when排序优先级队列
  • 最终的结果:消息队列是按when来排序的
    boolean enqueueMessage(Message msg, long when) {//将消息添加到消息队列中

        msg.when = when; 
        将消息添加到消息队列
        唤醒Looper

Looper-循环器

  • 从MessageQueue中获取当前需要处理的消息,并交给Handler处理
    public static void loop() {

         for (;;) {//死循环
            Message msg = queue.next(); //从消息队列中取消息, 如果没有得到合适就会进入等待状态

            msg.target.dispatchMessage(msg);//Handler分发并处理消息

            msg.recycle();//回收消息(清理数据,保存到消息池中)
        }

    }

异步任务?

  • 逻辑上: 以多线程的方式完成的功能需求
  • API上: 指AsyncTask类

AsyncTask的理解

  • 在没有AsyncTask之前, 我们用Handler+Thread就可以实现异步任务的功能需求
  • AsyncTask是对Handler和Thread的封装, 使用它更编码更简洁,更高效
  • AsyncTask封装了ThreadPool, 比直接使用Thread效率要高

相关API

尚硅谷15天Android基础笔记_第17张图片
注意:publishProgress()方法是在WorkerThread()执行的,一般写在doInBackground方法里面,当这个方法执行完毕之后,onProgressUpdate方法便会自动执行的啦

尚硅谷15天Android基础笔记_第18张图片

Json数据

  • JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式
  • 本质就是具有特定格式的字符串
  • JSON数据已经是客户端与服务器端交互的最常用的选择, 已经很少使用xml来进行数据交互了

Json数据格式

  • 整体结构:
    • Json数组 : [ ]
    • Json对象: { }
  • Json数组的结构: [value1, value2, value3]
  • Json对象的结构: {key1:value1, key2:value2, key3:value3}
  • key的数据类型: 字符串
  • value的数据类型: 数值,字符串,null,json数组 [],json对象 {}
  • 例子:
    [1, “ab”,[], {“n”:123, “b”:”abc”}] [1, “a”:3]
    {“name”:”TOM”, “age”:12} {“aa”:“a”, 3}
    其中”abc”:3可以理解成键值对HashMap封装的,键是abc,值是3

相关API

  • Android原生API:
    • JsonObject : json对象 { }
    • JSONObject(String json) : 将json字符串解析为json对象
    • Xxx getXxx(String name) : 根据name, 在json对象中得到对应的Value
    • JsonArray : json数组 []
    • JSONArray(String json) : 将json字符串解析为json数组
    • int length() : 得到json数组中元素的个数
    • Xxx getXxx(int index) : 根据下标得到json数组中对应的元素数据
  • Gson框架API
    • Gson : 能解析json数据的类
    • Gson() : 构造对象的方法
    • String toJson(Object src) : 将对象转换为对应格式的json字符串
    • T fromJson(String json, Type typeOfT) : 解析Json字符串, 得到对象
    • TypeToken : 用来得到Type的类
    • protected TypeToken() : 受保存的构造方法
    • Type getType() : 得到类型对象

图片的三级缓存

Android 图片三级缓存之内存缓存(告别软引用(SoftRefrerence)和弱引用(WeakReference)) - fancychendong的专栏 - CSDN博客

尚硅谷15天Android基础笔记_第19张图片

  1. 动态显示列表中的图片:Bitmap—>手机本地的图片文件—>服务器端的图片文件
1). 图片的三级缓存
    一级缓存: 内存缓存, 缓存的是bitmap对象, 用Map<String, Bitmap>结构保存, key是url
    二级缓存: 本地(sd卡)缓存, 缓存的是图片文件,  /storage/sdcard/Android/data/packageName/files/图片文件名(xxx.jpg)
    三级缓存: 远程服务器缓存, 缓存的是图片文件, 远程服务器上的应用中
2). 如何使用三级缓存?  -----如何根据图片的url动态显示图片?
    String iamgePath = http://192.168.10.165:8080//L05_Web/images/f10.jpg和ImageView对象
    1). 根据url从一级缓存中取对应的bitmap对象
        如果有, 显示(结束)
        如果没有, 进入2)
    2). 从二级缓存中查找: 得到文件名并在sd卡的缓存目录下加载对应的图片得到Bitmap对象
        如果有: 显示, 缓存到一级缓存中(结束)
        如果没有, 进入3)
    3). 显示代表提示正在加载的图片, 启动分线程联网请求得到Bitmap对象
            如果没有: 显示提示错误的图片(结束)
            如果有:
                显示
                缓存到一级缓存
                缓存到二级缓存

2 . 在ListView使用图片三级缓存会存在图片闪动的bug

    1). 原因
        converView被复用了
    2). 解决
        a. 每次getView()都将图片的url保存到ImageView上: imageView.setTag(imagePath)
        b. 在分线程准备请求服务器加载图片之前, 比较准备加载图片的url与ImageView中保存的最新图片的url是同一个,
            如果不是同一个, 当前加载图片的任务不应该再执行
            如果相同, 继续执行加载远程图片
        c. 在主线程准备显示图片之前, 比较加载到图片的url与ImageView中保存的最新图片的url是同一个
            如果不是同一个, 不需要显示此图片
            如果相同, 显示图片

三级缓存主要代码:http://pan.baidu.com/s/1bKCNq2 密码: 91py (里面有相关的源码以及配套的服务器,使用前麻烦看看说明哈)
尚硅谷15天Android基础笔记_第20张图片

相关文章

0.Android高效加载大图、多图解决方案,有效避免程序OOM - 郭霖的专栏 - CSDN博客
1.Android ListView工作原理完全解析,带你从源码的角度彻底理解 - 郭霖的专栏 - CSDN博客
2.Android ListView异步加载图片乱序问题,原因分析及解决方案 - 郭霖的专栏 - CSDN博客
3.Android DiskLruCache 源码解析 硬盘缓存的绝佳方案 - Hongyang - CSDN博客
不错的文章:Android高效加载图片和缓存策略LRU,DiskLRU - Houson_c的博客 - CSDN博客

LruCache实现

  • 缓存淘汰算法–LRU算法(java代码实现) - CSDN博客

Animation动画

  • Android中提供了两种实现动画的方式:
    • 纯编码的方式
    • Xml配置的方式

Animation的公用功能

  • setDuration(long durationMillis) : 设置持续时间(单位ms)
  • setStartOffset(long startOffset) : 设置开始的延迟的时间(单位ms)
  • setFillBefore(boolean fillBefore) : 设置最终是否固定在起始状态
  • setFillAfter(boolean fillAfter) : 设置最终是否固定在最后的状态
  • setAnimationListener(AnimationListener listener) : 设置动画监听
  • 坐标类型:
    • Animation.ABSOLUTE
    • Animation.RELATIVE_TO_SELF
    • Animation.RELATIVE_TO_PARENT
  • 启动动画 : view.startAnimation(animation);
  • 结束动画: view.clearAnimation()
  • 动画监听器 : AnimationListener
    • onAnimationStart(Animation animation) : 动画开始的回调
    • onAnimationEnd(Animation animation) : 动画结束的回调
    • onAnimationRepeat(Animation animation) : 动画重复执行

Interpolator属性的使用

  • Interpolator 被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果accelerated(加速),decelerated(减速),repeated(重复)等。
    • @android:anim/linear_interpolator : 线性变化
    • @android:anim/accelerate_interpolator : 加速变化
    • @android:anim/decelerate_interpolator : 减速变化
    • @android:anim/cycle_interpolator : 周期循环变化

1.Interpolator的几种属性 - Lucky_bo的专栏 - 博客频道 - CSDN.NET
2.Android应用开发之所有动画使用详解 - 工匠若水 - 博客频道 - CSDN.NET
3.animation 坐标参考系 Animation.RELATIVE_TO_PARENT 与 Animation.RELATIVE_TO_SELF - u013578042的博客 - 博客频道 - CSDN.NET

图形处理

相关API

  • Bitmap:位图,图片在内存中数据对象 .bmp .jpg .png
  • Drawable: 就是一个可画的对象,其可能是一张位图(BitmapDrawable),也可能是一个图型(ShapeDrawable),还有可能是一个图层(LayerDrawable)我们根据画图的需求,创建相应的可画对象
  • Canvas: 画布,手机屏幕上用于绘图的目标区域
  • Paint: 我们可以把它看做一个画图工具,比如画笔、画刷。他管理了每个画图工具的字体、颜色、样式。
  • Matrix: 矩阵,高等数学里有介绍,在图像处理方面,主要是用于平面的缩放、平移、旋转等操作

其它知识

  • Context
    • 理解:Context是提供了关于应用环境全局信息的抽象类,通过它的对象, 才可以操作系统或应用的相关资源
    • context最主要的功能是加载和访问资源
    • 启动或停止Activity/Service
    • 发送广播/注册广播接收器
    • 加载布局/创建视图对象
    • 获取应用环境全局信息(比如应用信息,内容解析者,包名等)
  • Application

    • 特点
      • 每个应用只有一个此对象, 单例
      • context类型的对象都可以得到此对象
    • 应用全局数据内存级共享容器
    • 生命周期
      • 创建 应用启动且其对象不存在(创建应用的进程之后)
      • 死亡
      • 应用的进程被主动或被动杀死时(注意:应用退出不会销毁Application对象),在内存不足时,系统会优先将空进程干掉
  • ANR

    • 原因:程序在UI线程中对用户的操作响应执行的时间过长
    • 类型
    • 按键或触摸事件在特定时间内无响应(大概5S在上)
    • BroadcastReceiver在特定时间内无法处理完成(大概10S以上) onReceive()
    • Service在特定的时间内无法处理完成(大概20S以上)
    • 解决
    • 不要在UI线程中做长时间的事
    • 耗时的操作放入分线程中处理
    • 服务和广播接收器的生命周期回调方法都是UI线程中执行(千万不要在这里做长时间的操作)
  • 屏幕横竖屏切换

    • 默认情况下, 横竖屏切换时Activity会被销毁并重新创建
    • 如何不让Activity不被销毁方法:
    • configChanges=”orientation|keyboardHidden|screenSize”
    • 自动调用onConfigurationChanged()
    • 如何只竖屏或横屏?
    • 竖屏: screenOrientation=”portrait”
      横屏: screenOrientation=”landscape”
    • 横屏: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
      竖屏: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
    • 得到当前手机的方向:getResources().getConfiguration().orientation

面试题:

  • 选择Activity还是getApplicationContext()?
    • 显示Dialog时必须用Activity
    • 创建视图对象/加载布局最好使用Activity
    • 使用Adapter最好用Activity
    • 显示地图时必须用ApplicationContext
    • 其它绝大的情况下两者都可
    • 选择applicationContext肯定没问题
    • 选择Actvity可能会导致Activity对象不能回收, 导致内存泄露
  • 内存泄露和内存溢出的问题
  • 内存泄露 一块内存占用着没有释放, 但无法再用到它(比如:使用Handler发延迟消息, 退出时不移除)
  • 内存溢出 当申请的内存超过能给你的内存时就会抛出此异常(比如:加载很多图片/大图片)

Xmind简单使用教程

尚硅谷15天Android基础笔记_第21张图片

android studio快捷键

本人已经改为eclipse样式

alt+Enter键:错误提示快捷键
提取局部变量:Ctrl+Alt+V
提取全局变量:Ctrl+Alt+F
提取方法:Shit+Alt+M
快速找类:Ctrl+Shift+T
查看类的继承关系:F4
查找java文件中的类:Ctrl+O
大写:Ctrl+Shift+X(小写的为Y)
快速复制上面的内容:Ctrl+Alt+向下箭头(选中内容,要不然就默认光标的那一行)
各处搜索:两次shift
搜索指令:Ctrl+Shift+A
关键词搜索:Ctrl+F
查看最近编辑过的文档:Ctrl+E
代码折叠:Ctrl+/Ctrl-
重命名:Shift+Alt+R
搜索Action:Ctrl+Shift+A

eclipse快捷键 包括查找类、方法、变量 - chushoutaizhong的博客 - 博客频道 - CSDN.NET


项目实战

  • Android应用开发-学生信息管理系统 - CSDN博客
  • Android 数据存储 利用SQLiteDatabase实现简单的学生管理 - CSDN博客
  • 欧酷天气-这是郭霖先生《第一行代码 第2版》书籍的最后项目,我个人建议买一本作为基础书籍好好看看,是一本非常不错的书籍

做完以上书籍就意味着你的Android基础告一段落啦,但还需要更加努力在Android海洋里面遨游,这里建议你接下来学习自定义view:尚硅谷自定义View学习笔记-小白到实战 - CSDN博客

你可能感兴趣的:(Android)