移动开发最新面试官:简历上最好不要写Glide,不是问源码那么简单,护理面试专业知识点

最后

针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

  • Android前沿技术大纲

  • 全套体系化高级架构视频

Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

public synchronized boolean remove(String key) throws IOException {

    checkNotClosed();

    validateKey(key);

    Entry entry = lruEntries.get(key);

    if (entry == null || entry.currentEditor != null) {

      return false;

    }



        //一个key可能对应多个value,hash冲突的情况

    for (int i = 0; i < valueCount; i++) {

      File file = entry.getCleanFile(i);

        //通过 file.delete() 删除缓存文件,删除失败则抛异常

      if (file.exists() && !file.delete()) {

        throw new IOException("failed to delete " + file);

      }

      size -= entry.lengths[i];

      entry.lengths[i] = 0;

    }

    ...

    return true;

}

复制代码




可以看到 DiskLruCache 同样是利用LinkHashMap的特点,只不过数组里面存的 Entry 有点变化,Editor 用于操作文件。



private final class Entry {

private final String key;



private final long[] lengths;



private boolean readable;



private Editor currentEditor;



private long sequenceNumber;

...

}

复制代码




### []( )2.4 防止OOM



加载图片非常重要的一点是需要防止OOM,上面的LruCache缓存大小设置,可以有效防止OOM,但是当图片需求比较大,可能需要设置一个比较大的缓存,这样的话发生OOM的概率就提高了,那应该探索其它防止OOM的方法。



##### []( )方法1:软引用



回顾一下Java的四大引用:



*   强引用: 普通变量都属于强引用,比如 `private Context context;`

*   软应用: SoftReference,在发生OOM之前,垃圾回收器会回收SoftReference引用的对象。

*   弱引用: WeakReference,发生GC的时候,垃圾回收器会回收WeakReference中的对象。

*   虚引用: 随时会被回收,没有使用场景。



怎么理解强引用:



> 强引用对象的回收时机依赖垃圾回收算法,我们常说的可达性分析算法,当Activity销毁的时候,Activity会跟GCRoot断开,至于GCRoot是谁?这里可以大胆猜想,Activity对象的创建是在ActivityThread中,ActivityThread要回调Activity的各个生命周期,肯定是持有Activity引用的,那么这个GCRoot可以认为就是ActivityThread,当Activity 执行onDestroy的时候,ActivityThread 就会断开跟这个Activity的联系,Activity到GCRoot不可达,所以会被垃圾回收器标记为可回收对象。



软引用的设计就是应用于会发生OOM的场景,大内存对象如Bitmap,可以通过 SoftReference 修饰,防止大对象造成OOM,看下这段代码



private static LruCache<String, SoftReference<Bitmap>> mLruCache = new LruCache<String, SoftReference<Bitmap>>(10 * 1024){

    @Override

    protected int sizeOf(String key, SoftReference<Bitmap> value) {

        //默认返回1,这里应该返回Bitmap占用的内存大小,单位:K



        //Bitmap被回收了,大小是0

        if (value.get() == null){

            return 0;

        }

        return value.get().getByteCount() /1024;

    }

};

复制代码




LruCache里存的是软引用对象,那么当内存不足的时候,Bitmap会被回收,也就是说通过SoftReference修饰的Bitmap就不会导致OOM。



当然,这段代码存在一些问题,Bitmap被回收的时候,LruCache剩余的大小应该重新计算,可以写个方法,当Bitmap取出来是空的时候,LruCache清理一下,重新计算剩余内存;



还有另一个问题,就是内存不足时软引用中的Bitmap被回收的时候,这个LruCache就形同虚设,相当于内存缓存失效了,必然出现效率问题。



##### []( )方法2:onLowMemory



**当内存不足的时候,Activity、Fragment会调用`onLowMemory`方法,可以在这个方法里去清除缓存,Glide使用的就是这一种方式来防止OOM。**



//Glide

public void onLowMemory() {

clearMemory();

}

public void clearMemory() {

// Engine asserts this anyway when removing resources, fail faster and consistently

Util.assertMainThread();

// memory cache needs to be cleared before bitmap pool to clear re-pooled Bitmaps too. See #687.

memoryCache.clearMemory();

bitmapPool.clearMemory();

arrayPool.clearMemory();

}

