00:01
那么还有一点事情,作业给大家讲完啊。上午呢,我们已经能够去用3G缓存来显示图片列表,我们先来看一下。嗯。好,现在他包一个错误,我们来找一下。说是一个什么错误了,孔子人是吧。那。他说这一个路径是空的。嗯,这个。这个看起来像什么呢?像一个。像好像我们没加权限似的。关键权限已经加了呀,对吧。
01:03
我们看一下那个路径在不在。嗯。那是模拟器出问题了。我再给他启动一下啊。诶,好像有点小问题啊,大家看着是吧。怎么办呢?删掉一个吧。重新创建一个。
02:01
嗯,来幺八。重新启动一下。好,这一次应该没问题啊。那我这里面怎么有多个呢?还没管理起来。学了是吧,嗯,好了,来这一次我们看一下那个SD卡能不能打开啊。哎,可以是吧。好,我们来运行一下。走。
03:02
大概。现在呢,我们可以正常的去动态加载图片了。嗯,都没有什么太大问题。但是这里面呢,在我们快速滑动的时候。就会存在一个问题。啊,就是我们经常说的一个问题,就是屏幕闪动,就是图片。会在不断的变化。来看啊。嗯。好,我们现在呢,就要说的是它为什么会变化。第三啊,来说一下啊,在list view中使用啊,图片三级缓存会。
04:04
存在啊,图片。转动的bug啊,那如何来去解决,或者说首先我们要知道原因是什么,再说我们怎么样去解决,其原因非常的简单。啊,就是试图复用。You can what you。被不用了。那我问一下大家看what of you不用,实际上就是imagine you也是被复用了吧?好了,为什么说what of you被复用了就会出现?一个视图闪动的一个图片闪动的一个bug呢,我们可以来想一想啊,大家看到。
05:00
来。好,现在呢,我们来去看一下。现在假设这是我们的那个界面,要显示一个列表。好显示的列表呢,比如说假设啊,我这里面多来几行。一行好,这个左边假设啊,当前要显示的图片是A,图片图片A可以吧。好。那我们要显示一个item,是不是要调用一下get view方法吧?我们一个个来啊,这里面会去调用get view。嗯,来。这个地方把它们的大小的给它做小点,Get view方法啊,Get view方法里面如何来展示图片呢?是不是最终要去加载图片呢?尤其是第一次的时候,大家先别想着说什么它已经加载过了。
06:04
它如果是第一次是不是要去启动分线程去加载呀,而且我们现在是不是启动了一个什么是是个踏实。去加载它对吧,我们启动的一个它去加载的,那也就是说每一次要展示一个新的item,是不是都要去相当于启动一个新的线程去做吧。嗯,好啦,然后我这个县城里面大家看啊,在我这个里面。好,打家看。我呢,我要加载的是image view,我要加载一个image view。我这当前呢是image view,呃,假设啊,当前呢叫什么呢,叫image view1吧,它对应的图片路径呢是。呃,a.B。可以吧,好,我们就是这里面啊,什么前面的路径,前面应该有个有一个路径的啊,我就不写了,嗯,后面呢,肯定是一个a.JBG需要去加载这个路径,好,那你想想看其他的下面的一些。
07:18
假设呢,这个是B,我要加载B图片啊,那我是不是又得干嘛。是不是又开一个线程啊,又会去调用get,肯定是调get方法去整呢啊,又开一个线程a think task一样的,都跟上面是一样的,每个人呢,都会去开一个线程好了。但是我们的线程它不是一下子就结束了吧,因为我们的扶墙是睡多长时间。是不是时间是两秒啊啊,在这个在这个两秒的过程中,我相当于是不是已经启动了好几个线程了。
08:05
啊,我其中了好几个好了啊,一直往下呢,都是一样的,这两假设是C。啊,这个是C啊,下一个还有一个假设啊。这是一个D。啊,这是个D,好,那我们现在相当于启动了几个线程。是不是四个吧,好。启动了四个线程,而这个里面呢,可能它呢是它呢是C啊,它呢是D。嗯,这一个呢,是假设是image view2image view3image view4。啊好,那我问大家,现在你说我的这一个假设,以他为例啊。你看他,你说我这个现在一定执行了吗。
09:02
很可能是不是这个线程在执行,上面的这个线程在执行吗?啊,他正在执行,它正在执行,那其他线程是不是还处于等待呀。嗯,好啦。那我做一个事情,我做一个什么事情呢。我快速的。线上拖动。会出现什么问题?大家看啊,我我快速的拖动的话,你在想我是不是这一个A这个视图会被复用啊,那个B这个视图也会被复用吗?他们是不是用来去显示别的图片。啊,譬如说假设这用来显示F,这样用来显示G,啊,好了,那是不是我又启动了新的线程。对吗?啊,我用他们,我不就这里面不又启动了,相当于启动了新的线程去做吗?好,我启动了新的线程啊,启动了新的线程,但是用的还是那个image VR吧,只是此时我要去显示的那个图片是F连接VG假设啊。
10:12
假设是F点接B,那你得想。这个线程是不是先启动啊。最后启动好。当前我要显示的F,但是你说它会不会显示B?会不会?肯定会啊,你这个地方你又没做限制对不对,你一开始是不是说要去加载b.JPG啊,你加载出来就把它显示到image view里面去了吧。等到过一会他又选什么?f.d be是吧?哎,你说的图片有没有一个图片一个视图啊,有没有可能闪动很多次。有可能就是你滑动的那个非常快,是不是假设你的那个item有100个对吧,你快速滑动是不是滑过去掉了你100次get view啊。
11:08
对吧。那是不是复用的次数很多呀?啊,怎么着也有五六次七八次了吧。那也就是说一个视图,它是不是会不间断的去显示七八个图片了。对吧。哎,你说我们真正做得怎么做呀。再说。你想想看。啊,这个当前这个imagine you,它现在要显示的是什么。f.JBG。那个以前的你说还需要去加载,还需要去显示吗?不需要吧,如果他还没有加载,那就不应该加载了。对吧,就是说说什么一个意思呢,其实非常简单啊。在准备。
12:07
嗯,第一个在,嗯,准备加载啊,新的图片之前,新的图片之前。啊,我现在是不是准备去加载新的图片呢?啊,加载这个f.JP但是。啊,大家想。啊,我在加载一个新的图片之前,我是不是得看一下现在当前还需不需要加载我。这什么意思,大家看啊啊,也或者说我要加载一张图片之前。在加载图片之前,比如说我这个啊,现在是不是准备加载B点接P加。但是一定需要加载吗?我准备去加载一个图片呢,不一定,因为它可能有新的任务了,对不对,他可能要显示一张什么新的图片,也就是在分线程啊,或者说在分线程准备加载图片之前。
13:17
减需要什么呢?判断是否。需要加载,这是一个。不知道大家能不能懂啊,这里要判断一下我到底需不需要加载这个图片。对吧,还有一个在主线程准备显示图片。之前你是不是也得判断一下我到底需不要写是吧?对吧,啊,需要判断是否需要显示。
14:05
这有两个非常重要的点,一个是在我分享准备加载数据,因为这个时间耗了很长。一旦你启动加载了这个事情,就会消耗一定的时间片段,那会导致于需要加载的图片没来得及加载,也就是说我这一个B点接VG,如果不需要加载,你去加载了。整个它的时间分辨是固定的吧,CPU能够处理的事情是固定的,对不对啊,你去干了一些暂时我不需要的图片。那就导致于我需要看到的图片更延迟的显示了。还有一个。这个就更第二个种情况就更严重了。本来就不该显示这张图片,但是你又显示了。对吧。那你不就看到图片闪动的一个问题了吗?现在关键是如何判断是吧?
15:03
就如何判断的问题啊,东西都知道了,关键是如何判断是否需要加载,是否需要显示呢?哎,这个其实判断的方式就是判断那个视图是否不用了吧。其实你这判断是否需要加载,是否需要显示,无非去判断视图是否被复用了,那如果它被复用了,我还需要加载吗?不需要,其实本质啊,就是判断图是否已经被复用了,被复用是什么个意思啊,大家想什么情况下才会被复用啊?是不是我在滑动的时候,我不用那个视图来用来显示新的图片呢?去加载新的图片显示吧。
16:00
好,现在就变成另外一个问题了。如何来判断一个视图被复用了?其实就是判断,看you是否被复用呗,对吧,这咋判断呢。这个地方啊,我也别别先说,我给大家写一下,说一下用的那个东西呢,视图里面啊。在视图里面呢,有一个非常重要的属性,那所有任何的视图都会有这样的一个属性,给大家说一下。啊,有两个方法,一个叫set。Tag里面传什么呀,大概啊,这里面有一个属性叫tag。这里面有set,有什么呢?Get t啊set t相当于是保存T什么意思?标签啊,它还有一种叫法,你可以看把它叫成标识。
17:05
其实就是用来保存数据的。嗯,有三角必然会有什么在上面,就在上面,是不是get呀。嗯。它有什么用呢,看到我啊。这一步很关键的,来看看啊,在这个里面呢,我们去做一个事情。看看这里,我们每一次调用get view。啊,都会传过来一个combo BU对吧,一旦试图被复用了,是不是像它不等于空吧。是都被复用的,那在里面怎么检查啊,关键是怎么怎么检查,大家看到每一次get view都会调用它,调用这个方法,我们在这个方法里面我来做一个事情,我写一个我写一个斜段代码。啊,Image view点一个tag。
18:02
等于什么呢?Image pass。打响啊。我现在是不是相当于在image you身上保存了当前最新需要显示的图片的地址啊?好。那就是什么意思呢?大家看我是不是把这一个东西保存他身上呢,是吧,啊把这个等到他们要复用的时候,我现在要保证几啦。是不是保存了f.JB啦,开始保存了b.JP后来变成保存了。f.JBG没问题吧?好。打开。这一个将按图片路径,嗯嗯,图片ul保存到视图身上好了,看着啊。
19:08
我每一次get view都会调用这个方法吧,啊,所以说会保存最新的。大家看啊,保存最新的,我们这往下滑,往下滑看这里。看这个方法在这个里面,它是一个的吧。你说我问一下大家啊,看着我。我呢,假设我在同一个视图身上,所以说我image view,我这我我调了两次啊,假设我调用两次,它先调用一次它啊传入的是一个什么呢?传的是一个呃b.JPG啊什么前面有一个路径啊,最后有一个b.JBG对吧,接着呢,传到一个imageu吧,好后面呢,我复用是个什么意思呢。我是不是又掉了一个这个方法。
20:00
我再调get view,调这个方法吗?只是这一次他传的还是这个image view image view没有变,是同一个啊。但是这一个是什么,这个是点点斜杠什么F点什么JPG吧f.JPG好了你来看啊。这个里面在这个内部一直看到的都是谁,对于他这开始这个方法来说,有一个是看到的是b.JPG,一个看到是什么。f.JPG吧。你说当我这个b.JBG的这个任务在执行的时候,他还需要去执行请求吗。他还需要吗?是不是不需要了吧?因为当前同一个视图,最新想要显示的图片是f.J吧?是这个意思吧,嗯,那我怎么样判断,我怎么知道的呢。
21:04
就是说我在这里看了啊,我是不是准备去发请求了。此时我准备去发请求的,在发请求之前刚才说过了,大家看啊,在分线程准备加载图片之前,需要判断是否需要加载,我们判断的逻辑就是判断是否被复用。对吧,看看啊,在这里面我如何来判断呢。大家看现在这一个还是我的b.JBD吧,但是这个内部的是这个内部他看着我,我呢去调用一下get t。是吧?这个get里面是不是也保存了一个图片路径呢?但它是什么?New,最新的image pass。
22:01
是吧,那个最新的它们俩一定相同吗。理解吗?这个实际上现在谁啦。是我现在在那个对象里面,在这个视图里面看到的是f.JPG了呀。因为是在同一个次数上面掉了两差,那肯定是早已经是F点接be了。对吧。嗯。因为我这个这个方法调过来以后啊,我这个方法执行了,执行了以后,说明我的image view里面是不是早已经设置了它呀。那现在就相当于调用过set tag里面传的是。iff.jvg了。对吧,但是我在方法里面看到的这个参数还是谁呀。D点接BG。那怎么样去判断其实就非常简单了。看到我。
23:01
六我如果不等于看到啊,很少用字符串来相等去比较吧。你这一次我就用等和不等来比较。看这里。嗯,我就这么做,如果它们不相等。那就说明什么啊。是不是试图已经被复用了呀?一旦被复用,就保存了新的路径。对不对,那我还需要做什么吗?我不需要去加载。什么图片的吧,那我怎么办呢?是不是结束啊,但结束我能写吗?能不能?冷不冷啊,不冷,你最多也只能跑回个什么,是不浪啊,是这意思吧?
24:06
嗯,只能跑回那因为它不返回一个对象嘛。能看懂吧。嗯。大家看啊。这个地方在准备请求呃,服务器图片之前,判断是否需要加载。啊,这个判断的依据就是看我的那个。要请求的这个图片,也就是我当前请求的,正在正准备请求的这个图片路径,跟我当前image view里面需要显示的这个图片路径。是否一致?如果不一致,那就没有必要了。好了吧,这是这一步,还有一步需要去做哪一步呢?大家看到。
25:04
在主线程准备显示图片之前。你需要去判断一下。是否需要显示,那你说在哪做啊?是不是这里做呀,好来做看了啊,这个做法其实跟上面还是什么。不一样啊,你这里面你这个方法看到的跟他一样的,还是说把这一段代码。再来一次。啊,再来一次来看呢,这个地方就直接。是不是结束了呀,因为它不需要返回值吗。大家看着啊。在主线程,如果说他们是有变化的。那说明视图已经被复用了。那你有人说前面你不整过了吗?后面为什么还要整呢?
26:01
大下啊。我这是我的主线程,我是不是启动了很多个分线程去加载图片了。好。启动分线程去加载图片,它总要有选择性的,我启动了,启动了并不代表执行吧,啊,我开始来执行它可以吧,好,接着呢,我准备去执行它。此时呢,图片还没被复用,有可能吧,这很有可能。我加载是不是需要一定的时间啊,又花了一定的时间,当我真正加载完了以后。这个中间是不是已经隔了两秒了呀。在这个两秒的时间内,我这个视图有没有可能被复用啊?是不是有可能就两种时间,只要有时间间隔就可能被复用,也就是说我从启动到运行这个中间是不是有时间是吧。一个线程从启动到运行。
27:00
是需要一定的时间的,因为这是靠CPU去选择你吧,我一下子启动了十个线程。那你这个线程总要选择,我选先选择一执行,选择二执行,选择四执行,那三可能过一会才执行。对吧,只要你中间有一定的时间间隔,就有可能被复用。对不对。啊,大家看着啊。从我到我这一,我问一下大家,你说我这个方法在哪个线程执行呢。这个方法。哪个县的?啊。这都这都反应过来,哪个现在呢。在主食,这明显在主,这不这个方法在哪个方法里面调用的。Get view嘛,那不就是主线程执行的吗?对吧,他们这个load,你说load image有没有可能异步执行,就是说大家看到啊,这一个是序列化执行的,因为它这个这个所有这个方法是不是都是在主线上执行。
28:05
我虽然是这个方法,执行的次数多吗,大家说。多不多?是不是多呀,因为它是在get view里面执行嘛,Get view是不是非常迅速的在调用啊。对吧,但是这个方法必须是序列化的执行。什么叫虚拟化的执行啊?执行完一次以后,完成以后再执行什么第二个,第二次漏明白方法吧,再执行什么第三次,再执行第四次对不对。这主线程的方法肯定是序列化执行的吧,这个没问题。啊,但是我的这些分线程。这就不好说了。对不对。他这一个这一个方法什么时候执行。谁能保证呢,是不能保证呢。
29:00
对吧,从我启动,从我执行这个方法,你就说这个方法执行是不是也是序列化的呀,他也是在主线上执行的。对不对。包括这一个也是,包括它也是。他也是在主线执行呢。关键就是它这一个在分线上执行,在分线上执行是不是就有可能有延迟啊。啊,可能,嗯,需要等待。嗯,等待,诶诶诶诶。嗯,等待一定的时间才会什么执行,那在这个等待的过程中。来看着啊,在等待的过程中就可能出现什么。
30:08
过程中,我的image view。看着我,Imagine you中的T值是不是有可能改变了?就有可能改变了,它改变了说明什么呀。你说它它tag,它里面内部的中的tag改变了说明什么呢。是不是说明我的image又被复用了,它需要显示新的图片呢?也就是说我们说这个它用来保存的是什么。将图片URL保存到这是最看到啊,需要显示的图片的UR保存到这上面了吧。嗯,那由于我们这地方有一定的时间间隔,大家看着啊。
31:03
有一定的时间间隔,因为它这这里面有一个间隔时间,那在等待的过程中,我的imagine u中的T值就有可能变化了。那如果它已经变化了,那我当前这一个。Image pass所对应的图片就不应该被加载吧?嗯,你就当前这个任务应该要被取消。对吧。就不应该再去加载图片,嗯。哎,你说为什么不要加载这图片呢?就是这个图片,我要加载图片,是不是应该我马上要显示的,但是你现在还需要显示这张图片吗?不需要此图片。
32:00
此时不能显示,不需要显示,你就算只是拿到了,你也不能显示出来,因为当前我需要显示的是别的图片。对吧。好了。但是呢,很多时候如果我不快速滑动的话,这个东西是不是应该要正常的执行呢。好,正常执行。我这一个是不是也需要一定的时间。嗯,这个时候又经历了一定的时间,也就是说从我去请求图片。但我真正得到图片。这个需要一定的时间。也就在这。从呃联网请求图片。嗯,到什么呢?到啊,得到图片显示图片对象,它是需要一定的时间的。
33:07
我们这地方不需要两秒钟吗?需要一定的时间啊,在这个时间段呢,它也是有可能什么。你你要一旦有有间隔时间,那不就可能被复用吗。对吧。而且我消耗的是在分线程消耗时间,还是在主线程消耗时间呢?啊,我有有去占用主线程吗?没有吧,这主线程也就是说主线程没有占用的话,我人就可以滑动吧。对不对,我人可以滑动,那当然就有可能变化呀。对吧。嗯。视图。一样可以被复用。试图可能。嗯,被复用了。啊,不需要显示。
34:06
但是我问一下大家,你说呃,我这个地方发现你你这地方我是不是还需要缓存吗。我得到图片以后。你假设在这里面,我我去比较一下他,如果发现他是被复用了,我需要缓存吗。啊,我得到一个图片,我发现不需要显示啊。缓不缓,人啊,管还是不管?不缓存了,不缓存那就代表你下次你要再去发请求。要还吗?那肯定得缓存了,你好不容易得到图片,你不你不保存为文件呐。你请求了两两分钟才得到一张图片,虽然说暂时不需要显示,对不对,那个用户是不是有可能划回去啊。你有可能下次再写是吧。你保存在一级缓存是为了什么呀?是不是再划回去?
35:05
那你保证20块钱是为了什么?你下次再启动你这个应用的时候,你是不是得去读图片呢。对不对。一级缓存是为了快速,就是缓步的回来,当我回来再显示的时候,能快速的显示,二级缓存呢,是为了你即使把手机关了,你再开机再起来。它也是能快速的显示的。嗯。有人可能在想,那个,呃,那个那个新闻客户端好像不这么做的,新闻客户端之所以不这么,他是得到了新的新闻,就是说你每一次开机是不是他会去展示一个新的新闻呢。那以前呢,如果你没联网。它能显示数据吗?能,那是上次的数据吧,能显示图片吗?能,还是上次的图片对不对?
36:02
嗯。那三个图片是在内存里面吗?不是,那肯定不是在内存里面,你的重新启动了对不对,内存早已释放了,不可能啊,所以说它是放在什么,是不是SD卡里面呢?啊,那个是不在意你,嗯,关机或者开机的对吧?但是如果你真的联网了,它就不一样了,联网它是不是加载最新的数据啊,那最新的数据是不是有最新的图片。那跟这个就不太一样了啊,那你如果新的数据,那肯定要显示新的图片。嗯。好。这一个啊,刚才我们就做了三步啊,记住了。再跟大家说一遍啊,第一步。保存标识。对吧。你只有保证标识,到时候才有判断。第二步。在你去准备去请求服务器之之前,就准备加载图片之前。
37:04
你要判断一下。啊,是否需要加载。其代码没有几行,说实话一共就不超过十行代码。第三步,就在你显示之前。是否需要显示?就这么简单的事。这个很有可能啊,基本上可以说,你只要是去面试,我我我觉得。不说,不说一半吧,至少有1/3的机会是有可能说到这个问题的。这个几率应该说不低的吧。啊,就是你有1/3的机会会说到关于list view的一些啊一些问题,那list view的问题无非不就是说缓存吧,对吧,还包括后面要学的一些东西啊,什么动态加载分页还记得吗。
38:00
大家知道分页的概念吗?当然这个是用的比较多的,对吧,加载更多就上它加载更多。那就是分页,其实就是分页的效果对不对啊,这也是list view里面可说的一个东西啊,包括啊我们下拉刷新就顶部,大家到了顶部以后,是不是可以往下一拉,它上面出现一个箭头。一个向上的,一个向下的箭头在在那转见过吗?难道我不信就没有有没见过的,有有没见过的吗?基本上啊,很多基本上的应用都会有这样的效果。只要是个联网的应用,基本上都有。好,这是这一个,大家下周来好好的看一下啊。
我来说两句