reoger的记录

--以后的你会感激现在那么努力的自己

0%

android四大组件

activity的生命周期

activity的生命周期图

activity的四种启动模式

Standard(默认)、singleTop(栈顶复用)、singleTask(栈内复用)、singleInstance(单实例)。
当然,不但可以通过在AndroidManifest.xml中声明launchMode,也可以通过指定Activity的Flags来设置启动模式。例如:
FLAG_ACTIVITY_NEW_TASK 相当于singleTask模式;
FLAG_ACTIVITY_SINGLE_TOP 相当于singleTop模式;
FLAG_ACTIVITY_CLEAR_TOP 标记的activity在同一个任务栈中所有位于它上面的activity都要出栈;
FLAY_ACTIVITY_EXCLUDE_FROM_RECENTS 标记的activity不会出现在历史的activity列表中。

activity的四种状态

runing、paused、stopped、kille

activity的启动方式

显式和隐式启动。其中隐式启动的匹配规则:
intentFilter中过滤信息有action、category、data。
Intent中的action能够和过滤规则中的任何一个action相同即可;
Intent中所有的category都必须和过滤过则中的某一个category相同;
Intent中的data能够和过滤规则中的任何一个data相同即可。
在隐式启动的时候,可以用packageManager的resolveActivity或者Activity的resolveActivity方法先判断是否有activity相匹配,避免异常发生。

activity的启动流程

参考这里

activity异常停止时保存数据的方法

可以在onSaveInstanceState(在onStop之前被调用)中将要保存的数据保存起来,可以通过Bundle进行临时保存,然后在onCreate中的bundle中取出来进行恢复,但是oncreate中进行数据恢复要注意判空,也可以在onRestoreInstanceState()取出数据进行恢复(这里就不要判空,因为只要回调这个方法就一定不为空)。参考。

Activity、Windows、View三者的区别和联系。

Activity是控制单元,通过attach方法创建window对象;window是承载模型,负责承载视图;view是要显示的视图,必须依附于window;
Window是一个抽象类,他的具体实现是phoneWindow,Android中所有的视图都是通过window来呈现的,不管是activity、Dialog还是Toast,他们的视图实际上都是附加在window上的,因为window实际是view的直接管理者。
最后总结一句话:activity像一个工匠(控制单元),window向窗户(承载模型),View像窗花(显示视图)。

activity间通过Intent传递数据的大小限制

肯定是有限制的,大约大1m(1020k)左右。但是官方并没有给出说明。

Fragment的生命周期

fragment生命周期图

添加fragment的步骤。

1
2
3
4
5
6
7
8
9
10
11
12
//步骤1、创建待添加的的fragment实例
MyFragment m = new MyFragment();

//步骤2、开始一个事务
FragmentManager fragmentManager = getFragmentManager(); //获取fragment对象
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); //利用fragment开启事务

//步骤3、向容器内加入碎片,可以通过add或者replace方法实现。
fragmentTransaction.add(R.id.fragment, m);

//步骤4、提交事务
fragmentTransaction.commit();

添加Fragment时方法

一般有两种方法添加碎片,即通过replace和add方法,下面是两种方法的区别:
通过add方法添加的fragment,每个fragment都只能添加一次。因此如果想达到切换的效果需要通过fragment的hide和show方法结合使用。将要显示的show出来,将其他hide起来,这个过程fragment生命周期没有变化。
通过replace方法添加fragment,每个fragmnet可以添加任意次,因为每一次添加都会先将之前的fragmemnt移除,然后在添加。因此切换fragment,每次都会执行上一和fragment的onDstoryView,新的Fragemnt的onCreateView、onStart、onResume方法。

Activity和Fragment之间的区别。

1. Fragment显得更加灵活,可以在xml文件中添加,动态的替换一部分界面,activity则不行;
2. Fragment不需要在manfest中申明,而activity需要。

Activity与Fragment通信方案。

  1. handler:该方案存在的缺点:
    Fragment对具体的Activity存在耦合,不利于Fragment复用
    不利于维护,若想删除相应的Activity,Fragment也得改动
    没法获取Activity的返回数据
  2. 广播方案,缺点:
    用广播解决此问题有点大材小用了,个人感觉广播的意图是用在一对多,接收广播者是未知的情况
    广播性能肯定会差
    传播数据有限制(必须得实现序列化接口才可以)
  3. EventBus方案:
    EventBus是用反射机制实现的,性能上会有问题
    EventBus难于维护代码
    没法获取Activity的返回数据
  4. 接口方案
    假如项目很大了,Activity与Fragment的数量也会增加

Service的生命周期。

Service的生命周期分为两种,一种是直接利用startCommand启动,另一种是通过bindService来启动。具体如图所示。值得注意的是,在已经启动的service,我们重新启动的时候,不会执行onCreate方法,而是直接执行onStartCommand方法。
服务生命周期
左图显示了使用 startService() 所创建的服务的生命周期,右图显示了使用 bindService() 所创建的服务的生命周期。

Service和Thread的区别

Service是安卓中系统的组件,它运行在独立进程的主线程中,不可以执行耗时操作。Thread是程序执行的最小单元,分配CPU的基本单位,可以开启子线程执行耗时操作;
Service在不同Activity中可以获取自身实例,可以方便的对Service进行操作。Thread在不同的Activity中难以获取自身实例,如果Activity被销毁,Thread实例就很难再获取得到。

Broadcast Receiver的种类

  • 普通广播
  • 有序广播
  • 本地广播
  • Sticky广播

注册广播有几种方式?他们有什么区别?

广播注册分为静态注册和动态注册,其中的静态注册的广播在应用安装时由系统自动完成注册,具体来说是由PMS来完成整个注册过程。
静态注册属于常驻型广播,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行(在android 3.1以后就可能不成立了,加入了一个flag位)。动态注册不是常驻型广播,也就是说广播跟随程序的生命周期。