复制代码




##### []( )方法3:从Bitmap 像素存储位置考虑



我们知道,系统为每个进程,也就是每个虚拟机分配的内存是有限的,早期的16M、32M,现在100+M,  

虚拟机的内存划分主要有5部分:



*   虚拟机栈

*   本地方法栈

*   程序计数器

*   方法区

*   堆



而对象的分配一般都是在堆中,堆是JVM中最大的一块内存,OOM一般都是发生在堆中。



Bitmap 之所以占内存大不是因为对象本身大,而是因为Bitmap的像素数据, **Bitmap的像素数据大小 = 宽 \* 高 \* 1像素占用的内存。**



1像素占用的内存是多少?不同格式的Bitmap对应的像素占用内存是不同的,具体是多少呢?  

在Fresco中看到如下定义代码



/**

  • Bytes per pixel definitions

*/

public static final int ALPHA_8_BYTES_PER_PIXEL = 1;

public static final int ARGB_4444_BYTES_PER_PIXEL = 2;

public static final int ARGB_8888_BYTES_PER_PIXEL = 4;

public static final int RGB_565_BYTES_PER_PIXEL = 2;

public static final int RGBA_F16_BYTES_PER_PIXEL = 8;

复制代码




如果Bitmap使用 `RGB_565` 格式,则1像素占用 2 byte,`ARGB_8888` 格式则占4 byte。  

**在选择图片加载框架的时候,可以将内存占用这一方面考虑进去,更少的内存占用意味着发生OOM的概率越低。** Glide内存开销是Picasso的一半,就是因为默认Bitmap格式不同。



至于宽高,是指Bitmap的宽高,怎么计算的呢?看`BitmapFactory.Options` 的 outWidth



/**

 * The resulting width of the bitmap. If {@link #inJustDecodeBounds} is

 * set to false, this will be width of the output bitmap after any

 * scaling is applied. If true, it will be the width of the input image

 * without any accounting for scaling.

 *

 * <p>outWidth will be set to -1 if there is an error trying to decode.</p>

 */

public int outWidth;

复制代码




看注释的意思,如果 `BitmapFactory.Options` 中指定 `inJustDecodeBounds` 为true,则为原图宽高,如果是false,则是缩放后的宽高。**所以我们一般可以通过压缩来减小Bitmap像素占用内存**。



扯远了,上面分析了Bitmap像素数据大小的计算,只是说明Bitmap像素数据为什么那么大。**那是否可以让像素数据不放在java堆中,而是放在native堆中呢**?据说Android 3.0到8.0 之间Bitmap像素数据存在Java堆,而8.0之后像素数据存到native堆中,是不是真的?看下源码就知道了~



###### []( )8.0 Bitmap



java层创建Bitmap方法



public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height,

        @NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace) {

    ...

    Bitmap bm;

    ...

    if (config != Config.ARGB_8888 || colorSpace == ColorSpace.get(ColorSpace.Named.SRGB)) {

        //最终都是通过native方法创建

        bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, null, null);

    } else {

        bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true,

                d50.getTransform(), parameters);

    }



    ...

    return bm;

}

复制代码




Bitmap 的创建是通过native方法 `nativeCreate`



对应源码 [8.0.0\_r4/xref/frameworks/base/core/jni/android/graphics/Bitmap.cpp]( )



//Bitmap.cpp

static const JNINativeMethod gBitmapMethods[] = {

{   "nativeCreate",             "([IIIIIIZ[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/Bitmap;",

    (void*)Bitmap_creator },

复制代码




JNI动态注册,nativeCreate 方法 对应 `Bitmap_creator`;



//Bitmap.cpp

static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,

                          jint offset, jint stride, jint width, jint height,

                          jint configHandle, jboolean isMutable,

                          jfloatArray xyzD50, jobject transferParameters) {

...

//1\. 申请堆内存,创建native层Bitmap

sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap, NULL);

if (!nativeBitmap) {

    return NULL;

}



...

//2.创建java层Bitmap

return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable));

}

复制代码




