面试过程:
11:00 公司开会(我等,我在等)
12: 00-14:00 跟两位 android程序员+技术总监 pk。
14:00-15:00 :跟两位人事谈人生理想.
面试记录:
11:00 : 到公司,前台后面 摆着桌球, 各种水果,感觉公司气氛还不错。
到公司的时候,已经11.00多了,刚好周一他们团队开会,我就被一个的小哥带到一个会议室等带,给倒了杯水,就把哥丢在那了。在会议室,听到隔壁的会议室,有来参加面试的和人事在那侃大山,略搞笑,依稀记得 还问了,假如 有其他公司 高薪挖你走2828之类的问题。(PPS:面试 技术是一方面,人事也不可以忽略。)
12:00: (一轮PK)终于开会结束了,哥可是饿着肚子呢, 这家公司没有笔试题,直接一个搞Android的哥们进来,简单介绍了一下 ,就聊起来了。首先 J哥 简单介绍了一下 在上一家公司 担任什么角色,平时开发流程 之类的,然后J哥 就说大概在公司开发了有5款APP,自己私下接过一款私活,然后自己没事也做了两款应用,然后J哥 把应用展示给他看,他看了连连称赞不错啊。。。(lalala,其实都是J哥网上巴拉的项目啦。)
(然后大体给他介绍了 项目基本框架,是 v4包里的 SlidingPaneLayout 嵌套了实现了轮询效果 自定义的viewpager 。然后 具体界面是用的瀑布流,项目的关键就是 对 图片的处理,因为有N张 图片,但是并没有卡顿,所以就说了 自己用 了开源的imagedownloader 和 volley 以及自己定义的 lrucache 缓存 bitmap 对象,这里大家一定要把图片的三级缓存 自己了解清楚,基本面试会问到。)
其实 当面试问你 如何避免oom ,内存泄露导致的原因,以及如何处理大图片等等,其实都是 如何优化内存。
可以按照我自己总结的回答,你可以说,这个问题 ,跟 oom 以及 内存泄露,其实是一样的,关键 就是 如何 优化内存,避免不必要的 内存泄露,
而 内存泄露 的原因 ,我总结了 4点,
1. 匿名内部类,和非静态内部类, 举个栗子:我们用handler 进行线程间 假如 我们在activity中这样定义 handler :
- Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- mImageView.setImageBitmap(mBitmap);
- }
- }
然后,我们用 右键 选中工程 运行 lint工具 , android tools---run lint ,就会提示我们这样一个`warning: In Android, Handler classes should be static or leaks might occur.`。
类似的还有 匿名子线程。
2.还是 拿网上的 栗子来说,
- Vector v = new Vector( 10 );
- for ( int i = 1 ;i < 100 ; i ++ ){
- Object o = new Object();
- v.add(o);
- o = null ;
- }
即便是 我们把 o 对象 置为 null,但是 vector 集合中还有有o的引用,所以 集合 没有被清空,这一部分内存 还是不能被释放,这就导致了内存泄露。
3, 当我们操作数据库的时候,我们在执行完 相应的crud 方法后,我们没有关闭 cursor .close()或者 db.close(),也同样会占用内存、因为只有关闭连接后,才会被GC 回收。
4.继续举个栗子
- Set<Person> set = new HashSet<Person>();
- Person p1 = new Person("唐僧","pwd1",25);
- Person p2 = new Person("孙悟空","pwd2",26);
- Person p3 = new Person("猪八戒","pwd3",27);
- set.add(p1);
- set.add(p2);
- set.add(p3);
- System.out.println("总共有:"+set.size()+" 个元素!");
- p3.setAge(2);
- set.remove(p3);
-
- set.add(p3);
- System.out.println("总共有:"+set.size()+" 个元素!");
J哥 亲自 实践了下,发现问题了,这个网上的栗子 是错的。实际上 是可以remove掉得、真是个悲伤地故事。这个栗子是不正确的。。网上好有一片这样的文章,都是这个栗子。。
OK。还有最后一点,就是 关于图片的,bitmap对象的及时释放,这里 就不细说了,等在图片三级缓存一起去总结。
此时 感觉 对面的android 小哥 已经被我吸引了。好像很认真的在听我讲课一样
![偷笑](http://static.blog.csdn.net/xheditor/xheditor_emot/default/titter.gif)
。
然后, 他问我问题。我大体总结了一下。
面试官01问:有没有自定义过view。
J哥回答:这个很常见,我自己定义过很多,比如 下拉刷新,上拉加载更多数据的listview,类似github 上面的pulltorefreshlistview。
还有图片轮询播放的viewpager,也是 继承viewpager,然后自己开启一个线程,去控制 切换的。还比如,跑马灯效果的textview ,scrollview与 listview 相互嵌套 导致 listview 高度计算不正确,我也是 自定义listview,复写了 onmeaure方法,然后解决冲突的。在比如 一些开源的 可以放大缩小的图片,我也是做过,主要是对onmeasure 方法,onlayout方法,ondraw 方法的复写。以及复写一下 view 自己的 touch事件等等,奥 对了,我们公司当时有需求 做一个 锁屏软件,侧滑解锁的,我也是自己定义的,然后展示给他看了一下,当时 那篇文章在这里。传送门
http://blog.csdn.net/u011733020/article/details/41863861。
面试官01问:listview的优化、
J哥回答:(PS:这种问题,基本上 都快被问烂了,但是没办法 还是要回答。) listview 作为最常见的 用来显示数据的view ,一般 从四个方面 去优化。
1 ,复用convertview, 不然假如有1000条数据,那么我们滑动,就会 产生1000个convertview ,这对内存是很大的浪费,所以 我们一定要复用。
2. 减少 findviewbyid 的次数, 因为 每次 去 执行 findviewbyid 也是要消耗资源的,我们要尽可能的减少,通常 我们定义一个viewholder,去管理 这些id ,然后通过tag 去直接拿到 id。
3, 分页加载,延迟加载 预加载。 这个在我们以前项目,有一个榜单,数据量很大,一次请求过来的数据量很大,这样有两个问题,一个是 请求网络 时间可能会很长,另一个展示数据 上面 体验对不是很好,所以 我们做了 第一次加载 20条,然后每次请求 再去 加载10条新数据。
面试官01问: 看你简历上面 做过 社交,通信这块是怎么做的。
J哥回答:我看 咱们公司 也用到了 聊天,咱们公司是 自己做的 还是 用的第三方的类似 环信的。结果被J哥猜中,他说 是集成的环信(但是 有丢包现象,所以打算自己做通信)。
OK,J哥说 ,我们 项目中聊天 是基于xmpp协议的做的,在没有android 以前 ,java 有个 开源的 smack ,android 上 现在有一个asmack ,其实 就是移植到android 中来了, 服务端是基于 openfire的 ,我们就是做的 openfire+asmack 的 聊天,这个原理主要 就是 绑定 ip 拿到 connection 然后 connect ,然后进行通信,我说,这个 跟http请求 其实原理上一样,都是 绑定ip,然后 设置一些property,然后通过类似流进行通信的, asmack,其实底层 就是xml通信的。
面试官01问: touch 事件的传递机制,还特意画了,一个 就是 button LinearLayout 嵌套 。
J哥回答:就是这个, 这也难不倒我。因为J哥觉得 这个问题肯定会问到 所以 早有准备,这里 我就大体说下结论,详细原理 给你传送门。
我回答,这个很简单,只要你继承一下 button 和 linearlayout 复写一下 三个方法 dispatchtouchEvent onInterceptTouchEvent 和onTouchEvent .就能很清楚的明白 传递的过程,我给你总的说下结论的,点击这个button,一般是 外面的父控件 先响应这个down 事件,然后 往子类里面传递,让子类 在往子类的下一级子类去传递,让最终的孩子去决定是不要要消费掉这个点击事件,如果消费掉,那么父类将不会响应,如果子类不消费,那么会退回到次级子类,然后看是否要消费,这样,一句话 就是父传子, 子决定要不要,不要 然后传回去。
面试官01问: 项目中图片的优化。
J哥回答:我给他展示的项目 其中有一款app 是有很多图片 ,但是 很流畅,也没有oom。关于图片 优化,一般我们采用三级缓存,1 。内存加载 2.本地加载 3 网络加载。 首先 我们看 内存中有没有,有直接拿来用,这里 我项目里是这样做的,我先获取一下 分配给我们应用的可用内存是多少,然后 拿1/4 或者 1/8做一个 lrucache. 把我们的bitmap 对象添加进去。有些比较常用的图片,我会保存到本地,避免每次重复联网下载。结合 开源的 afinal universalimageloader 以及 13年谷歌官方推荐的volley(号称是 asynchttpclient 和
universalimageloader)的结合、 所以 在我的项目中基本没有遇到过图片导致的oom 问题,对于单张的 大图片,我也会利用bitmapFactory,进行计算大小,然后 计算手机分辨率,进行定量的 压缩 处理。
面试官问: GC的回收
J哥回答:我说。GC 回收 应该不只是按照一种方式,应该有多种不同的算法,我看过谷歌 官网介绍的一点,有这样一块区域,他分为 latest(最近) middle(中等)permanent(永久的),这样三块子区域。里面分别存放,刚刚被创建的,以及 时间 靠后的,很久的,对象,不断地新对象 往latest里面添加,当达到相应对象区域的阀值的时候,就会触发GC,GC 进行回收的时候,对于latest 中回收的速度是最快的,而permanent 相对是最久的,而时间 也跟 每块区域中对象的个数有关系, 还有一种算法,是根据最近被引用的时间,或者 被引用的次数 去进行 GC的、、这里随便扯就是了。GC 回收并不是立即执行的。是不定时的。GC回收的时候 会阻塞线程,所以代码中要避免创建不必要的对象,例如for循环中 创建大量对象 就会容易引起GC。 当我们也可以主动 在方法中执行system.gc() 去手动释放一些资源。
面试官01问: 怎么避免 viewpager 预加载 fragment的、
J哥回答:这个问题 我也碰到过,我们都知道,viewpager 它本身会预加载 左右两个 和当前一个对象、而 我们viewpager setOffscreenPageLimit(0) 不生效因为看源码知道 这个方法 默认最少也要加载一个。所以 这个fragment 还没有被当前页面显示出来,已经夹在好了,有可能数据不是最新的,我是在 setuservisibilityhint() 这个方法中跟参数 动态去判断 要不要刷新的。
问了一圈,这个哥们大概没什么问的了,然后 就让我等一下,说让他们技术总监过来 。 我就等。。。
然后等了几分钟,进来一小姑娘,坐下,看了我简历,我以为是人事,来跟我谈人生理想。结果,没说几句话,让我讲一下我的项目。我qu,惊呆我了。我问,你也是做android的,我去,是这样的、、把J哥吓到, 然后问了J哥几个问题。
Android 小姑娘问: 看你项目中的listview 中item类型 是统一的,而加入 item 差别挺大的 你怎么复用。
J哥回答:J哥装作很牛的样子说,我暂时想到两种方法,1.给这个对象 加一个type 然后 根据 type 去复用,或者 把这几种类型 一起加载,然后控制显示隐藏。然后 我反问小姑娘,假如 我这里 有一百条数据,这一百条是无序的,包含了 10种 item类型,你有没有什么好方法 去处理这个问题,
![偷笑](http://static.blog.csdn.net/xheditor/xheditor_emot/default/titter.gif)
小姑娘说,你不是定义了类型吗,我们就是 通过type 去判断的。
Android 小姑娘问: onAttch onDetach 还是 onAttachedToWindow onDetachedFromWindow
J哥回答
:其实 那个小姑娘忘记这两个方法了。我说什么方法,她说onAttachIntent() 和 onDetachIntent(). 反正 J哥是没听说过, 我只见过 onAttach , 但是 这个方法 我也没用过。我就问她,这两个方法是做什么的,小姑娘跟我说 是 把子view绑定到界面上的,那么的话 应该是
onAttachedToWindow
onDetachedFromWindow方法了,小
姑娘说: 在这个方法 可以计算子 view的高度宽度,在 oncreate 里面不能计算,其实虽然刚开始 在oncreate里面是不能计算,但是还是有方法计算的,(本人觉得面试 问你 API 是 最2的了,忍不住吐槽下,我遇到过,Camera 拍照,问我获取 一个图片 还是 视频的 方法,我去百度 一下,随便就知道,真是不懂 为什么会问方法。随便一个程序员 都会百度。。)
跟小姑娘聊得其他问题 不太记得了,感觉这个女程序员啊。。就问方法 给我的印象不太好,不管方法用没用到,我觉得面试 直接问你方法 好2 好2...
然后技术总监 有进来跟我聊了,后技术总监 有进来跟我聊了、技术总监 年龄30出头吧,到是没有问我什么技术问题,
总监: 问我 做没做过通信这块,能不能做这一块。
J哥回答:
,我说做过,通信有几种协议的,我们用的 是xmpp协议的 ,服务器 是 基于apache的 openfire 搭建的,客户端 是用的asmack。还有一些 其他协议的 ,比如我知道有些项目中用的 soap协议的,还有ip 协议的。PS:反正就是扯
我说 通信 客户端这一块 我没问题,但是 服务端 我 从工作以来 一直偏向 android 移动端开发,后台这一块,如果数据量大了,还要考虑并发之类的,我是做不了,让我做个tomcat搭建的demo 我可能可以。
其他也是随便聊了下,然后 就说,让人事来跟我谈理想了。
总监: 问我 什么时候能上班
J哥回答:
![大笑](http://static.blog.csdn.net/xheditor/xheditor_emot/default/laugh.gif)
我说 这个看公司需求啦。
其他也是随便聊了下,然后 就说,让人事来跟我谈理想了。
这里 感觉应该没问题了。差不多能拿下了。
人事1:一进来,就问东问西。问加班看法啊,他们公司技术 一般都八九点走啊。说七点基本没有走的啊、、、
J哥回答
:我说,一般遇到项目加功能 ,版本升级,等等 这些加班都没什么,只要不是一直在加班。。。。这里每个人自己看法就好了、、
反正人事 是一直跟我强调这个,她不停强调 我就暗暗下决心,薪资 我是不会要低了。
人事1:看你还年轻啊,还能拼一拼啊、、、、
J哥回答
:我说现在 这几年对我人生规划也算比较重要的时期,也是过一年少一年了,其实她的意思 还是侧面强调加班。。。。日了UZI了。
中间一堆废话,然后 我问了她 公司一般上下班时间啊。。之类的有没有技术交流啊,之类的。。。
最后到关键问题上啦,最关心的,薪资问题。
人事1:期望薪资
J哥回答
:我说16K左右吧。她问 你以前公司多少 握手 15K。她说她们公司 是 14薪。反正 我还是说16K。她说 那好,你等下,然后就出去了。
不知道 跟什么人 讨论了许久,然后又来一个 可能是人事吧。又进来,问了一遍,也问了薪资。。哥还是说16K 。 。。估计是她们公司想要我,但是又觉得有点超出她们薪资期望吧,当场被没有给什么offer。然后就有点婉拒的说,两天给我答复,心里很气愤,饿着肚子 面试到三点,竟然婉拒、、、
反正我是很生气,我说,好,然后我就走。结果,没过一个小时,人事又打电话来,非要约我 见一下她们CEO。这是什么鬼,难道她们CEO要给我煲汤 了?我说可以,然后时间定在后天了,,反正心灵鸡汤对我是没用了、
OK ,这家面试 先写到这里,下面 下午还有一家,等下在写。准备睡觉。今天面试回来,累的就睡着了,晚上十点多才醒过来,想了想还是 把今天面试的过程总结一下。
-------------------------------待续 -------------------------
面试公司:五道口 某公司
面试时间:5月27日 11:00 AM.
面试过程:
感觉公司还是挺有活力的的,进去填了申请表,人事就跟我聊了下 从上家离职原因(找工作 要准备怎么回答这个问题,最好能找一些客观因素,不要说 感觉以前公司不好。。。)
随后 就是 android 技术 跟我面试。大概面试了 四十分钟吧。惯例 总结了一下 有六七个问题,这个技术水平比较高,问得问题 有几个我开发中都没有遇到过。这次面试感觉 没戏。。。
不过 失败乃成功之父。不气馁
面试官01问:解析json。
类似这样的 {
"11:00": "0",
"12:00": "0",
"13:00": "0",
"14:00": "0",
"15:00": "0",
"16:00": "0",
"17:00": "0",
"18:00": "0",
"19:00": "0",
"20:00": "0",
"21:00": "0",
"22:00": "0 "
}
这个平时开发中没碰到这种情况,自己也懒,没有看还有什么解析方法 说以当时只是说,解析json 源码 其实跟解析xml 类似 都是 遍历每一级,存到map集合中。知道应该有方法可以取到,但是 却没有想到 通过iterator。
J哥回答:这里 当时没能回答上方法,回来 看过解析json 的源码 其实就是 存到一个HashMap<key,value>集合中
其实解析很简单 就是先转成JsonObject 然后拿到迭代器, 遍历集合就可以取到 value。
代码很简单
- try {
- obj = new JSONObject(json);
-
- Iterator iterator = obj.keys();
- while(iterator.hasNext()){
- Object next = iterator.next();
- obj.get(next.toString());
- }
- } catch (JSONException e) {
-
- e.printStackTrace();
- }
平时不在意 ,关键时刻掉链子!!!
面试官01问:Listview 如果有多种类型的 item ,怎么实现。
J哥回答:这个问题,被问到的概率还是挺大的,自己以前 开发也没遇到这样的需求,也没有去了解,所以回答的都不是很好。
这里 我回来查找资料,才发现,google 开发者 已经 考虑到这种情况,做了相应的支持,所以listview 本身是支持item 多type的。
通常我们在使用listview 给 listview 适配数据的时候,用到adapter,以及 四个方法 getCount getItem getItemId getView,那么 在我们 多type 的item 时候,我们需要去复写 两个方法 getItemViewType getViewTypeCount, 着两个方法的的意思是
getViewTypeCount:
Returns the number of types of Views that will be created bygetView
. Each type represents a set of views that can be converted ingetView
. If the adapter always returns the same type of View for all items, this method should return 1.
This method will only be called when when the adapter is set on the theAdapterView
.
Returns:
-
The number of types of Views that will be created by this adapter
-
意思是 返回 getview 时 产生的 item 的 类型的个数,如果 我们的listview 中所有的item 只有一种类型 ,那么我们不需要复写这个方法,这个方法 当我们给listview setAdapter 的时候 被调用。
-
因为 可以在baseAdapter 中发现 ,默认值 就是 1.
-
- public int getViewTypeCount() {
- return 1;
- }
-
另一个方法
getItemViewType
Get the type of View that will be created by getView
for the specified item.
Parameters:
-
position The position of the item within the adapter's data set whose view type we want.
Returns:
-
An integer representing the type of View. Two views should share the same type if one can be converted to the other in
getView
. Note: Integers must be in the range 0 to
getViewTypeCount
- 1.
IGNORE_ITEM_VIEW_TYPE
can also be returned
这个方法 返回 将要被 getview 创建的 item的 类型 。
-
返回值 范围 0 ~~~~
到 类型type-1。
-
-
就是 这两个关键的方法,要想我们实现多type 我们就要实现这两个方法。
效果就是这样子。
代码实现 跟单一 item 唯一的区别 就在上述两个方法 和 getview 方法中增加了判断当前类型。下面Adapter中部分代码
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
-
- int type = list.get(position).type;
- int itemViewType = getItemViewType(position);
-
- System.out.println("type===itemViewType"+(itemViewType==type));
- if(getItemViewType(position)==0){
- convertView =inflater.inflate(
- R.layout.item01_layout, parent, false);
- return convertView;
-
- }else if(getItemViewType(position)==1){
- convertView =inflater.inflate(
- R.layout.item02_layout, parent, false);
- return convertView;
- }else{
- convertView =inflater.inflate(
- R.layout.item03_layout, parent, false);
- return convertView;
- }
-
- }
- @Override
- public int getItemViewType(int position) {
- System.out.println("getItemViewType-------------------------------"+list.get(position).type);
- return list.get(position).type;
- }
- @Override
- public int getViewTypeCount() {
- return maxType;
- }
上面这里 并没有考虑过 convertview 的复用, 但是如果 有很多条的话,我们就不能这么简单的用了,要复用的话,我们要分别判断 type类型,和 convertview 去实现复用, 这里如果type 很多 复用的话,在getview 方法里面要写很多 判断,假如 type==0 inflate 一个layout01,type==2 inflate 一个layout02....假如有十个 那么 我们的getview方法里要分别判断 十次type 而且还要判断 convetview==null,所以 类型多了的话,这个方法写起来逻辑 可读性都不好,
然而我有没想到什么好办法,网上搜了点资料,这个感觉还是不错的,用这种方式 虽然 要写很多类,但是感觉不用把所有的代码都写在一个getview中。如果 有什么好方法 可以一起分享出来。
面试官01问:可以手动缩放的图片 怎么去实现。
J哥回答:由于 自己展示中的项目用到了 开源的
PhotoView, 所以 被问到自己实现。当时自己也没有看 源码,只是知道类似的,touchevent,
知道 两点触摸时间,那是关于图片 拖拽的, 实现的关键 就是 touch 时间的监听,对于 手指按下, 手机移动,手指离开的监听,记录初始的xy ,以及离开时的 xy,进行相应的缩放,在onlayout ondraw. 关于 photoview 的分析,自己找了些资料,这里有一篇分析,感兴趣的可以看一下,个人感觉还不错。地址。
面试官01问:ImageLoader 源码 分析
原理 这张图 表达的比较清楚
使用方法 一般是
- ImageLoader.getInstance().displayImage(picurl, imageview);
这个ViewAware 对象 里面 有一个 weakReference, 是的。 图片在内存中就是 通过这个 弱引用缓存的。
Implements a weak reference, which is the middle of the three types of references. Once the garbage collector decides that an objectobj
is is weakly-reachable, the following happens:
- A set
ref
of references is determined. ref
contains the following elements:
- All weak references pointing to
obj
. - All weak references pointing to objects from which
obj
is either strongly or softly reachable.
- All references in
ref
are atomically cleared. - All objects formerly being referenced by
ref
become eligible for finalization. - At some future point, all references in
ref
will be enqueued with their corresponding reference queues, if any.
Weak references are useful for mappings that should have their entries removed automatically once they are not referenced any more (from outside). The difference between a
SoftReference
and a
WeakReference
is the point of time at which the decision is made to clear and enqueue the reference:
- A
SoftReference
should be cleared and enqueued as late as possible, that is, in case the VM is in danger of running out of memory. - A
WeakReference
may be cleared and enqueued as soon as is known to be weakly-referenced.
上面 是对于weakreference 的注释
WeakReference实现了一个弱引用,弱引用是三种引用(StrongReference、WeakReference、SoftReference)中的一个。一旦垃圾回收器判定一个对象是是弱获取(对象可获取程度分为五种strongly reachable,softly reachable、weakly reachable、phantomly reachable、unreachable),则下列情况发生:
计算一组引用的ref,ref包括下列元素:
所有指向obj的弱引用
所有指向objects的弱引用,objects是软获取对象或者是强获取对象
在ref中的所有引用被自动删除
所有以前被ref引用的对象都符合终止条件的对象(become eligible for finalization)
在未来的某个时间,所有的在ref中的引用将被放入合适的引用队列中
弱引用对那些映射,这些映射中的实体的引用一旦被不存在这些实体将被自动删除。弱引用和软引用的区别是清空和将加入排队的时间点不同:
一个弱引用应该尽可能晚的被清除和加入队列,那是因为如果内存不足是vm将是危险的
弱引用对象是一旦知道引用的是弱获取对象就会被清除和入队。
就是 在 JVM 内存不足的时候 GC 会优先回收这个 设置了 WeakReference 的对象。
在继续看Display 方法
- public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
- ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
- checkConfiguration();
- if (imageAware == null) {
- throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
- }
- if (listener == null) {
- listener = defaultListener;
- }
- if (options == null) {
- options = configuration.defaultDisplayImageOptions;
- }
-
- if (TextUtils.isEmpty(uri)) {
- engine.cancelDisplayTaskFor(imageAware);
- listener.onLoadingStarted(uri, imageAware.getWrappedView());
- if (options.shouldShowImageForEmptyUri()) {
- imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
- } else {
- imageAware.setImageDrawable(null);
- }
- listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
- return;
- }
-
- ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
- String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
- engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
-
- listener.onLoadingStarted(uri, imageAware.getWrappedView());
-
- Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
- if (bmp != null && !bmp.isRecycled()) {
- L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);
-
- if (options.shouldPostProcess()) {
- ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
- options, listener, progressListener, engine.getLockForUri(uri));
- ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
- defineHandler(options));
- if (options.isSyncLoading()) {
- displayTask.run();
- } else {
- engine.submit(displayTask);
- }
- } else {
- options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
- listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
- }
- } else {
- if (options.shouldShowImageOnLoading()) {
- imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
- } else if (options.isResetViewBeforeLoading()) {
- imageAware.setImageDrawable(null);
- }
-
- ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
- options, listener, progressListener, engine.getLockForUri(uri));
- LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
- defineHandler(options));
- if (options.isSyncLoading()) {
- displayTask.run();
- } else {
- engine.submit(displayTask);
- }
- }
- }
代码太多,不一一贴出来了,在下载图片的task 方法中,会 做一些 线程安全的同步,还有就是判断本地 内存 有没有以前下载好了,有的话直接拿过来用,没有 去下载 然后试着保存在磁盘缓存中
- private boolean downloadImage() throws IOException {
- InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());
- if (is == null) {
- L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey);
- return false;
- } else {
- try {
- return configuration.diskCache.save(uri, is, this);
- } finally {
- IoUtils.closeSilently(is);
- }
- }
- }
面试官01问: 大图片 加载 oom 问题
J哥回答: 对于大图片,直接加载给Imageview 容易出现 OOM 异常,那么我们就要对其进行压缩, 压缩原理,根据手机分辨率 去压缩
首先获取手机分辨率:
- WindowManager windowManager= (WindowManager)getSystemService(WINDOW_SERVICE);
- Display display= windowManager.getDefaultDisplay();
- height = display.getHeight();
- width = display.getWidth();
然后 计算图片的宽度高度,根据比例压缩 在显示 图片
- public void calImageView(){
-
-
-
-
- BitmapFactory.Options options= new BitmapFactory.Options();
- options.inJustDecodeBounds=true;
-
-
- String path="";
- BitmapFactory.decodeFile(path, options);
- int ImageWidth= options.outWidth;
- int ImageHeight= options.outHeight;
- System.out.print("ImageWidth:" + ImageWidth);
- System.out.print("ImageHeight:"+ImageHeight);
- int scaleX=ImageWidth/width;
- int scaleY=ImageHeight/height;
- int scale=1;
-
- if(scaleX>scaleY & scaleY>=1){
- scale=scaleX;
- }
-
- if(scaleY>scaleX & scaleX>=1){
- scale=scaleY;
- }
-
- options.inJustDecodeBounds=false;
- options.inSampleSize=scale;
-
- Bitmap bitmap= BitmapFactory.decodeFile("/storage/emulated/a.jpg", options);
-
- iv.setImageBitmap(bitmap);
-
-
-
-
- }