Broadcast Receiver实现机制

  1. 自定义广播类继承BroadcastReceiver,复写onReceiver()
  2. 通过Binder机制向AMS进行注册广播
  3. 广播发送者通过Binder机制向AMS发送广播
  4. AMS查找符合相应条件的广播发送到BroadcastReceiver相应的循环队列中
    消息队列执行拿到广播,回调BroadcastReceiver的onReceiver()

LocalBroadcastManager特点

  • 本地广播只能在自身App内传播,不必担心泄漏隐私数据
  • 本地广播不允许其他App对你的App发送该广播,不必担心安全漏洞被利用
  • 本地广播比全局广播更高效
  • 以上三点都是源于其内部是用Handler实现的

ContentProvider是如何实现数据共享的。

在android中如果想就将自己应用的数据(一般多位数据库中的数据)提供给第三方应用,那么我们只能通过ContentPrivider来实现。
ContentProvider是应用程序之间共享的接口,使用的时候首先自定义一个类继承ContentProvider,然后覆写query、insert、update、delete等方法,因为其是四大组件之一,因此必须在AndroidManifest文件中进行注册,把自己的数据通过uri的形式共享出去。第三方可以通过ContentResplver来访问该Provider。

Application的生命周期?

onCreate() –> 创建application –> onTerminate() –> 结束。
其中,在application中,可能会回调onConfigurationChanged()(在设备配置发生改变的时候调用)、onLowMemory()(手机内存低时调用))、OnTrimMemory()(在操作系统清理内存的时候时候调用)。

View相关面试题

简述View的事件分发机制。

当一个点击事件产生后,他的传递过程应该是:Activity –> windos –> View;
顶级View接到事件后,就会按照事件分发机制去分发事件,具体如下图所示。
值得注意的是,ViewGroup默认不拦截任何事件,而子View中没有OnIntercepterTouchEvent方法,一旦有事件传递给他,那么他的onTouchEvent就会马上调用。某个View一旦决定拦截事件,那么这一个事件序列都只能由他来处理。
简单来说:点击事件分发过程如下 dispatchTouchEvent—->OnTouchListener的onTouch方法—->onTouchEvent–>OnClickListener的onClick方法。也就是说,我们平时调用的setOnClickListener,优先级是最低的,所以,onTouchEvent或OnTouchListener的onTouch方法如果返回true,则不响应onClick方法。
Touch事件传递机制流程图

view测量的宽/高和实际上的宽/高是一样的吗?

不一定一样,虽然在大部分情况下是一样的,但是在某些情况下会导致两者的值不相同。
在View的默认实现中,View的测量宽/高和最终的宽/高是相等的,只不过测量宽/高形成于measure过程,而最终宽/高形成与View的layout过程,即两者的赋值时机不同,测量宽/高的赋值时机要稍微早一点。当我们重写layout方法时,可以导致测量的宽/高和实际的宽/高不同(虽然没有什么具体的含义,但是证明了view测量的宽/高和实际的宽/高是可以不相同的)。

在activity启动的时候就需要获取某个View的宽/高,该怎么做?

注意,我们一定不能在onCreate、onStart、onResume中去获取。因为View的measure过程和Activity的生命周期方法不是同步的执行的,因此无法保证Activity在某个生命周期方法中这个View已经测量完毕了。
我们可以在onWindowFocusChanged方法中获取;我们也可以通过view.post(runnable)将runnable投递到消息队列的尾部,然后等待Looper调用此runnable获取View的高和宽;我们也可以使用ViewTreeObserver的众多回调来获取View的高,例如使用onGlodalLayoutListener这个接口。最后,我们当然也可以手动对view进行measure来得到View的宽/高。

自定义View要注意哪些地方?

让view支持wrap_content;
直接继承View或者ViewGroup的控件,如果不在onMeasure中对wrap_content做特殊的处理,那么当外界在布局中使用wrap_content时就无法达到预期的效果。
让view支持padding和margin;
直接继承view的话,如果不在draw方法中处理paddding,那么padding属性是无法起作用的。另外,直接继承ViewGroup的控件需要在onMeasure和onLayout中考虑padding、
和子元素margin对其造成的影响,不然将会导致这两个属性失效。
尽量不要在View中使用handler,view中如果有线程或者动画,一定要及时停止,否则可能会造成内存泄露;
View带有滑动嵌套的时候,要注意处理滑动冲突。

自定义view的步骤。

简单来说,主要有三大步骤:

  • onMeasure
  • onLayout
  • onDraw
    具体详细,可以参考博客

动画相关

Android中的三种动画。

Android中的动画有三种:View动画、帧动画和属性动画。
View动画的作用对象是View,他支持4中动画效果:分别是平移动画(TranslateAnimation)、缩放动画(ScaleAnimation)、旋转动画(RotateAnimation)和透明度动画(AlphaAnimation)。
帧动画是顺序播放一组预先定义好的图片,类似于电影播放。
属性动画是在API 11新加入的特性,属性动画可以对任何对象做动画,甚至可以没有对象。除了动画对象进行了扩展以外,属性动画的动画效果也进行了加强,不像View动画只支持四种动画。

可以对任意属性做动画吗?

可以。如果想对Object的属性abd做动画,如果想让动画生效,要同时满足两个条件:
object必须要提供setAbd方法,如果动画的时候没有传递初始值,那么还要提供getAbd方法,因为系统要去取abd属性的初始值(如果这个条件不满足,程序直接Crash);
object的setAbd对属性adc所做的改变必须能够通过某种方法反映出来,比如会带来UI的改变之类的(如果这条不满足,动画无效果但不会Crash)。
针对上面的两个条件,google官方告诉我们三种解决方案:
给你的对象加上get和set方法,如果有权限的话;
用一个类来包装原始对象,间接为其提供get和set方法;
采用ValueAnimator,监听动画过程,自己实现属性的改变。

使用动画的注意事项。

在开发的过程中尽量少用帧动画,避免OOM;
在activity退出时及时停止动画,避免内存泄露;
在android3.0以下的系统中使用属性动画需做好适配工作;
在view动画中,view只是对view的影像做动画,并没有真正的改变view的状态;
使用动画的时候,建议开启硬件加速(在声明文件中添加android:hardwareAccelerated=”true”属性即可),提高动画的流畅性。