主要两个步骤:



1.  申请内存,创建native层Bitmap,看下`allocateHeapBitmap`方法  

    [8.0.0\_r4/xref/frameworks/base/libs/hwui/hwui/Bitmap.cpp]( )



//

static sk_sp allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes,

    SkColorTable* ctable) {

// calloc 是c++ 的申请内存函数

void* addr = calloc(size, 1);

if (!addr) {

    return nullptr;

}

return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes, ctable));

}

复制代码




可以看到通过c++的 `calloc` 函数申请了一块内存空间,然后创建native层Bitmap对象,把内存地址传过去,也就是native层的Bitmap数据(像素数据)是存在native堆中。



2.  创建java 层Bitmap



//Bitmap.cpp

jobject createBitmap(JNIEnv* env, Bitmap* bitmap,

    int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets,

    int density) {

...

BitmapWrapper* bitmapWrapper = new BitmapWrapper(bitmap);

 //通过JNI回调Java层,调用java层的Bitmap构造方法

jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,

        reinterpret_cast<jlong>(bitmapWrapper), bitmap->width(), bitmap->height(), density,

        isMutable, isPremultiplied, ninePatchChunk, ninePatchInsets);

return obj;

}

复制代码




env->NewObject,通过JNI创建Java层Bitmap对象,`gBitmap_class,gBitmap_constructorMethodID`这些变量是什么意思,看下面这个方法,对应java层的Bitmap的类名和构造方法。



//Bitmap.cpp

int register_android_graphics_Bitmap(JNIEnv* env)

{

gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap"));

gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J");

gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "<init>", "(JIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");

gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V");

gBitmap_getAllocationByteCountMethodID = GetMethodIDOrDie(env, gBitmap_class, "getAllocationByteCount", "()I");

return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods,

                                     NELEM(gBitmapMethods));

}

复制代码




8.0 的Bitmap创建就两个点:



1.  创建native层Bitmap,在native堆申请内存。

2.  通过JNI创建java层Bitmap对象,这个对象在java堆中分配内存。



像素数据是存在native层Bitmap,也就是证明8.0的Bitmap像素数据存在native堆中。



###### []( )7.0 Bitmap



直接看native层的方法,



[/7.0.0\_r31/xref/frameworks/base/core/jni/android/graphics/Bitmap.cpp]( )



//JNI动态注册

static const JNINativeMethod gBitmapMethods[] = {

{   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",

    (void*)Bitmap_creator },

static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,

                          jint offset, jint stride, jint width, jint height,

                          jint configHandle, jboolean isMutable) {

... 

//1.通过这个方法来创建native层Bitmap

Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);

...



return GraphicsJNI::createBitmap(env, nativeBitmap,

        getPremulBitmapCreateFlags(isMutable));

}

复制代码




native层Bitmap 创建是通过`GraphicsJNI::allocateJavaPixelRef`,看看里面是怎么分配的, GraphicsJNI 的实现类是[Graphics.cpp]( )



android::Bitmap* GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,

                                         SkColorTable* ctable) {

const SkImageInfo& info = bitmap->info();



size_t size;

//计算需要的空间大小

if (!computeAllocationSize(*bitmap, &size)) {

    return NULL;

}



// we must respect the rowBytes value already set on the bitmap instead of

// attempting to compute our own.

const size_t rowBytes = bitmap->rowBytes();

// 1\. 创建一个数组,通过JNI在java层创建的

jbyteArray arrayObj = (jbyteArray) env->CallObjectMethod(gVMRuntime,

                                                         gVMRuntime_newNonMovableArray,

                                                         gByte_class, size);

...

// 2\. 获取创建的数组的地址

jbyte* addr = (jbyte*) env->CallLongMethod(gVMRuntime, gVMRuntime_addressOf, arrayObj);

...

//3\. 创建Bitmap,传这个地址

android::Bitmap* wrapper = new android::Bitmap(env, arrayObj, (void*) addr,

        info, rowBytes, ctable);

wrapper->getSkBitmap(bitmap);

// since we're already allocated, we lockPixels right away

// HeapAllocator behaves this way too

bitmap->lockPixels();



return wrapper;

}

