1、面向对象三大特性:封装、继承、多态
2、四大组件:Activity、Service、BroadcastReceiver、ContentProvider
Acticity 生命周期
启动(包括杀死重启):onCreate()—>onStart()—>onResume()
后台(包括锁屏):onPause()—>onStop()
返回前台:onRestart()—>onStart()—>onResume()
解锁:onStart()—>onResume()
Acticity 启动模式
standard 标准模式:无论存在与否,创建新实例,压入栈顶
singleTop 栈顶复用:若位于栈顶,不重新创建实例,并调用onNewIntent();否则新建实例走标准模式
singleTask 栈内复用:一个任务栈中该实例唯一;当有任务栈且存在实例,则调起实例并调用onNewIntent(),任务栈清除此实例上的所有Activity;否则新建实例
singleTask 栈内独占:一个任务栈中只能存在这一个实例,不允许其他Acitivty存在;
Service的启动和生命周期
startService:不依赖context,除非该服务被杀死则一直存在
bindService:依赖context,和context共存亡
防杀策略:
Service设置成START_STICKY,会在kill后5s重启,重传intent
提升优先级,清单中设置android:priority,1000为最高
提升进程优先级,startForeground()
onDestroy方法里重启service
BroadcastReceiver的注册方法及其效果异同
静态注册:在清单文件中注册,app退出后仍可收到广播
动态注册:在代码中注册,当app退出后,广播接收停止
ContentProvider
用于将数据共享给其他应用,需要继承ContentProvider;
其他应用使用getContentResolver进行调用
3、Fragment
在项目中目前主要采用的单Activity + 多Fragment架构
生命周期:和Acitivty的大部分都是相似的
比Acitivty生命周期多出的函数
onAttach(Activity)
当Activity与Fragment发生关联时调用
onCreateView(LayoutInflater,ViewGroup,Bundle);
创建该Fragment的视图
onActivityCreate(bundle);
当Activity的onCreate();方法返回时调用
onDestoryView();
与onCreateView相对应,当改Fragment被移除时调用
onDetach();
与onAttach()相对应,当Fragment与Activity的关联被取消时调用
使用方法:
android.app.Fragment 主要用于定义Fragment
android.app.FragmentManager Fragment管理器
android.app.FragmentTransaction Fragment事务
事务操作:add、remove、replace、hide、show、detach(移除view)、attach(重建view)、commit
4、handler机制
为了避免ANR,我们会通常把 耗时操作放在子线程里面去执行,因为子线程不能更新UI,所以当子线程需要更新的UI的时候就需要借助到安卓的消息机制,也就是Handler机制了。
三个关键:
handler、looper、MessageQueue
Handler 是一个消息分发对象,而消息分发依赖于消息循环 Looper;在一个线程中,Looper 阻塞线程,等待Message消息构成循环,有了消息,分配到对应的 Handler,让他进一步分发处理。
工作过程:
主线程创建时就会创建looper,而looper会开启消息队列;
当handler发送消息时,是调用MessageQueue插入一条消息;looper在不断轮询发现message,交给handler处理。
5、AsyncTask机制
AsyncTask是对Handler和Thread的封装,使用它编码更简洁,效率更高。
AsyncTask封装了ThreadPool线程池,实现了线程的复用,比直接使用Thread效率更高。
AsyncTask是定义异步任务在后台线程(子线程中)运行的,而结果更新UI在UI线程中;
接收三个参数,Params(传入的值)Progress(进度) Result(返回的结果);
有四步onPreExecute(在UI线程中执行,准备操作)doInBackground(子线程中耗时操作)onProgressUpdate (进度更新)onPostExecute(结果返回)
6、事件分发机制
android事件分发机制 就是一个触摸事件发生了,从一个窗口传递到一个视图,再传递到另外一个视图,最后被消费的过程。
dispatchTouchEvent:负责事件分发
return true:消费事件,停止传递;
return false:回归到父View的onTouchEvent进行处理
super:
调用 onInterceptTouchEvent拦截处理(viewgroup);
调用自己的onTouchEvent(View)
interceptTouchEvent:负责事件拦截(只有viewgroup中有)
return true:将事件给自己的onTouchEvent处理
return false、super:继续向下传递事件
onTouchEvent:负责事件处理
return true:消费事件,停止传递;
return false、super:回归到父View的onTouchEvent进行处理
7、自定义控件
view的绘制流程:
onMeasure:计算大小
onLayout:计算位置
onDraw:绘制
自定义控件的实现方法:
通过继承已有的控件来实现:适用于对原生控件的扩展需求
通过引用布局文件来实现:做组合控件可以用此方法
继承View实现:实现完全自定义的控件
增加属性:
通过构造函数中引入的AttributeSet 去查找XML布局的属性名称,然后找到它对应引用的资源ID去找值;
通过XML为View注册属性,与Android提供的标准属性写法一样
8、ANR问题
容易出现的情况:
高耗时的操作,如图像变换
磁盘读写,数据库读写操作
大量的创建新对象
此问题避免:
UI线程尽量只做跟UI相关的工作
耗时的操作(比如数据库操作,I/O,连接网络或者别的有可能阻塞UI线程的操作)把它放在单独的线程处理
尽量用Handler来处理UIThread和别的Thread之间的交互
如何解决:
使用AsyncTask
在doInBackground()方法中执行耗时操作
在onPostExecuted()更新UI
使用Handler实现异步任务
在子线程中处理耗时操作
处理完成之后,通过handler.sendMessage()传递处理结果
在handler的handleMessage()方法中更新UI
或者使用handler.post()方法将消息放到Looper中
9、Andrid oom和图片的三级缓存
Andrid oom原因:
加载对象过大
相应资源过多,来不及释放
在实际开发中经常在未作处理的加载过大图片时出现
关于内存引用:
强引用:一般垃圾回收器不回收被强引用的对象
软引用:内存不足的时候,对象被回收(一般用于允许被回收的对象,当内存不够时会回收)
弱引用:GC只要发现一个对象的引用是全部是弱引用,就会回收此对象、释放内存
Andrid oom解决方法:
尽量不加载过大的图片,可以压缩图片分辨率或大小再进行加载;
利用软引用声明图片对象并用map保存缓存,可以在内存吃紧的时候适当回收掉一些引用图片;
利用LruCache来进行图片的内存缓存。当缓存的图片达到了预先设定的值的时候,那么近期使用次数最少的图片就会被回收掉
图片三级缓存:
网络加载,仅第一次,不优先加载,速度慢,浪费流量
本地缓存,次优先加载,速度快
内存缓存,优先加载,速度最快
首次加载 Android App 时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地SD卡和内存中
之后运行 App 时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的图片
总之,只在初次访问新内容时,才通过网络获取图片资源
目前都是通过一些图片第三方框架来实现三级缓存,方便快捷效果好,例如frecro、picasso等。
10、RecyclerView和ListView的异同
ViewHolder是用来保存视图引用的类,无论是ListView亦或是RecyclerView;
只不过在ListView中,ViewHolder需要自己来定义,不过非必须,只不过不使用ViewHolder的话,ListView每次getView的时候都会调用findViewById(int),这将导致ListView性能展示迟缓。
而在RecyclerView中使用RecyclerView.ViewHolder则变成了必须,尽管实现起来稍显复杂,但它却解决了ListView面临的上述不使用自定义ViewHolder时所面临的问题。
ListView只能在垂直方向上滚动,Android API没有提供ListView在水平方向上面滚动的支持;
但是RecyclerView相较于ListView,在滚动上面的功能扩展了许多。它可以支持多种类型列表的展示要求,主要如下:
LinearLayoutManager,可以支持水平和竖直方向上滚动的列表。
StaggeredGridLayoutManager,可以支持交叉网格风格的列表,类似于瀑布流或者Pinterest。
GridLayoutManager,支持网格展示,可以水平或者竖直滚动,如展示图片的画廊。
11、java GC
发现无用信息对象;
回收被无用对象占用的内存空间,使该空间可被程序再次使用。
引用计数法(Reference Counting Collector)
引用计数法是唯一没有使用根集的垃圾回收的法,该算法使用引用计数器来区分存活对象和不再使用的对象。一般来说,堆中的每个对象对应一个引用计数器。当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1当对象出了作用域后(该对象丢弃不再使用),引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。
两个对象相互引用存在问题,这时引用计数法则不会进行gc。
另外有根集的算法对此情况进行解决,若两个相互引用的对象没有被根对象间接或直接的引用,那么会被回收
12、SharedPreferences原理
getSharedPreferences()
初始化映射对象:
ContextImpl 包含了一个比较复杂的内部全局私有变量sSharedPrefs,类型是ArrayMap,就是从包名映射到preference的集合。
创建包名下的一个preference集合映射
创建SharedPrefs文件:
根据文件名构建一个File的prefsFile对象,通过此对象创建一个SharedPreferencesImpl实例
SharedPreferencesImpl()
构建函数中调用makeBackupFile和startLoadFromDisk
makeBackupFile生成一个prefsFile的备份文件
startLoadFromDisk将文件异步读取到内存
至此,SharedPreferences创建成功
获取原理
SharedPreferencesImpl中的getString()方法,通过key拿取map中的值
修改原理
edit()
SharedPreferencesImpl的edit()函数返回的是一个新建EditorImpl类的对象。
SharedPreferencesImpl.EditorImpl类里面有一个关键的变量mModified,这是一个Map,它会将以后要修改的值都放到里面去
editor.putString()
要保存的值存入EditorImpl类里的变量mModified里。
editor.commit()
提交修改,将map中的数据写入文件
步骤:
调用commitToMemory()
MemoryCommitResult类是一个封装了commitmemory结果的一个类,里面有许多信息。
commitToMemory首先创建一个MemoryCommitResult对象。接着将mMap对象赋值给MemoryCommitResult对象中的map,接下来我们就会对mMap里面的值进行修改,在修改完成后会把之前我们所进行修改临时保存的全局变量mModified进行清空处理。然后返回出去MemoryCommitResult对象。
在commitToMemory返回一个结果类后会将它当作参数传入enqueueDiskWrite()函数里。
enqueueDiskWrite()
enqueueDiskWrite()函数里面执行的是一个异步操作,在外部commit()函数会做一个await操作等待异步的完成。
在这个函数里会开启一个writeToDiskRunnable的线程,该线程做的事情是将传进来的MemoryCommitResult 里的数据写入到文件里。
writeToFile()
此函数首先会判断mFile文件是否存在,如果存在就再判断备份文件是否存在,备份文件不在的话就创建一个备份文件,然后删除原文件。
接着创建一个mFile的文件,将数据写入,再添加权限,做完后将备份文件删除。
13、java排序理解
冒泡:
一趟比较的过程,从后往前,比较相邻数的大小,小的往前排;
小的数继续和前面相邻数比较,直到最前,一趟完成,最小的数已经在顶部;
以此类推,每一趟的小数都浮起来,再进行数趟,从小到大,排序完成。
一句话理解:每一次都是最轻的气泡从最下面浮上来。
选择:
一趟比较的过程,首先从全部数中找到最小的,将最小的与顶部交换位置,最小的已在顶部;
之后从剩下数组中继续挑选最小的,排到剩下数组的顶部,即为全部数组的第二位;
以此类推,每一趟的小数都向前排了,进行数趟,从小到大,排序完成。
一句话理解:每一次选择最小的数然后放在前面。
插入:
一趟比较的过程,从顶部开始,首先比较前两个数,然后小的数在前,这两个数排序完成;
之后让紧挨着已排序组的数,去已排序组中挨个比较大小,然后替换位置;
类似是这个数插入到已排序数组的效果。进行数趟,从小到大,排序完成。
一句话理解:每一次按照顺序拿数插入前面已经排序好的数里面。