怎么为activity设置切换效果,怎么给ViewGroup子元素添加子元素的出场动画?

Activity有默认的切换效果,但是这个效果我们可以自定义的,主要用到overriderPendingTransition()这个方法,这个方法必须在startActivity或者finish()之后调用才能生效。至于Fragment的切换效果,我们可以通过setCustormAnimation方法来添加切换动画。
至于在ViewGroup中为子元素出场添加动画,可以在xml中对布局的LayoutAnimation引用相应的动画资源文件即可,或者我们也可以在java中利用LayoutAnimationController将获取到的动画资源文件设置到viewGroup中即可。

动画中的差值器和估值器怎么理解?

时间插值器(TimeInterpolator)的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有LinearInterpolator(线性插值器:匀速动画),
AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快),
DecelerateInterpolator(减速插值器:动画越来越慢)。
估值器(TypeEvaluator)的作用是根据当前属性改变的百分比来计算改变后的属性值。系统预置有IntEvaluator 、FloatEvaluator 、ArgbEvaluator。
具体来说 对于一个作用在view上改变其宽度属性、持续40ms的属性动画来说,就是当时间t=20ms时,时间流逝了50%,那么view的宽度属性应该改变了多少呢?这个就由Interpolator和Evaluator的算法来决定。

滑动冲突是如何产生的,该如何避免?

在界面中只有有内外两层同时可以滑动,这个时候就产生了滑动冲突。例如,scrollView嵌套一个listView,因为listView和scrollView都是可以滑动的,这个时候就出现了滑动冲突。
常见的解决滑动冲突的方法有两种:外部拦截和内部拦截。
外部拦截法是指点击事件都先经过父容器的拦截处理(ACTION_DOWN不能被消耗),如果父容器需要此事件就拦截,如果不需要此事件就不拦截,这样就可以解决滑动冲突问题,这种方法比较符合点击事件的分发机制。外部拦截法需要重写父容器中的onIntercepetTouchEvent方法,在内部做相应的拦截即可。
外部拦截法是指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器进行处理,这种方法和Android分发机制不一致,需要配合requetDisallowInterceptTounchEvent方法才能正常工作,使用起来较外部拦截法稍显复杂。

通信机制

IPC指什么?android中实现IPC有哪些

IPC(Inter-Process Communication),含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。
Android中主要通过Bundler(用于android四大组键间的进程间通信)、文件共享、AIDL、Messenger、ContentProvider和Socket来进行进程间通信。

Binder指什么?

直观的说,binder是android中的一个类,他实现了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式;从android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。

Binder机制

参考这里.

进程间通信中可以使用sharedPreferences吗?

不能。虽然从本质上来将,sharedPreferences也属于文件的一种,但是由于喜用对他的读/写有一定的缓存策略,即在内存中会有一份sharedPreferences文件的缓存,因此在多进程模式下,系统对他的读/写就变得不可靠,当面对高并发的读/写访问,sharedPreferences有很大的几率会丢失数据,因此,不建议在进程间中使用sharedPreferences。

AIDL支持的数据类型有哪些?

除short以外的基本数据类型(int、long、char、boolean、double等);
String和CharSequence;
ArrayList、HashMap:里面的每个元素都必须被AIDL支持。
Parcelable: 所有实现了Parcelable接口的对象。
AIDL: 所有的AIDL接口本身也可以在AIDL文件中使用。

handler原理

Handler创建的时会采用当前线程Looper来构建内部的消息循环系统,如果当前线程没有Looper,那么就会报错。Handler创建完毕后,这个时候其内部的Looper以及MessageQueue就可以和Handler一起协同工作了,然后通过Handler的post方法将一个Runnable投递到Handler内部的Looper去处理,也可以通过Handler的send方法发送一个消息,这个消息同样会在Looper中去处理。其实post方法最终也是通过send方法完成的。Send方法被调用时,MessageQueue的enqueueMessage方法将这个消息放入消息队列中,然后Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或者Handler的handlerMessage方法就会被调用。注意,Looper是运行在创建Handler所在的线程中,这样依赖Handler中的业务逻辑就被切换到创建Handler所在的线程去执行了。
handler原理

AsyncTask的工作原理。

AsyncTask是对Thread和Handler的组合包装,方便我们在后台线程中执行操作,然后将结构发送给主线程,从而在主线程中进行UI更新等操作。
实现原理,AsyncTask中有两个线程和一个Handler,其中一个线程池(SerialExecuor)用于任务的排队,另一个线程池(THREAD_POOL_EXECUTOR)用于真正的执行任务。InternalHandler用于将要执行的环境从线程池切换到主线程。关于AsynchTask有以下几点需要注意:AsyncTask的对象必须在主线程中创建;execute方法必须在UI线程中调用;一个axyncTask对象只能执行一次,即执行一次execute方法;在android 1.6之前AsyncTask是串行执行任务的,而在android 1.6的时候开始采用线程池里处理并行任务,但在android 3.0以后,为了避免并发错误,asyncTask又采用了一个线程来穿行执行任务,同时也提供一个串行执行任务的方法executeOnExecutor方法。

HandlerThread是什么?简述原理并举出一个应用的例子。

HandlerThread是一种可以使用Handler的Thread。他的实现也很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际的使用中就允许在HandlerThread中创建Handler了。
HandlerThread在Android中的一个具体的使用场景是IntentService。IntentService封装了HandlerThread和Handler。

IntentService使用和原理。

IntentService是一种特殊的Service,他继承了Service并且他是一个抽象类,因此必须创建他的子类才能使用IntentService。IntentService可用于执行后台耗时的任务,当任务执行后他会自动停止,同时由于IntentService是服务的原因,这导致他的优先级比单纯的线程要高很多,所以IntentService比较适合执行一些高优先级的后台任务。
原理上来说,IntentService封装了Handler和Handler。在onCreate方法中创建一个HadnlerThread,然后使用它的Looper来构建一个Handler对象mServicehandler,这样通过mServiceHandler发送的消息最终都会在HandlerThread中执行。而在OnStart方法中,IntentService仅仅只是通过mServiceHandler发送了一个消息,这个消息会在HandlerThread中被处理。