复制代码




可以看到,7.0 像素内存的分配是这样的:



1.  通过JNI调用java层创建一个数组

2.  然后创建native层Bitmap,把数组的地址传进去。



由此说明,7.0 的Bitmap像素数据是放在java堆的。



当然,3.0 以下Bitmap像素内存据说也是放在native堆的,但是需要手动释放native层的Bitmap,也就是需要手动调用recycle方法,native层内存才会被回收。这个大家可以自己去看源码验证。



###### []( )native层Bitmap 回收问题



Java层的Bitmap对象由垃圾回收器自动回收,而native层Bitmap印象中我们是不需要手动回收的,源码中如何处理的呢?



记得有个面试题是这样的:



> 说说final、finally、finalize 的关系



三者除了长得像,其实没有半毛钱关系,final、finally大家都用的比较多,而 `finalize` 用的少,或者没用过,`finalize` 是 Object 类的一个方法,注释是这样的:



/**

 * Called by the garbage collector on an object when garbage collection

 * determines that there are no more references to the object.

 * A subclass overrides the {@code finalize} method to dispose of

 * system resources or to perform other cleanup.

 * <p>

 ...**/

protected void finalize() throws Throwable { }

复制代码




意思是说,垃圾回收器确认这个对象没有其它地方引用到它的时候,会调用这个对象的`finalize`方法,子类可以重写这个方法,做一些释放资源的操作。



**在6.0以前,Bitmap 就是通过这个finalize 方法来释放native层对象的。** [6.0 Bitmap.java]( )



Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density,

        boolean isMutable, boolean requestPremultiplied,

        byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets) {

    ...

    mNativePtr = nativeBitmap;

    //1.创建 BitmapFinalizer

    mFinalizer = new BitmapFinalizer(nativeBitmap);

    int nativeAllocationByteCount = (buffer == null ? getByteCount() : 0);

    mFinalizer.setNativeAllocationByteCount(nativeAllocationByteCount);

}

private static class BitmapFinalizer {

    private long mNativeBitmap;



    // Native memory allocated for the duration of the Bitmap,

    // if pixel data allocated into native memory, instead of java byte[]

    private int mNativeAllocationByteCount;



    BitmapFinalizer(long nativeBitmap) {

        mNativeBitmap = nativeBitmap;

    }



    public void setNativeAllocationByteCount(int nativeByteCount) {

        if (mNativeAllocationByteCount != 0) {

            VMRuntime.getRuntime().registerNativeFree(mNativeAllocationByteCount);

        }

        mNativeAllocationByteCount = nativeByteCount;

        if (mNativeAllocationByteCount != 0) {

            VMRuntime.getRuntime().registerNativeAllocation(mNativeAllocationByteCount);

        }

    }



    @Override

    public void finalize() {

        try {

            super.finalize();

        } catch (Throwable t) {

            // Ignore

        } finally {

            //2.就是这里了,

            setNativeAllocationByteCount(0);

            nativeDestructor(mNativeBitmap);

            mNativeBitmap = 0;

        }

    }

}

复制代码




在Bitmap构造方法创建了一个 `BitmapFinalizer`类,重写finalize 方法,在java层Bitmap被回收的时候,BitmapFinalizer 对象也会被回收,finalize 方法肯定会被调用,在里面释放native层Bitmap对象。



6.0 之后做了一些变化,BitmapFinalizer 没有了,被[NativeAllocationRegistry]( )取代。



例如 8.0 Bitmap构造方法



Bitmap(long nativeBitmap, int width, int height, int density,

        boolean isMutable, boolean requestPremultiplied,

        byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets) {



    ...

    mNativePtr = nativeBitmap;

    long nativeSize = NATIVE_ALLOCATION_SIZE + getAllocationByteCount();

    //  创建NativeAllocationRegistry这个类,调用registerNativeAllocation 方法

    NativeAllocationRegistry registry = new NativeAllocationRegistry(

        Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), nativeSize);

    registry.registerNativeAllocation(this, nativeBitmap);

}

复制代码




