之前在一次面试中被问到数据库.网络请求.IntentService. view 绘制.内存泄漏.OOM.动画框架.自定义 view.Glide 源码.属性动画等等一系列的问题,虽然都答上来了,但是还是有一些细节性的东西没有注意得到。
回来以后吧很多知识整理成了一份PDF,还是比较全面的
(^▽^)
从基础到进阶。含有BATJ.字节跳动面试专题,算法专题,高端技术专题,混合开发专题,java面试专题,Android,Java小知识,到性能优化.线程.View.OpenCV.NDK等应有尽有。还有辅之相关的视频+学习笔记
(更多完整项目下载。未完待续。源码。图文知识后续上传github。)
可以点击关于我联系我获取完整PDF
(VX:mm14525201314)
读懂题目。如果碰到问题比较模糊的时候可以适当问问面试官。
配合面试官来面试:面试是一个相互了解的过程,要充分利用面试的题目和时间把自己的能力和技术展现出来,面试官能够看到你的真实技术。
openOrCreateDatabase(String path);
SqliteOpenHelper
类对数据库及其版本进行管理(onCreate
,onUpgrade
)getWritableDatabase()
或者getReadableDatabase();
的时候才会打开数据库。如果当时没有数据库文件的时候,系统就会自动生成一个数据库。直接操作SQL语句:SQliteDatabase.execSQL(sql);
面向对象的操作方式:SQLiteDatabase.insert(table, nullColumnHack, ContentValues);
一般外部数据库文件可能放在SD卡或者res/raw或者assets目录下面。
写一个DBManager
的类来管理,数据库文件搬家,先把数据库文件复制到”/data/data/包名/databases/”目录下面,然后通过db.openOrCreateDatabase(db文件)
,打开数据库使用。
我上一个项目就是这么做的,由于app上架之前就有一些初始数据需要内置,也会碰到数据的升级等问题,我是这么做的…… 同时我碰到最有意思的问题就是关于数据库并发操作的问题,比如:多线程操作数据库的时候,我采取的是封装使用互斥锁来解决……
引入本地广播的机制是为了解决安全性的问题:
1) 正在发送的广播不会脱离应用程序,比用担心app的数据泄露;
2) 其他的程序无法发送到我的应用程序内部,不担心安全漏洞。(比如:如何做一个杀不死的服务---监听火的app 比如微信、友盟、极光的广播,来启动自己。)
3) 发送本地广播比发送全局的广播高效。(全局广播要维护的广播集合表 效率更低。全局广播,意味着可以跨进程,就需要底层的支持。)
本地广播不能用静态注册。----静态注册:可以做到程序停止后还能监听。
使用:
LocalBroadcastManager.getInstance(this).registerReceiver(new XXXBroadCastReceiver()
, new IntentFilter(action));
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
如果有一个任务,可以分成很多个子任务,需要按照顺序来完成,如果需要放到一个服务中完成,那么使用IntentService
是最好的选择。
一般我们所使用的Service是运行在主线程当中的,所以在service里面编写耗时的操作代码,则会卡主线程会ANR
。为了解决这样的问题,谷歌引入了IntentService
.
IntentService
的优点:
(1) 它创建一个独立的工作线程来处理所有一个一个intent。
(2) 创建了一个工作队列,来逐个发送intent给onHandleIntent()
(3) 不需要主动调用stopSelf()
来结束服务,因为源码里面自己实现了自动关闭。
(4) 默认实现了onBind()
返回的null。
(5) 默认实现的onStartCommand()
的目的是将intent插入到工作队列。
总结: 使用IntentService
的好处有哪些。首先,省去了手动开线程的麻烦;第二,不用手动停止service;第三,由于设计了工作队列,可以启动多次---startService()
,但是只有一个service实例和一个工作线程。一个一个熟悉怒执行。
AIDL 解决了什么问题?
AIDL的全称:Android Interface Definition Language,安卓接口定义语言。
由于Android系统中的进程之间不能共享内存,所以需要提供一些机制在不同的进程之间进行数据通信。
远程过程调用: RPC—Remote Procedure Call。 安卓就是提供了一种IDL
的解决方案来公开自己的服务接口。AIDL:可以理解为双方的一个协议合同。双方都要持有这份协议---文本协议 xxx.aidl
文件(安卓内部编译的时候会将aidl
协议翻译生成一个xxx.java文件---代理模式:Binder驱动有关的,Linux底层通讯有关的。)
在系统源码里面有大量用到aidl,比如系统服务。
电视机顶盒系统开发。你的服务要暴露给别的开发者来使用。
讲解Binder机制。
Activity、 Window、 View 三者如何协同显示界面的。---考点:显示的过程(view 绘制流程)源码的熟悉度。
Activity剪窗花的人(控制的);Window窗户(承载的一个模型);View窗花(要显示的视图View);LayoutInflater
剪刀---将布局(图纸)剪成窗花。
(Alt+方向箭头)
fragment 的特点?(你用fragment有没有领略到一些乐趣,或者有没有踩过什么坑?)
fragment的设计主要是把Activity界面包括其逻辑打碎成很多个独立的模块,这样便于模块的重用和更灵活地组装呈现多样的界面。
踩过的坑:
newAPI
(兼容包解决);Setarguement()
初始化数据;onsave
...()方法后,commit;几种情况:
1) 一般很多高版本的新的API都会在兼容包里面找到替代的实现。比如fragment。
Notification,在v4兼容包里面有NotificationCompat
类。5.0+出现的backgroundTint
,minSdk
小于5.0的话会包检测错误,v4兼容包DrawableCompat
类。
2) 没有替代实现就自己手动实现。比如:控件的水波纹效果—第三方实现。
或者直接在低版本去除这个效果。
3)补充: 如果设置了minSDK
但是代码里面使用了高版本的API,会出现检测错误。需要在代码里面使用声明编译检测策略,比如:@SuppressLint
和@TargetApi
注解提示编译器编译的规则。@SuppressLint
是忽略检测;@TargetApi=23
,会根据你函数里面使用的API,严格地匹配SDK
版本,给出相应的编译错误提示。
4)为了避免位置的错误,最好不要使用废弃api。(一般情况下不会有兼容性问题,后面可能会随时删除这个API方法;性能方面的问题。)
栈:先进后出
标准模式SingleTop
:使用场景:浏览器的书签;通讯消息聊天界面。SingleTask
:使用场景:某个Activity当做主界面的时候。SingleInstance
:使用场景:比如浏览器BrowserActivity
很耗内存,很多app都会要调用它,这样就可以把该Activity设置成单例模式。比如:闹钟闹铃。
Measure: 测量,测量自己。如果是ViewGroup
就需要测量里面的所有childview
.
测量的结果怎么办?setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), heightSizeAndState);
设置自己的大小。
Layout: 摆放,把自己摆放在哪个位置。如果是ViewGroup
就需要发放里面的所有childview
.
怎么去具体摆放呢?
Draw:绘制
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
1) 什么是内存泄漏:最好解释清楚GC垃圾回收机制以及概念GC Root。
2) 为什么会有内存泄漏:因为内存泄漏是属于人为的失误造成的。而且面向对象开发关系复杂、多线程的关系,很容易出现引用层级关系很深以及很混乱。
3) 什么情况容易导致内存泄漏:
4) 如何解决内存泄漏
可以通过查看/data/anr/traces.txt查看ANR信息。
根本原因是:主线程被卡了,导致应用在5秒时间未响应用户的输入事件。
很多种ANR错误出现的场景:
onDraw
里面创建对象容易导致内存抖动---绘制动作会大量不断调用,产生大量垃圾对象导致GC很频繁就造成了内存抖动。)内存抖动就容易造成UI出现掉帧卡顿的问题BroadCastReceiver
没有在10秒内完成处理。BroadCastReceiver
的onReceived
代码中也要尽量减少耗时的操作,建议使用IntentService
处理。AsyncTask
处理耗时的IO等操作。HandlerThread
时,使用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
或者java.lang.Thread.setPriority (int priority)
设置优先级为后台优先级,这样可以让其他的多线程并发消耗CPU的时间会减少,有利于主线程的处理。onCreate
和onResume
回调中尽量耗时的操作。OOM产生的原因:内存不足,
android系统为每一个应用程序都设置了一个硬性的条件:DalvikHeapSize
最大阀值64M/48M/24M
.如果你的应用程序内存占用接近这个阀值,此时如果再尝试内存分配的时候就会造成OOM。
ActivityManager.getMemoryClass()
可以用来查询当前应用的HeapSize
阀值。可以通过命名adb shellgetProp | grep dalvik.vm.heapxxxlimit
查看。如何避免内存泄露:
1)减小对象的内存占用:
HashMap
等传统数据结构而使用安卓专门为手机研发的数据结构类ArrayMap/SparseArray。SparseLongMap/SparseIntMap/SparseBoolMap
更加高效。HashMap.put(string,Object);Object o = map.get(string);
会导致一些没必要的自动装箱和拆箱。
Enum
枚举,替代使用普通的static常量。(一般还是提倡多用枚举---软件的架构设计方面;如果碰到这个枚举需要大量使用的时候就应该更加倾向于解决性能问题。)。inSampleSize
: 计算图片压缩比例进行图片压缩,可以避免大图加载造成OOM;
decodeformat
:图片的解码格式选择,ARGB_8888/RGB_565/ARGB_4444/ALPHA_8
,还可以使用WebP
。2)内存对象的重复利用:
使用对象池技术,
两种:
自己写;
利用系统既有的对象池机制。比如LRU(Last Recently Use)
算法。
- a.
ListView/GridView
源码可以看到重用的情况ConvertView
的复用。RecyclerView
中Recycler
源码。- b.Bitmap的复用
Listview
等要显示大量图片。需要使用LRU
缓存机制来复用图片。- c. 避免在
onDraw
方法里面执行对象的创建,要复用。避免内存抖动。- d 常见的java基础问题---
StringBuilder
等
3)避免对象的内存泄露
4)使用一些内存的优化策略:看文档
1)通过Binder
2)通过广播
把几个关键版本的特性记住:3.0/4.0、4.4、5.0、6.0/7.0
RequestLayout()
方法: 会导致调用Measure()方法和layout()。将会根据标志位判断是否需要onDraw()
;onLayout()
:摆放viewGroup
里面的子控件onDraw()
:绘制视图本身;(ViewGroup
还需要绘制里面的所有子控件)drawChild()
: 重新回调每一个子视图的draw方法。child.draw(canvas, this, drawingTime);
invalidate()
: 在主线程当中刷新;postInvalidate()
:在子线程当中刷新;其实最终调用的就是invalidate,原理依然是通过工作线程向主线程发送消息这一机制。
public void postInvalidate() {
postInvalidateDelayed(0);
}
public void postInvalidateDelayed(long delayMilliseconds) {
// We try only with the AttachInfo because there's no point in invalidating
// if we are not attached to our window
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
}
}
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
传统的动画框架: View.startAnimation();
弊端: 移动后不能点击。原因?跟实现机制有关系。
所有的透明度、旋转、平移、缩放动画,都是在view不断刷新调用draw的情况下实现的。
调用的canvas.translate(xxx),canvas.scaleX(xxx)…. Xxx:matrix像素矩阵来控制动画的数据。记得看源码,结合多只缩放的demo看源码。
看具体的手机平台,常见的有:64M/32M等
性能对比: LinearLayout
的性能要比RelativeLayout
好。
因为RelativeLayout
会测量两次。而默认情况下(没有设置weight)LinearLayout
只会测量一次。
为什么RelativeLayout
会测量两次?首先RelativeLayout
中的子view排列方式是基于彼此依赖的关系,而这个依赖可能和布局中view的顺序无关,在确定每一个子view的位置的时候,就需要先给每一个子view排一下序。又因为RelativeLayout
允许横向和纵向相互依赖,所以需要横向纵向分别进行一次排序测量。
1) 减少在onDraw
里面大量计算和对象创建和大量内存分配。
2) 应该尽量少用invalidate()
次数。
3) view里面耗时的操作layout。减少requestLayout()
避免让UI系统重新遍历整棵树。Mearsure
。
4) 如果你有一个很复杂的布局,不如将这个复杂的布局直接使用你自己的写的ViewGroup
来实现。减少了一个树的层次关系 全部都是自己测量和layout,达到优化的目的。(Facebook就经常这么干)
提示:跨进程通信。进程之间进行数据交互共享。;源码来一剁。
请查看完整的PDF版
(更多完整项目下载。未完待续。源码。图文知识后续上传github。)
可以点击关于我联系我获取完整PDF
(VX:mm14525201314)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。