android中的线程与线程池

Android中的线程池有什么优点?

可以重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销;
能有效控制线程池的最大并发数,避免大量的线程之间因互相抢夺资源而导致的阻塞现象;
能够对线程进行简单的管理,并提供定时执行已经指定间隔循环执行等功能。
ThreadPoolExecutor是线程池的真正实现。

性能优化

什么是ANR,产生的条件有那些

ANR即Application not responding,应用程序无响应。
产生条件:在Ui线程中5s无响应,在broadcastReceiver中10s无相应,或者在service中20s无相应都会导致ANR。
解决方法:运行在主线程里的任何方法都尽可能少做事情,耗时的操作应该放在子线程中进行操作,然后通过hander或者其他方法来通知主线程。
应用程序应该避免在broadcastReceiver里做耗时的操作或者计算,代替的是,如果想用Internt广播需要执行一个耗时的动作的话,应用程序应该启动一个servicer。

造成ANR的主要原因

主线程被IO操作阻塞

  • Activity的所有生命周期回调都是执行在主线程的
  • Service默认执行在主线程中
  • BoardcastReceiver的回调onReceive()执行在主线程中
  • AsyncTask的回调除了doInBackground,其他都是在主线程中
    没有使用子线程Looper的Handler的handlerMessage,post(Runnable)都是执行在主线程中

OOM指什么?什么时候会出现OOM。

OOM指Out of memory(内存溢出),当前占用内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出Out of memory异常
出现OOM的大部分情况都是内存泄露引起的、也有可能是操作系统本身内存就不足了。
这里只介绍内存泄露。

内存泄露出现与避免

总的来说,出现内存泄露的原因是本来应该被GC回收的资源,因为某种原因不能被回收,而导致内存越来越小,进而导致内存泄露,最终可能会导致OOM。
出现内存泄露一般来自于两个原因:没有即使释放分配的内存(Cursor没有及时关闭);
当应用不再需要这个对象,却没有释放该对象的所有引用。
检测内存泄露可以通过android studio的monitors工具,生成hprof文件,我们可以通过分析这个文件来分析内存泄露。当然,在android studio中分析内存泄露不是很直观,专业一点的工具就是MAT,我们可以将我们得到的hporf文件导入到MAT中进行分析。
当然,我们也可以通过在android项目中使用leakCanary来检测内存泄露。当应用程序出现内存泄露的时候,leakCanary会在通知栏通知我们,而且会提示我们内存泄露的相关信息,可以让我们迅速定给到内存泄露的的位置和原因。

具体说明什么时候出现内存泄露

  1. Static 变量没有及时清空
    但在类中定义了静态的Activity变量,把当前运行的Activity实例赋值给给这个静态常量。如果这个静态变量在activity的生命周期结束后没有清空,即会导致内存泄露;
  2. 注册了没有及时解注册
    如动态注册中,如果我们在activity中注册了广播,在activity销毁之前就应该解注册,否则就会出现内存泄露;
  3. 资源或者属性动画没有及时关闭
    在一类属性动画中,可以无线循环播放,如果在activity中播放此类动画且在activity生命周期中没有停止动画,那么动画会一直播放下去,尽管我们已经看不到效果了。至于资源回收典型的例子就是数据的Cursor了,当操作完数据库没有及时关闭Cursor也可能会导致内存泄露;
  4. 静态类或者内部类导致的内存泄露
    这类导致的内存泄露究其原因也是因为static变量没有及时回收,这里不做介绍;
  5. Handler或者AsyncTask导致的内存泄露
    我们试想一种情况,在我们的activity结束之后,handler又给Ui线程发送了一个消息,或者是在activity结束之后,我们的AsynchTask任务才执行完。这两种情况都会导致activity实例无法被回收,导致内存泄露。

如何处理应用可能存在的crash

一般实现Thread类中UncaughtExceptionHandler对象,在发生崩溃时,系统会调用UncaughtExceptionHandler的uncaughtException方法,在uncaughtException方法中可以捕获到异常信息,可以选择把异常信息存储到SD卡中,然后在合适的时机通过网络将crash信息上传到服务器,这样就可以分析crash的原因了。当然,在crash发生时,我们可以先弹出一个对话框告诉用户crash了,然后在退出。

新技术

kotlin语言的优势

  • 简洁性
    例如在kotlin中的数据类会自动获得所需的getter、setters、toString
  • 安全性
    对象不能为空,避免出现空指针异常。
  • 更优雅,遵循Effective Java设计
  1. 类默认不可继承
  2. 更有效的使用构建器模式
  3. 默认提供单利模版
  4. 重载必须使用Override
  • 完全兼容Java

开源库源码解析

gile相关问题

如何使用OkHttp进行异步网络请求,并根据请求结果刷新UI

##介绍一下OkHttp的整个异步请求流程
前面两个问题都比较简单,具体可以参考博客中的解答。

##OkHttp对于网络请求都有哪些优化,如何实现的

OkHttp框架中都用到了哪些设计模式

建造者模式、

Okhttp的优势与劣势

Android 开发中是可以直接使用现成的api进行网络请求的。是Square公司开源的针对Java和Android程序,封装的一个高性能http请求库,它的职责跟HttpUrlConnection 是一样的,支持同步、异步,而且 OkHttp 又封装了线程池,封装了数据转换,封装了参数使用、错误处理等,api使用起来更加方便。可以把它理解成是一个封装之后的类似HttpUrlConnection的东西,但是在使用的时候仍然需要自己再做一层封装,这样才能像使用一个框架一样更加顺手。

Volley VS OkHttp