NativeAllocationRegistry 就不分析了, **不管是BitmapFinalizer 还是NativeAllocationRegistry,目的都是在java层Bitmap被回收的时候,将native层Bitmap对象也回收掉。** 一般情况下我们无需手动调用recycle方法,由GC去盘它即可。



上面分析了Bitmap像素存储位置,我们知道,Android 8.0 之后Bitmap像素内存放在native堆,Bitmap导致OOM的问题基本不会在8.0以上设备出现了(没有内存泄漏的情况下),那8.0 以下设备怎么办?赶紧升级或换手机吧~



![](https://img-blog.csdnimg.cn/img_convert/3179a4301b2f0a90d13c83884ae54a9b.png)



我们换手机当然没问题,但是并不是所有人都能跟上Android系统更新的步伐,所以,问题还是要解决~



Fresco 之所以能跟Glide 正面交锋,必然有其独特之处,文中开头列出 Fresco 的优点是:“在5.0以下(最低2.3)系统,Fresco将图片放到一个特别的内存区域(Ashmem区)” 这个Ashmem区是一块匿名共享内存,Fresco 将Bitmap像素放到共享内存去了,共享内存是属于native堆内存。



Fresco 关键源码在 `PlatformDecoderFactory` 这个类



public class PlatformDecoderFactory {

/**

  • Provide the implementation of the PlatformDecoder for the current platform using the provided

  • PoolFactory

  • @param poolFactory The PoolFactory

  • @return The PlatformDecoder implementation

*/

public static PlatformDecoder buildPlatformDecoder(

  PoolFactory poolFactory, boolean gingerbreadDecoderEnabled) {

//8.0 以上用 OreoDecoder 这个解码器

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

  int maxNumThreads = poolFactory.getFlexByteArrayPoolMaxNumThreads();

  return new OreoDecoder(

      poolFactory.getBitmapPool(), maxNumThreads, new Pools.SynchronizedPool<>(maxNumThreads));

} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

  //大于5.0小于8.0用 ArtDecoder 解码器

  int maxNumThreads = poolFactory.getFlexByteArrayPoolMaxNumThreads();

  return new ArtDecoder(

      poolFactory.getBitmapPool(), maxNumThreads, new Pools.SynchronizedPool<>(maxNumThreads));

} else {

  if (gingerbreadDecoderEnabled && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {

    //小于4.4 用 GingerbreadPurgeableDecoder 解码器

    return new GingerbreadPurgeableDecoder();

  } else {

    //这个就是4.4到5.0 用的解码器了

    return new KitKatPurgeableDecoder(poolFactory.getFlexByteArrayPool());

  }

}

}

}

复制代码




8.0 先不看了,看一下 4.4 以下是怎么得到Bitmap的,看下`GingerbreadPurgeableDecoder`这个类有个获取Bitmap的方法



//GingerbreadPurgeableDecoder

private Bitmap decodeFileDescriptorAsPurgeable(

  CloseableReference<PooledByteBuffer> bytesRef,

  int inputLength,

  byte[] suffix,

  BitmapFactory.Options options) {

//  MemoryFile :匿名共享内存

MemoryFile memoryFile = null;

try {

  //将图片数据拷贝到匿名共享内存

  memoryFile = copyToMemoryFile(bytesRef, inputLength, suffix);

  FileDescriptor fd = getMemoryFileDescriptor(memoryFile);

  if (mWebpBitmapFactory != null) {

    // 创建Bitmap,Fresco自己写了一套创建Bitmap方法

    Bitmap bitmap = mWebpBitmapFactory.decodeFileDescriptor(fd, null, options);

    return Preconditions.checkNotNull(bitmap, "BitmapFactory returned null");

  } else {

    throw new IllegalStateException("WebpBitmapFactory is null");

  }

} 

}

复制代码




捋一捋,4.4以下,Fresco 使用匿名共享内存来保存Bitmap数据,首先将图片数据拷贝到匿名共享内存中,然后使用Fresco自己写的加载Bitmap的方法。



Fresco对不同Android版本使用不同的方式去加载Bitmap,至于4.4-5.0,5.0-8.0,8.0 以上,对应另外三个解码器,大家可以从`PlatformDecoderFactory` 这个类入手,自己去分析,思考为什么不同平台要分这么多个解码器,8.0 以下都用匿名共享内存不好吗?期待你在评论区跟大家分享~



