尚硅谷15天Android基础笔记
前言:建议入门Android的小伙伴作为复习的时候用到,因为涉及比较多的细节知识,不利于你们的前期学习,视频地址:15天精讲精练Android核心技术
Android系统的基本了解
Android整体架构图:Android系统架构—–Android的系统体系架构 - xiaoluo501395377 - 博客园
手机尺寸的相关概念:手机尺寸相关的概念 +尺寸单位+关于颜色 - D(a/e)mon - 博客园
解决模拟器创建或启动问题:
Lunix常用命令:
常见的异常处理:
常见的异常:
- NullPointerException
原因: 调用值为null的对象的方法或属性 - ClassCastException
原因: 执行强制转换, 但类型匹配 - ActivityNotFoundException:
原因: Activity没有找到, 很可能没有注册或注册不正确
基本常见异常的一般分析步骤:
- 确定异常类型: 从下向上找, 最好能找到cause by
- 确定出异常的行号: 找到当前应用的代码行号, 双击定位
- 分析, 打印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只有一个对象)
- Activity:
区别Service与Thread
- Service
- 用来在后台完成一个时间跨度比较大的工作的应用组件
- Service的生命周期方法运行在主线程, 如果Service想做持续时间比较长的工作, 需要启动一个分线程(Thread)
- 应用退出: Service不会停止
- 应用再次进入: 可以与正在运行的Service进行通信
- Thread
- 用来开启一个分线程的类, 做一个长时间的工作
- Thread对象的run()在分线程执行
- 应用退出: Thread不会停止,
- 应用再次进入: 不能再控制前面启动的Thread对象
- Service
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() : 判断是否是有序广播
区别静态注册与动态注册
区别无序广播跟有序广播
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中提供了多种方式拦截处理不同类型的事件
视图本身就可以处理发生在该视图上的事件
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.相对布局相关属性比较多:
兄弟视图之间: 同方向对齐, 反方向对齐
与父视图之间: 同方向对齐, 居中
全部布局都有的属性
属性的划分
- 针对任何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()
适配器
- 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.按键操作的顺序: downdowndown…—>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()可以在主线程或分线程执行
- draw()
- 事件处理
- 流程方法
- 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()
数据存储
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: 数据库操作的抽象帮助类
SqliteDatabase: 代表与数据库的连接的类
Cursor : 包含所有查询结果记录的结果集对象(光标,游标)
远程服务器存储
- 对于联网的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
消息机制与异步任务
消息机制
原理图一:
原理图二:
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
注意:publishProgress()方法是在WorkerThread()执行的,一般写在doInBackground方法里面,当这个方法执行完毕之后,onProgressUpdate方法便会自动执行的啦
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博客
- 动态显示列表中的图片: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 (里面有相关的源码以及配套的服务器,使用前麻烦看看说明哈)
相关文章
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简单使用教程
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浏览器显示大分辨率图片的问题
- android 网络访问-图片处理优秀开源项目
- Android NDK 学习之传递类对象
- android listviewListview中的button点击事件或者是onitemclick
- android 开发 解码gif图片,获取每帧bitmap
- android图片压缩质量参数Bitmap.Config RGB_565等的含义
- Android怎么让RadioButton图片居中显示
- android点击按钮控制图片切换-kotlin
- android 触摸手指动作放大和缩小图片