volley不支持Post大数据,所以不适合上传文件。
okhttp支持同步、异步,封装了线程池,参数使用、错误处理等。
volley封装的扩展性强,支持HttpClient、HttpUrlConnection, 甚至支持OkHttp,而且Volley里面也封装了ImageLoader。
Okhttp基于NIO和OKio,所以性能上要比Volley更快,功能更加齐全,只是使用需要进一步的封装。


Gilde相关问题

更多的android面试资料

glide是一个快速,高效的开源媒体管理和Android的图像加载框架,将媒体解码,内存,磁盘缓存和资源池封装成一个简单易于使用的接口。

相对于同为图片加载的ImageLoader、Picasso来说,他支持的图片格式更加齐全(包括video、GIF、SVG等),支持缩略请求,内存管理也更加优秀。所以现在对其源码进行一个比较简单的分析。

源码导入

本次源码解析均基于3.7.0的版本。在build.gradle中添加如下的依赖:

1
compile 'com.github.bumptech.glide:glide:3.7.0'

点击同步,然后我们就可以开始我们的源码解析之旅了。
如果想看更多的版本信息,可以去他的github主页进行查看选择。

简单使用示例

相信使用过gilde来加载图片,对于gilde的使用都很清楚吧。下面是加载一张图片到ImageView中的关键代码:

1
2
ImageView imageView = (ImageView) findViewById(R.id.imageView);
Glide.with(this).load(R.mipmap.ic_launcher).into(imageView);

下面我就上面的简单的使用进行源码分析。

源码分析

1
Glide.with(this).load(R.mipmap.ic_launcher).into(imageView);

总体设计图

资料来源网络
资料来源于网络

Glide.with()

我们首先来看Glide中的第一步,with方法的实现。源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}

public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}

public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}

public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}

可以看到,with有很多中重载,我们可以在with中传入Context,Activity、Fragment等等参数。但是在这些重载方法中,实现的逻辑大都差不多,首先是构建一个RequestManagerRetriever对象,然后调用get方法。那么我们先来看看RequestManagerRetriever.get();构建出来的对象的作用是什么。
继续看源码RequestManagerRetriever.get()

1
2
3
4
5
6
7
8
9
10
11
/** The singleton instance of RequestManagerRetriever. */
private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();

public static RequestManagerRetriever get() {
return INSTANCE;
}

// Visible for testing.
RequestManagerRetriever() {
handler = new Handler(Looper.getMainLooper(), this /* Callback */);
}

通过上面的源码我们可以看到,RequestManagerRetriever.get()返回的就是一个静态的RequestManagerRetriever对象,这个对象初始化的时候就是只是创建了一个Handler。那么这个Handler什么时候触发,我们后面在讲。
我们注意到在with()方法中,我们最后返回的是retriever.get(activity)或者retriever.get(fragment),具体实现如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {//如果是在后台线程
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}

public RequestManager get(Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}

return getApplicationManager(context);
}

可以发现,如果当前是在后台线程的话,get()方法会返回一个get(activity.getApplicationContext())。我们继续来看getApplicationManager(context)的实现逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private RequestManager getApplicationManager(Context context) {
// Either an application context or we're on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or activity.
// However, in this case since the manager attached to the application will not receive lifecycle
// events, we must force the manager to start resumed using ApplicationLifecycle.
applicationManager = new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}

return applicationManager;
}

可以看出,getApplicationManager方法通过使用双重锁的单例模式通过RequestManager来实例化一个applicationManager,并将当作返回值。关于这个applicationManager我们稍后在做讨论。
在当前线程不是在主线程时,而且在get()方法中传入的不是Application对象时,会根据传入的对象来调用RequestManagerRetriever中重载的get()方法。
以传入的对象是Activity为例子,那么调用的就是get((Activity) context),下面是该方法的实现逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);//判断activity是否已经销毁了
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm);
}
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
RequestManagerFragment current = getRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
//这里handler发送了一个消息,这里就是handler发送消息的时机
}
}
return current;
}

//这里就是handler回调的时机,主要用于移除容器中的对象。
@Override
public boolean handleMessage(Message message) {
boolean handled = true;
Object removed = null;
Object key = null;
switch (message.what) {
case ID_REMOVE_FRAGMENT_MANAGER:
android.app.FragmentManager fm = (android.app.FragmentManager) message.obj;
key = fm;
removed = pendingRequestManagerFragments.remove(fm);
break;
case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER:
FragmentManager supportFm = (FragmentManager) message.obj;
key = supportFm;
removed = pendingSupportRequestManagerFragments.remove(supportFm);
break;
default:
handled = false;
}
if (handled && removed == null && Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Failed to remove expected request manager fragment, manager: " + key);
}
return handled;
}

总的来说,这里逻辑还是比较简单。首先在get()方法中,判断是否在后台线程且判断以下当前的版本。如果不是在后台线程且当前版本低于11,那么就会先判断当前activity是否销毁了,然后调用ragmentGet(activity, fm)方法。在ragmentGet(activity, fm)方法中,首先会调用getRequestManagerFragment(fm)来创建RequestManagerFragment对象,在getRequestManagerFragment方法中,首先创建一个RequestManagerFragment,然后将该对象放入到一个名为pendingRequestManagerFragments的hashMap对象中,然后通过handler发送一个消息。
handleMessage中,收到这个消息之后,就会将RequestManagerFragmentpendingRequestManagerFragments的hashMap中移除。