### []( )2.5 ImageView 内存泄露



> 曾经在Vivo驻场开发,带有头像功能的页面被测出内存泄漏,原因是SDK中有个加载网络头像的方法,持有ImageView引用导致的。



当然,修改也比较简单粗暴,**将ImageView用WeakReference修饰**就完事了。



事实上,这种方式虽然解决了内存泄露问题,但是并不完美,例如在界面退出的时候,我们除了希望ImageView被回收,同时希望加载图片的任务可以取消,队未执行的任务可以移除。



Glide的做法是监听生命周期回调,看 `RequestManager` 这个类



public void onDestroy() {

targetTracker.onDestroy();

for (Target<?> target : targetTracker.getAll()) {

  //清理任务

  clear(target);

}

targetTracker.clear();

requestTracker.clearRequests();

lifecycle.removeListener(this);

lifecycle.removeListener(connectivityMonitor);

mainHandler.removeCallbacks(addSelfToLifecycle);

glide.unregisterRequestManager(this);

}

复制代码




在Activity/fragment 销毁的时候,取消图片加载任务,细节大家可以自己去看源码。



### []( )2.6 列表加载问题



#### []( )图片错乱



由于RecyclerView或者LIstView的复用机制,网络加载图片开始的时候ImageView是第一个item的,加载成功之后ImageView由于复用可能跑到第10个item去了,在第10个item显示第一个item的图片肯定是错的。



常规的做法是给ImageView设置tag,tag一般是图片地址,更新ImageView之前判断tag是否跟url一致。



当然,可以在item从列表消失的时候,取消对应的图片加载任务。要考虑放在图片加载框架做还是放在UI做比较合适。



#### []( )线程池任务过多



列表滑动,会有很多图片请求,如果是第一次进入,没有缓存,那么队列会有很多任务在等待。所以在请求网络图片之前,需要判断队列中是否已经存在该任务,存在则不加到队列去。



[]( )总结

------------------------------------------------------------------



本文通过Glide开题,分析一个图片加载框架必要的需求,以及各个需求涉及到哪些技术和原理。




### 结语

**由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是目录截图:**

![](https://img-blog.csdnimg.cn/img_convert/1c5ec3e622d57af57e64016e168a9b2f.webp?x-oss-process=image/format,png)

>由于整个文档比较全面,内容比较多,篇幅不允许,下面以截图方式展示 。
>

再附一部分Android架构面试视频讲解:

![](https://img-blog.csdnimg.cn/img_convert/5c1f3c1c7bf39e50238d718eabf418a7.webp?x-oss-process=image/format,png)




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

(connectivityMonitor);

    mainHandler.removeCallbacks(addSelfToLifecycle);

    glide.unregisterRequestManager(this);

  }

复制代码



在Activity/fragment 销毁的时候,取消图片加载任务,细节大家可以自己去看源码。

2.6 列表加载问题

图片错乱

由于RecyclerView或者LIstView的复用机制,网络加载图片开始的时候ImageView是第一个item的,加载成功之后ImageView由于复用可能跑到第10个item去了,在第10个item显示第一个item的图片肯定是错的。

常规的做法是给ImageView设置tag,tag一般是图片地址,更新ImageView之前判断tag是否跟url一致。

当然,可以在item从列表消失的时候,取消对应的图片加载任务。要考虑放在图片加载框架做还是放在UI做比较合适。

线程池任务过多

列表滑动,会有很多图片请求,如果是第一次进入,没有缓存,那么队列会有很多任务在等待。所以在请求网络图片之前,需要判断队列中是否已经存在该任务,存在则不加到队列去。

总结


本文通过Glide开题,分析一个图片加载框架必要的需求,以及各个需求涉及到哪些技术和原理。

结语

由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是目录截图:

[外链图片转存中…(img-N8wU8Z4Y-1715467035340)]

由于整个文档比较全面,内容比较多,篇幅不允许,下面以截图方式展示 。

再附一部分Android架构面试视频讲解:

[外链图片转存中…(img-EH0nSdIp-1715467035340)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 23
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值