这里有没有想这么一个问题,为什么要存在pendingRequestManagerFragments这么一个容器呢?当我们创建一个RequestManagerFragment之后,马上将其添加到pendingRequestManagerFragments中,然后运行commitAllowingStateLoss方法之后,又马上通过Handler发送一个消息让其从pendingRequestManagerFragments容器中移除,竟然如此,那何不如直接new一个RequestManagerFragment,然后直接提交就好了,何必用这个容器呢?以我的理解,这个gilde开发者为了增加gilde的可靠性而设计的,梳理第25-36行的逻辑如下:首先通过findFragmentByTag来查找RequestManagerFragment,若其返回空,继续下面的逻辑,否则直接返回找到的RequestManagerFragment对象。在findFragmentByTag返回空的情况下,会继续用get()方法来从容器中查找RequestManagerFragment对象,如果还是空就重新创建一个RequestManagerFragment,并将其添加到pendingRequestManagerFragments容器中,然后执行commitAllowingStateLoss方法,随后通过handler发送消息将pendingRequestManagerFragments容器中的该条记录移除。那么为什么要用pendingRequestManagerFragments呢?道理很简单,避免快速多次调用getRequestManagerFragment时,重复创建RequestManagerFragment对象。试想一下,当我们运行完第30行的时候,第二个getRequestManagerFragment请求已经到了,这个时候,第二个请求需要的RequestManagerFragment,就可以通过get()方法获取,而不要去重新创建。当运行完第31行时,第二个getRequestManagerFragment就可以直接通过findFragmentByTag方法获取。这种情况是可能产生的,设想这样一种情况:当我们在Activity A和B都同时有Gilde.with(),我们在进入activity A后秒切ACtivity B就可能会导致上面所说的情况,只是可能性比较低,但毕竟是可能的,所以gilde开发团队就用了一个容器来存储当前的RequestManagerFragment对象,进一步提高其性能。
关于commitAllowingStateLoss方法,这里我们不做详细的讨论,具体可参考这里
然后在fragmentGet方法中也会调用RequestManager方法。似曾相识是吧,没错,我们在getApplicationManager方法中也调用了这个方法!!那么下面来看看这个方法究竟做了写什么吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
}

RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
this.context = context.getApplicationContext();
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.glide = Glide.get(context); //创建一个Glide对象
this.optionsApplier = new OptionsApplier();

ConnectivityMonitor connectivityMonitor = factory.build(context,
new RequestManagerConnectivityListener(requestTracker));

// If we're the application level request manager, we may be created on a background thread. In that case we
// cannot risk synchronously pausing or resuming requests, so we hack around the issue by delaying adding
// ourselves as a lifecycle listener by posting to the main thread. This should be entirely safe.
if (Util.isOnBackgroundThread()) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
lifecycle.addListener(RequestManager.this);//将生命周期进行绑定
}
});
} else {
lifecycle.addListener(this);//绑定生命周期
}
lifecycle.addListener(connectivityMonitor);
}

//单例模式创建Gilde对象
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext();
List<GlideModule> modules = new ManifestParser(applicationContext).parse();

GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module : modules) {
module.applyOptions(applicationContext, builder);
}
glide = builder.createGlide();
for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide);
}
}
}
}

return glide;
}

//在这里进行一系列的初始化,
Glide createGlide() {
if (sourceService == null) {
final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
sourceService = new FifoPriorityThreadPoolExecutor(cores);
}
if (diskCacheService == null) {
diskCacheService = new FifoPriorityThreadPoolExecutor(1);
}

MemorySizeCalculator calculator = new MemorySizeCalculator(context);
if (bitmapPool == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
int size = calculator.getBitmapPoolSize();
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}

if (memoryCache == null) {
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}

if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}

if (engine == null) {
engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
}

if (decodeFormat == null) {
decodeFormat = DecodeFormat.DEFAULT;
}

return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}

不难看出,在RequestManager构造方法中,对生命周期进行了绑定,并绑定了监听。然后通过Glide.get(context);创建一个Gilde对象,在Glide.get()方法中,
这里createGlide方法中,我们通过new Gidle创建了一个Gilde对象,简单介绍以下其中的四个参数,他们都是在createGlide中生成的。

  • MemoryCache 内存缓存
  • BitmapPool 图片池
  • DecodeFormat 图片格式
  • Engine 引擎类
    我们对其值的获取和计算也进行一个简单的了解:我们首先来看memoryCacher的大小是由calculator.getMemoryCacheSize()计算,来看其实现:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    //首先是其调用MemorySizeCalculator的构造方法,最终会回调到如下构造方法中
    MemorySizeCalculator(Context context, ActivityManager activityManager, ScreenDimensions screenDimensions) {
    this.context = context;
    final int maxSize = getMaxSize(activityManager);

    final int screenSize = screenDimensions.getWidthPixels() * screenDimensions.getHeightPixels()
    * BYTES_PER_ARGB_8888_PIXEL;(宽*高*4)

    int targetPoolSize = screenSize * BITMAP_POOL_TARGET_SCREENS;(宽*高*4*4)
    int targetMemoryCacheSize = screenSize * MEMORY_CACHE_TARGET_SCREENS;(宽*高*4*2)

    //判断是否超过最大值,否则就等比缩小
    if (targetMemoryCacheSize + targetPoolSize <= maxSize) {
    memoryCacheSize = targetMemoryCacheSize;
    bitmapPoolSize = targetPoolSize;
    } else {
    int part = Math.round((float) maxSize / (BITMAP_POOL_TARGET_SCREENS + MEMORY_CACHE_TARGET_SCREENS));
    memoryCacheSize = part * MEMORY_CACHE_TARGET_SCREENS;
    bitmapPoolSize = part * BITMAP_POOL_TARGET_SCREENS;
    }
    ...
    }

    //该方法主要用于计算最大内存。如果是低配手机就每个进程可用的最大内存乘以0.33,否则就每个进程可用的最大内存乘以0.4
    private static int getMaxSize(ActivityManager activityManager) {
    final int memoryClassBytes = activityManager.getMemoryClass() * 1024 * 1024;
    //每个进程可用的最大内存
    final boolean isLowMemoryDevice = isLowMemoryDevice(activityManager);
    return Math.round(memoryClassBytes
    * (isLowMemoryDevice ? LOW_MEMORY_MAX_SIZE_MULTIPLIER : MAX_SIZE_MULTIPLIER));
    }

    //这里直接返回已经在构造函数中计算好的内容缓存。
    public int getMemoryCacheSize() {
    return memoryCacheSize;
    }
    从上面的代码可以分析出来,内容缓存memoryCacheSize的大小一般为屏幕的宽4*2,当然,也有可能等比例缩小。
    然后图片池BitmapPool的计算就比较简单了。在createGlide中计算BitmapPool的方法为:
    1
    2
    3
    4
    5
    6
    7
    8
     if (bitmapPool == null) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    int size = calculator.getBitmapPoolSize();
    bitmapPool = new LruBitmapPool(size);
    } else {
    bitmapPool = new BitmapPoolAdapter();
    }
    }
    可以看出来,bitmapPool在android3.0以后用calculator.getBitmapPoolSize()进行计算,这个值在前面的MemorySizeCalculator已经计算出来了,而在MemorySizeCalculator中只是将其返回。
    1
    2
    3
    public int getBitmapPoolSize() {
    return bitmapPoolSize;
    }
    可以分析出来,图片池用的是targetPoolSize(即一般是缓存大小是屏幕的宽4*4)。
    接下来是DecodeFormat 图片格式
    1
    2
    3
    4
    5
    //GlideBuilder中的createGlide方法
    decodeFormat = DecodeFormat.DEFAULT;

    //public enum DecodeFormat
    public static final DecodeFormat DEFAULT = PREFER_RGB_565;
    即默认的图片格式是PREFER_RGB_565;

最后一个Engine引擎类:

1
2
3
4
//GlideBuilder中的createGlide方法
if (engine == null) {
engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
}

engine里面的主要参数:

  • 内存缓存 memoryCache
  • 本地缓存 diskCacheFactory
  • 处理源资源的线程池 sourceService
  • 处理本地缓存的线程池 diskCacheService
    第一个参数就是我们前面已经计算出来的memoryCache,这里就不在介绍,加下来介绍第二个参数diskCacheFactory:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//GlideBuilder中的createGlide方法
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}

//InternalCacheDiskCacheFactory中的方法
public InternalCacheDiskCacheFactory(Context context) {
this(context, DiskCache.Factory.DEFAULT_DISK_CACHE_DIR, DiskCache.Factory.DEFAULT_DISK_CACHE_SIZE);
}


//DiskCache接口中的内容
interface Factory {

//250 MB of cache.
int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;
String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache";

//Returns a new disk cache, or {@code null} if no disk cache could be created.
DiskCache build();
}

从上面的代码我们不难看出:默认大小为250m,默认目录:image_manager_disk_cache

接下来,我们继续看:
sourceService 处理源资源的线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//GlideBuilder中的createGlide方法
if (sourceService == null) {
final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
sourceService = new FifoPriorityThreadPoolExecutor(cores);
}

//下面的方法都是在FifoPriorityThreadPoolExecutor类中,且该类继承ThreadPoolExecutor
public FifoPriorityThreadPoolExecutor(int poolSize) {
this(poolSize, UncaughtThrowableStrategy.LOG);
}

public FifoPriorityThreadPoolExecutor(int poolSize, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
this(poolSize, poolSize, 0, TimeUnit.MILLISECONDS, new DefaultThreadFactory(),
uncaughtThrowableStrategy);
}

public FifoPriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAlive, TimeUnit timeUnit,
ThreadFactory threadFactory, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
super(corePoolSize, maximumPoolSize, keepAlive, timeUnit, new PriorityBlockingQueue<Runnable>(), threadFactory);
this.uncaughtThrowableStrategy = uncaughtThrowableStrategy;
}

通过上面的源码,我们不难分析出diskCacheService其实一个线程池,里面的核心线程数为可用的处理器的个数。

最后是处理本地缓存的线程池 diskCacheService的实现,代码如下:

1
2
3
4
//GlideBuilder中的createGlide方法
if (diskCacheService == null) {
diskCacheService = new FifoPriorityThreadPoolExecutor(1);
}

核心线程数为1,最多的线程也为1。至于FifoPriorityThreadPoolExecutor方法的实现,可以参考前面的代码。

到这里,我们的with方法的实现就解析的差不多了。最后总结一下with方法的实现逻辑:
with方法可以传入不同的参数,根据不同的参数和线程环境来绑定不同的生命周期。如果在子线程且SDK>=11即android3.0以后,则绑定的是Application的生命周期;如传入的是Activity且在主线程,则将gilde和activity的生命周期绑定(通过创建一个无界面的Fragment,让请求和Activity的生命周期同步),然后添加监听。

load方法

通过的,load方法也有很多的重载。load在RequestManager类中的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}

public DrawableTypeRequest<Uri> load(Uri uri) {
return (DrawableTypeRequest<Uri>) fromUri().load(uri);
}

public DrawableTypeRequest<File> load(File file) {
return (DrawableTypeRequest<File>) fromFile().load(file);
}

public DrawableTypeRequest<Integer> load(Integer resourceId) {
return (DrawableTypeRequest<Integer>) fromResource().load(resourceId);
}

@Deprecated
public DrawableTypeRequest<URL> load(URL url) {
return (DrawableTypeRequest<URL>) fromUrl().load(url);
}

@Deprecated
public DrawableTypeRequest<byte[]> load(byte[] model, final String id) {
return (DrawableTypeRequest<byte[]>) load(model).signature(new StringSignature(id));
}

public DrawableTypeRequest<byte[]> load(byte[] model) {
return (DrawableTypeRequest<byte[]>) fromBytes().load(model);
}

public <T> DrawableTypeRequest<T> load(T model) {
return (DrawableTypeRequest<T>) loadGeneric(getSafeClass(model)).load(model);
}

可以看到,load也有很多的重载方法,具体的作用和参数就不做说明了。通过源码我们可以看到,无论是那个load的重载,最终都会返回一个DrawableTypeRequest 对象(继承自DrawableRequestBuilder类)
我们不可能将所有的load方法分析一篇,这里我就以load(URL url)这个方法为例进行说明,至于其他的load方法,可以自行去理解。
首先,我们在RequestManager.java中,我们我们看到load的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Deprecated
public DrawableTypeRequest<URL> load(URL url) {
return (DrawableTypeRequest<URL>) fromUrl().load(url);
}

@Deprecated
public DrawableTypeRequest<URL> fromUrl() {
return loadGeneric(URL.class);
}

private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"
+ " Glide#register with a ModelLoaderFactory for your custom model class");
}

return optionsApplier.apply(
new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}

通过上面的源码,我们发现加载图片UR这一个load方法中的逻辑是非常简单的,只是简单的调用fromUrl().load(url)方法(先调用fromUrl(),后调用load(url),典型的build模式)。而fromUrl()的代码也是非常简单的,只是调用了loadGeneric(URL.class)这么一个方法。然后继续来看loadGeneric(URL.class)方法,这个方法也比较简单,用Glide.buildStreamModelLoaderGlide.buildFileDescriptorModelLoader()来获得ModeiLoade对象,这里面的具体实现就不做详细的讨论了,比较复杂,或许等我以后弄清楚了会加上。
loadGeneric(URL.class)方法中,我们返回的是DrawableTypeRequest对象,所以最后创建了一个DrawableTypeRequest对象,并将相关参数应用到这个DrawableTypeRequest对象上,那么我们对DrawableTypeRequest这个对象就有必要进行以下分析了。
以下是DrawableTypeRequest.java的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
private final ModelLoader<ModelType, InputStream> streamModelLoader;
private final ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader;
private final RequestManager.OptionsApplier optionsApplier;

private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
ModelLoader<A, InputStream> streamModelLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
Class<R> transcodedClass,
ResourceTranscoder<Z, R> transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}

if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}

DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
super(context, modelClass,
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.optionsApplier = optionsApplier;
}

/**
* Attempts to always load the resource as a {@link android.graphics.Bitmap}, even if it could actually be animated.
*
* @return A new request builder for loading a {@link android.graphics.Bitmap}
*/
public BitmapTypeRequest<ModelType> asBitmap() {
return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader,
fileDescriptorModelLoader, optionsApplier));
}

/**
* Attempts to always load the resource as a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
* <p>
* If the underlying data is not a GIF, this will fail. As a result, this should only be used if the model
* represents an animated GIF and the caller wants to interact with the GIfDrawable directly. Normally using
* just an {@link com.bumptech.glide.DrawableTypeRequest} is sufficient because it will determine whether or
* not the given data represents an animated GIF and return the appropriate animated or not animated
* {@link android.graphics.drawable.Drawable} automatically.
* </p>
*
* @return A new request builder for loading a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
*/
public GifTypeRequest<ModelType> asGif() {
return optionsApplier.apply(new GifTypeRequest<ModelType>(this, streamModelLoader, optionsApplier));
}

/**
* {@inheritDoc}
*/
public <Y extends Target<File>> Y downloadOnly(Y target) {
return getDownloadOnlyRequest().downloadOnly(target);
}

/**
* {@inheritDoc}
*/
public FutureTarget<File> downloadOnly(int width, int height) {
return getDownloadOnlyRequest().downloadOnly(width, height);
}

private GenericTranscodeRequest<ModelType, InputStream, File> getDownloadOnlyRequest() {
return optionsApplier.apply(new GenericTranscodeRequest<ModelType, InputStream, File>(File.class, this,
streamModelLoader, InputStream.class, File.class, optionsApplier));
}
}

通过注释,我们大概也可以猜到这个类是用来做什么的。
DrawableTypeRequest是一个用于创建加载请求的类,该加载方法可以直接加载动画的GIF或者Bitmap drawable格式的图片。他提供了两个的方法asBitmap()asGif()来强制指定加载静态图片和GIF图片。通过观察这两个方法的实现,发现他们的实现其实是通过创建BitmapTypeRequestGifTypeRequest来实现的,当然默认的实现还是通过创建DrawableTypeRequest实现的。
但是我们发现,在DrawableTypeRequest这个类中,并没有load这个方法,而在RequestManager.java中的loadGeneric方法中,最后返回的是DrawableTypeRequest对象,这就说明在DrawableTypeRequest肯定存在load方法的实现,我们在上面的代码中无法直接看到其方法的存在,那其实现很可能存在其父类中。通过上面的代码可知,RequestManager的父类是DrawableRequestBuilder。至于其父类DrawableRequestBuilder的实现,这里就不全部贴出来了,只贴出load方法的实现:

1
2
3
4
5
@Override
public DrawableRequestBuilder<ModelType> load(ModelType model) {
super.load(model);
return this;
}

当然,在DrawableRequestBuilder中,load()只是其中的一个方法而已,在这个类中,还定义了gilde常用的一些API方法,例如error(),placeholder(),crossFade()等等,至于其实现,可以参照源码去了解。下面继续来对load()方法进一步的深入,可以看到load方法调用了父类的load方法,接下来我们查看 其父类GenericRequesBuilder.javaload方法的实现:

1
2
3
4
5
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
this.model = model;
isModelSet = true;
return this;
}

可以看到,最终load()方法的实现很简单,只是设置了加载的类型,然后就返回了this,当然,这个this最终就变成了DrawableTypeRequest对象。到此,load方法基本流程已经差不多了,下面将分析into()方法的实现。

into()

前面的三个方法最终都是为了最后这个into方法做准备,所以搞懂into方法,对gilde源码的理解是非常非常重要的,下面我们进行into方法的解析。
首先,我们直接调用的into方法的实现是在DrawableRequestBuilder中实现的,具体实现的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/**
* {@inheritDoc}
*
* <p>
* Note - If no transformation is set for this load, a default transformation will be applied based on the
* value returned from {@link android.widget.ImageView#getScaleType()}. To avoid this default transformation,
* use {@link #dontTransform()}.
* </p>
*
* @param view {@inheritDoc}
* @return {@inheritDoc}
*/
@Override
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}

可以看出了,在DrawableRequestBuilder中,其实并没有对into方法进行实现,只是简单的将其实现抛给了父类into方法。前面就分析过,GenericRequestBuilder就是DrawableRequestBuilder的父类,下面就into方法的实现进行解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();//判断是否在主线程
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}

if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}

return into(glide.buildImageViewTarget(view, transcodeClass));
}

参考资料:

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment