Android Fresco 框架擴展模塊源碼深度剖析(四)

Android Fresco 框架擴展模塊源碼深度剖析

一、引言

在 Android 開發領域,圖片處理一直是一個重要且具有挑戰性的任務。Fresco 作為 Facebook 開源的強大圖片加載框架,在圖片的加載、緩存和顯示等方面已經提供了非常完善的功能。然而,為了滿足不同開發者多樣化的需求,Fresco 設計了豐富的擴展模塊,這些擴展模塊允許開發者根據自身項目的特點對框架進行定制和擴展。本文將深入剖析 Fresco 框架的擴展模塊,從源碼級別進行詳細分析,幫助開發者更好地理解和運用這些擴展功能。

二、Fresco 擴展模塊概述

Fresco 的擴展模塊主要圍繞幾個核心方向展開,包括自定義圖片解碼器、自定義圖片處理器、自定義緩存策略以及與其他第三方庫的集成等。這些擴展模塊通過接口和抽象類的設計,使得開發者可以方便地實現自己的邏輯,而不需要對框架的核心代碼進行修改。

2.1 主要擴展點

Fresco 提供了多個關鍵的擴展點,下面我們先對這些擴展點進行簡單介紹,后續會結合源碼詳細分析。

2.1.1 自定義圖片解碼器

通過實現?ImageDecoder?接口,開發者可以自定義圖片的解碼邏輯,支持新的圖片格式或者對現有格式進行特殊處理。

java

// 圖片解碼器接口,定義了解碼圖片的基本方法
public interface ImageDecoder {/*** 解碼圖片* @param encodedImage 包含圖片原始數據的 EncodedImage 對象* @param length 圖片數據的長度* @param options 解碼選項* @return 解碼后的 CloseableImage 對象*/CloseableImage decodeImage(EncodedImage encodedImage, int length, ImageDecodeOptions options);
}
2.1.2 自定義圖片處理器

實現?Postprocessor?接口,開發者可以在圖片解碼后對其進行進一步處理,如裁剪、濾鏡等操作。

java

// 圖片后處理器接口,用于在圖片解碼后進行額外處理
public interface Postprocessor {/*** 獲取處理器的名稱* @return 處理器的名稱*/String getName();/*** 處理圖片* @param destBitmap 目標 Bitmap 對象,用于存儲處理后的圖片* @param sourceBitmap 源 Bitmap 對象,即解碼后的原始圖片*/void process(Bitmap destBitmap, Bitmap sourceBitmap);
}
2.1.3 自定義緩存策略

通過實現?CacheKeyFactory?接口和自定義?Cache?類,開發者可以定制圖片的緩存鍵生成規則和緩存存儲方式。

java

// 緩存鍵工廠接口,用于生成緩存鍵
public interface CacheKeyFactory {/*** 生成圖片緩存鍵* @param imageRequest 圖片請求對象* @param callerContext 調用上下文* @return 生成的緩存鍵*/CacheKey getBitmapCacheKey(ImageRequest imageRequest, Object callerContext);/*** 生成編碼圖片緩存鍵* @param imageRequest 圖片請求對象* @param callerContext 調用上下文* @return 生成的緩存鍵*/CacheKey getEncodedCacheKey(ImageRequest imageRequest, Object callerContext);
}
2.1.4 與第三方庫集成

Fresco 允許開發者將其與其他第三方庫集成,例如與 OkHttp 集成來替換默認的網絡請求庫,或者與 Glide 集成實現混合使用。

2.2 擴展模塊的使用場景

  • 支持新圖片格式:當項目中需要支持一些 Fresco 原生不支持的圖片格式時,可以通過自定義解碼器來實現。
  • 圖片特效處理:在圖片顯示前添加自定義的特效,如模糊、銳化等,提升用戶體驗。
  • 優化緩存策略:根據項目的具體需求,定制緩存鍵生成規則和緩存存儲方式,提高緩存命中率和性能。
  • 集成第三方庫:利用其他第三方庫的優勢,如 OkHttp 的高性能網絡請求能力,增強 Fresco 的功能。

三、自定義圖片解碼器擴展

3.1 實現自定義解碼器的步驟

要實現一個自定義的圖片解碼器,需要完成以下幾個步驟:

3.1.1 定義新的圖片格式

首先,需要在?ImageFormat?枚舉類中添加新的圖片格式。

java

// 圖片格式枚舉類,定義了支持的圖片格式
public enum ImageFormat {JPEG,PNG,GIF,WEBP,// 添加新的圖片格式CUSTOM_FORMAT;// 可以添加一些輔助方法,例如判斷是否為已知格式public static boolean isKnownFormat(ImageFormat format) {return format != null && format != UNKNOWN;}
}
3.1.2 實現新的圖片格式檢測方法

在?ImageFormatChecker?類中添加新的圖片格式檢測邏輯。

java

// 圖片格式檢測類,用于檢測圖片的格式
public class ImageFormatChecker {private static final int MARK_SIZE = 16;/*** 檢測圖片的格式* @param is 圖片數據的輸入流* @return 圖片的格式* @throws IOException 如果讀取輸入流時發生錯誤*/public static ImageFormat getImageFormat(InputStream is) throws IOException {if (is == null) {return ImageFormat.UNKNOWN;}// 標記輸入流的當前位置is.mark(MARK_SIZE);try {// 讀取輸入流的前幾個字節byte[] imageHeaderBytes = new byte[MARK_SIZE];int headerSize = readHeaderFromStream(is, imageHeaderBytes);// 根據讀取的字節判斷圖片的格式return getImageFormat_WrapIOException(imageHeaderBytes, headerSize);} finally {// 恢復輸入流的位置is.reset();}}/*** 從輸入流中讀取圖片的頭部信息* @param is 輸入流* @param imageHeaderBytes 用于存儲頭部信息的字節數組* @return 實際讀取的字節數* @throws IOException 如果讀取輸入流時發生錯誤*/private static int readHeaderFromStream(InputStream is, byte[] imageHeaderBytes) throws IOException {int totalBytesRead = 0;while (totalBytesRead < imageHeaderBytes.length) {int bytesRead = is.read(imageHeaderBytes, totalBytesRead, imageHeaderBytes.length - totalBytesRead);if (bytesRead == -1) {break;}totalBytesRead += bytesRead;}return totalBytesRead;}/*** 根據頭部字節信息判斷圖片的格式* @param imageHeaderBytes 圖片的頭部字節信息* @param headerSize 頭部信息的長度* @return 圖片的格式*/public static ImageFormat getImageFormat_WrapIOException(byte[] imageHeaderBytes, int headerSize) {if (isJpegFormat(imageHeaderBytes, headerSize)) {return ImageFormat.JPEG;} else if (isPngFormat(imageHeaderBytes, headerSize)) {return ImageFormat.PNG;} else if (isGifFormat(imageHeaderBytes, headerSize)) {return ImageFormat.GIF;} else if (isWebpFormat(imageHeaderBytes, headerSize)) {return ImageFormat.WEBP;} else if (isCustomFormat(imageHeaderBytes, headerSize)) {return ImageFormat.CUSTOM_FORMAT;}return ImageFormat.UNKNOWN;}/*** 判斷是否為 JPEG 格式* @param imageHeaderBytes 圖片的頭部字節信息* @param headerSize 頭部信息的長度* @return 如果是 JPEG 格式返回 true,否則返回 false*/private static boolean isJpegFormat(byte[] imageHeaderBytes, int headerSize) {return headerSize >= 2 &&imageHeaderBytes[0] == (byte) 0xFF &&imageHeaderBytes[1] == (byte) 0xD8;}/*** 判斷是否為 PNG 格式* @param imageHeaderBytes 圖片的頭部字節信息* @param headerSize 頭部信息的長度* @return 如果是 PNG 格式返回 true,否則返回 false*/private static boolean isPngFormat(byte[] imageHeaderBytes, int headerSize) {return headerSize >= 8 &&imageHeaderBytes[0] == (byte) 0x89 &&imageHeaderBytes[1] == (byte) 0x50 &&imageHeaderBytes[2] == (byte) 0x4E &&imageHeaderBytes[3] == (byte) 0x47 &&imageHeaderBytes[4] == (byte) 0x0D &&imageHeaderBytes[5] == (byte) 0x0A &&imageHeaderBytes[6] == (byte) 0x1A &&imageHeaderBytes[7] == (byte) 0x0A;}/*** 判斷是否為 GIF 格式* @param imageHeaderBytes 圖片的頭部字節信息* @param headerSize 頭部信息的長度* @return 如果是 GIF 格式返回 true,否則返回 false*/private static boolean isGifFormat(byte[] imageHeaderBytes, int headerSize) {return headerSize >= 6 &&imageHeaderBytes[0] == 'G' &&imageHeaderBytes[1] == 'I' &&imageHeaderBytes[2] == 'F' &&imageHeaderBytes[3] == '8' &&(imageHeaderBytes[4] == '7' || imageHeaderBytes[4] == '9') &&imageHeaderBytes[5] == 'a';}/*** 判斷是否為 WebP 格式* @param imageHeaderBytes 圖片的頭部字節信息* @param headerSize 頭部信息的長度* @return 如果是 WebP 格式返回 true,否則返回 false*/private static boolean isWebpFormat(byte[] imageHeaderBytes, int headerSize) {return headerSize >= 12 &&imageHeaderBytes[0] == 'R' &&imageHeaderBytes[1] == 'I' &&imageHeaderBytes[2] == 'F' &&imageHeaderBytes[3] == 'F' &&imageHeaderBytes[8] == 'W' &&imageHeaderBytes[9] == 'E' &&imageHeaderBytes[10] == 'B' &&imageHeaderBytes[11] == 'P';}/*** 判斷是否為自定義格式* @param imageHeaderBytes 圖片的頭部字節信息* @param headerSize 頭部信息的長度* @return 如果是自定義格式返回 true,否則返回 false*/private static boolean isCustomFormat(byte[] imageHeaderBytes, int headerSize) {// 根據自定義格式的頭部特征進行判斷// 例如,假設自定義格式的前兩個字節是 0xAA 和 0xBBreturn headerSize >= 2 &&imageHeaderBytes[0] == (byte) 0xAA &&imageHeaderBytes[1] == (byte) 0xBB;}
}
3.1.3 實現自定義解碼器類

創建一個新的解碼器類,實現?ImageDecoder?接口。

java

// 自定義圖片解碼器,用于解碼自定義格式的圖片
public class CustomImageDecoder implements ImageDecoder {@Overridepublic CloseableImage decodeImage(EncodedImage encodedImage, int length, ImageDecodeOptions options) {InputStream is = encodedImage.getInputStream();if (is == null) {return null;}try {// 實現自定義圖片格式的解碼邏輯// 這里只是示例,需要根據新格式的具體規范進行解碼Bitmap bitmap = decodeCustomFormat(is, options);if (bitmap == null) {return null;}return new CloseableStaticBitmap(bitmap, SimpleBitmapReleaser.getInstance());} catch (Exception e) {e.printStackTrace();return null;} finally {try {is.close();} catch (IOException e) {e.printStackTrace();}}}/*** 解碼自定義圖片格式* @param is 圖片數據的輸入流* @param options 解碼選項* @return 解碼后的 Bitmap 對象* @throws IOException 如果讀取輸入流時發生錯誤*/private Bitmap decodeCustomFormat(InputStream is, ImageDecodeOptions options) throws IOException {// 根據自定義格式的規范進行解碼// 例如,讀取特定的字節信息,解析圖片數據等// 這里只是示例,需要根據實際情況實現return null;}
}
3.1.4 注冊自定義解碼器

在?ImageDecoderRegistry?中注冊自定義解碼器。

java

// 圖片解碼器注冊表,用于管理不同格式圖片的解碼器
public class ImageDecoderRegistry {private static final ImageDecoderRegistry sInstance = new ImageDecoderRegistry();private final Map<ImageFormat, ImageDecoder> mDecoders = new HashMap<>();private ImageDecoderRegistry() {// 注冊默認的解碼器registerDecoder(ImageFormat.JPEG, new JpegImageDecoder());registerDecoder(ImageFormat.PNG, new PngImageDecoder());registerDecoder(ImageFormat.GIF, new GifImageDecoder());registerDecoder(ImageFormat.WEBP, new WebpImageDecoder());// 注冊自定義的解碼器registerDecoder(ImageFormat.CUSTOM_FORMAT, new CustomImageDecoder());}/*** 獲取 ImageDecoderRegistry 的單例實例* @return ImageDecoderRegistry 的單例實例*/public static ImageDecoderRegistry getInstance() {return sInstance;}/*** 注冊解碼器* @param imageFormat 圖片的格式* @param decoder 對應的解碼器*/public void registerDecoder(ImageFormat imageFormat, ImageDecoder decoder) {mDecoders.put(imageFormat, decoder);}/*** 根據圖片格式獲取對應的解碼器* @param imageFormat 圖片的格式* @return 對應的解碼器,如果未找到則返回 null*/public ImageDecoder getDecoder(ImageFormat imageFormat) {return mDecoders.get(imageFormat);}
}

3.2 自定義解碼器的調用流程

當 Fresco 需要解碼圖片時,會首先調用?ImageFormatChecker?檢測圖片的格式,然后根據格式從?ImageDecoderRegistry?中獲取對應的解碼器進行解碼。以下是調用流程的詳細分析:

java

// 獲取圖片的原始數據,封裝為 EncodedImage 對象
EncodedImage encodedImage = getEncodedImageFromSomewhere();
// 創建默認的圖片解碼器
DefaultImageDecoder decoder = new DefaultImageDecoder(ImageDecoderRegistry.getInstance());
// 創建解碼選項
ImageDecodeOptions options = ImageDecodeOptions.newBuilder().build();
// 檢測圖片格式
ImageFormat imageFormat = ImageFormatChecker.getImageFormat_WrapIOException(encodedImage.getInputStream());
// 根據圖片格式獲取對應的解碼器
ImageDecoder specificDecoder = ImageDecoderRegistry.getInstance().getDecoder(imageFormat);
if (specificDecoder != null) {// 調用具體的解碼器進行解碼CloseableImage closeableImage = specificDecoder.decodeImage(encodedImage, encodedImage.getSize(), options);if (closeableImage != null) {// 處理解碼后的圖片}
}

四、自定義圖片處理器擴展

4.1 實現自定義圖片處理器的步驟

要實現一個自定義的圖片處理器,需要完成以下步驟:

4.1.1 實現?Postprocessor?接口

創建一個新的類,實現?Postprocessor?接口,并實現其中的方法。

java

// 自定義圖片后處理器,用于對解碼后的圖片進行額外處理
public class CustomPostprocessor implements Postprocessor {@Overridepublic String getName() {return "CustomPostprocessor";}@Overridepublic void process(Bitmap destBitmap, Bitmap sourceBitmap) {// 實現自定義的圖片處理邏輯// 例如,對圖片進行模糊處理blurBitmap(destBitmap, sourceBitmap);}/*** 對圖片進行模糊處理* @param destBitmap 目標 Bitmap 對象,用于存儲處理后的圖片* @param sourceBitmap 源 Bitmap 對象,即解碼后的原始圖片*/private void blurBitmap(Bitmap destBitmap, Bitmap sourceBitmap) {// 使用 RenderScript 進行模糊處理RenderScript rs = RenderScript.create(Fresco.getContext());Allocation input = Allocation.createFromBitmap(rs, sourceBitmap);Allocation output = Allocation.createTyped(rs, input.getType());ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));script.setRadius(10f);script.setInput(input);script.forEach(output);output.copyTo(destBitmap);rs.destroy();}
}
4.1.2 在圖片請求中使用自定義處理器

在創建?ImageRequest?時,設置自定義的圖片處理器。

java

// 創建自定義圖片處理器實例
Postprocessor customPostprocessor = new CustomPostprocessor();
// 創建 ImageRequest
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse("http://example.com/image.jpg")).setPostprocessor(customPostprocessor).build();

4.2 自定義圖片處理器的調用流程

當圖片解碼完成后,Fresco 會檢查圖片請求中是否設置了圖片處理器,如果設置了,則會調用處理器的?process?方法對圖片進行處理。以下是調用流程的詳細分析:

java

// 獲取圖片請求
ImageRequest imageRequest = getImageRequestFromSomewhere();
// 解碼圖片
CloseableImage closeableImage = decodeImage(imageRequest);
if (closeableImage instanceof CloseableStaticBitmap) {CloseableStaticBitmap staticBitmap = (CloseableStaticBitmap) closeableImage;Bitmap sourceBitmap = staticBitmap.getUnderlyingBitmap();// 獲取圖片請求中的圖片處理器Postprocessor postprocessor = imageRequest.getPostprocessor();if (postprocessor != null) {// 創建目標 Bitmap 對象Bitmap destBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), sourceBitmap.getHeight(), sourceBitmap.getConfig());// 調用圖片處理器的 process 方法進行處理postprocessor.process(destBitmap, sourceBitmap);// 更新 CloseableStaticBitmap 中的 Bitmap 對象staticBitmap.setUnderlyingBitmap(destBitmap);}
}

五、自定義緩存策略擴展

5.1 實現自定義緩存鍵工廠

要實現自定義的緩存策略,首先需要實現自定義的緩存鍵工廠,實現?CacheKeyFactory?接口。

java

// 自定義緩存鍵工廠,用于生成自定義的緩存鍵
public class CustomCacheKeyFactory implements CacheKeyFactory {@Overridepublic CacheKey getBitmapCacheKey(ImageRequest imageRequest, Object callerContext) {// 生成自定義的 Bitmap 緩存鍵String url = imageRequest.getSourceUri().toString();// 可以添加額外的信息到緩存鍵中,例如圖片的尺寸int width = imageRequest.getResizeOptions() != null ? imageRequest.getResizeOptions().width : 0;int height = imageRequest.getResizeOptions() != null ? imageRequest.getResizeOptions().height : 0;String cacheKeyString = url + "_" + width + "x" + height;return new SimpleCacheKey(cacheKeyString);}@Overridepublic CacheKey getEncodedCacheKey(ImageRequest imageRequest, Object callerContext) {// 生成自定義的編碼圖片緩存鍵String url = imageRequest.getSourceUri().toString();return new SimpleCacheKey(url);}
}

5.2 自定義緩存類

可以實現自定義的緩存類,繼承自?MemoryCache?或?DiskCache?等基類,并重寫其中的方法。

java

// 自定義內存緩存類,繼承自 BaseMemoryCache
public class CustomMemoryCache extends BaseMemoryCache<CacheKey, CloseableImage> {public CustomMemoryCache(MemoryCacheParams memoryCacheParams, ValueDescriptor<CloseableImage> valueDescriptor, EntryEvictionComparatorSupplier<CacheKey, CloseableImage> entryEvictionComparatorSupplier) {super(memoryCacheParams, valueDescriptor, entryEvictionComparatorSupplier);}@Overrideprotected boolean isOrphan(Entry<CacheKey, CloseableImage> entry) {// 實現自定義的孤兒對象判斷邏輯return super.isOrphan(entry);}@Overrideprotected void onCacheHit(Entry<CacheKey, CloseableImage> entry) {// 實現緩存命中時的處理邏輯super.onCacheHit(entry);}@Overrideprotected void onCacheMiss() {// 實現緩存未命中時的處理邏輯super.onCacheMiss();}@Overrideprotected void onCachePut(Entry<CacheKey, CloseableImage> entry) {// 實現緩存插入時的處理邏輯super.onCachePut(entry);}@Overrideprotected void onCacheEviction(Entry<CacheKey, CloseableImage> entry) {// 實現緩存淘汰時的處理邏輯super.onCacheEviction(entry);}
}

5.3 配置自定義緩存策略

在創建?ImagePipelineConfig?時,配置自定義的緩存鍵工廠和緩存類。

java

// 創建自定義緩存鍵工廠實例
CacheKeyFactory customCacheKeyFactory = new CustomCacheKeyFactory();
// 創建自定義內存緩存實例
MemoryCache<CacheKey, CloseableImage> customMemoryCache = new CustomMemoryCache(new MemoryCacheParams(10 * 1024 * 1024, // 最大緩存大小Integer.MAX_VALUE, // 最大緩存項數量10 * 1024 * 1024, // 最大緩存項大小Integer.MAX_VALUE, // 最大緩存項年齡Integer.MAX_VALUE // 最大緩存項數量),new CloseableImageValueDescriptor(),new EntryEvictionComparatorSupplier<CacheKey, CloseableImage>() {@Overridepublic Comparator<Entry<CacheKey, CloseableImage>> get() {return new EntryEvictionComparator<CacheKey, CloseableImage>() {@Overridepublic int compare(Entry<CacheKey, CloseableImage> lhs, Entry<CacheKey, CloseableImage> rhs) {// 實現自定義的緩存項淘汰比較邏輯return 0;}};}}
);
// 創建 ImagePipelineConfig
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context).setCacheKeyFactory(customCacheKeyFactory).setBitmapMemoryCache(customMemoryCache).build();
// 初始化 Fresco
Fresco.initialize(context, config);

5.4 自定義緩存策略的調用流程

當 Fresco 進行圖片緩存操作時,會使用自定義的緩存鍵工廠生成緩存鍵,然后根據緩存鍵在自定義的緩存類中進行查找、插入或淘汰操作。以下是調用流程的詳細分析:

java

// 獲取圖片請求
ImageRequest imageRequest = getImageRequestFromSomewhere();
// 獲取自定義緩存鍵工廠
CacheKeyFactory cacheKeyFactory = ImagePipelineFactory.getInstance().getCacheKeyFactory();
// 生成 Bitmap 緩存鍵
CacheKey bitmapCacheKey = cacheKeyFactory.getBitmapCacheKey(imageRequest, null);
// 獲取自定義內存緩存
MemoryCache<CacheKey, CloseableImage> memoryCache = ImagePipelineFactory.getInstance().getBitmapMemoryCache();
// 從緩存中查找圖片
CloseableReference<CloseableImage> cachedImage = memoryCache.get(bitmapCacheKey);
if (cachedImage != null) {// 緩存命中,處理緩存中的圖片
} else {// 緩存未命中,進行圖片加載和解碼操作CloseableImage closeableImage = decodeImage(imageRequest);if (closeableImage != null) {// 將解碼后的圖片存入緩存memoryCache.cache(bitmapCacheKey, CloseableReference.of(closeableImage));}
}

六、與第三方庫集成擴展

6.1 與 OkHttp 集成

OkHttp 是一個高性能的 HTTP 客戶端庫,將 Fresco 與 OkHttp 集成可以提升網絡請求的性能。

6.1.1 添加依賴

在項目的?build.gradle?文件中添加 OkHttp 的依賴。

groovy

implementation 'com.squareup.okhttp3:okhttp:4.9.3'
6.1.2 實現 OkHttp 網絡請求處理器

創建一個實現?NetworkFetcher?接口的類,使用 OkHttp 進行網絡請求。

java

// 使用 OkHttp 實現的網絡請求處理器
public class OkHttpNetworkFetcher implements NetworkFetcher<OkHttpNetworkFetchState> {private final OkHttpClient mOkHttpClient;public OkHttpNetworkFetcher(OkHttpClient okHttpClient) {this.mOkHttpClient = okHttpClient;}@Overridepublic OkHttpNetworkFetchState createFetchState(ImageRequest request, Object callerContext) {return new OkHttpNetworkFetchState(request, callerContext);}@Overridepublic void fetch(final OkHttpNetworkFetchState fetchState, final Callback callback) {Request okHttpRequest = new Request.Builder().url(fetchState.getRequest().getSourceUri().toString()).build();mOkHttpClient.newCall(okHttpRequest).enqueue(new okhttp3.Callback() {@Overridepublic void onFailure(Call call, IOException e) {callback.onFailure(fetchState, e);}@Overridepublic void onResponse(Call call, Response response) throws IOException {if (!response.isSuccessful()) {callback.onFailure(fetchState, new IOException("Unexpected code " + response));return;}callback.onResponse(fetchState, response.body().

6.1 與 OkHttp 集成

6.1.3 配置 ImagePipelineConfig 使用 OkHttp

在創建?ImagePipelineConfig?時,將?OkHttpNetworkFetcher?配置進去,讓 Fresco 使用 OkHttp 進行網絡請求。

java

import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import okhttp3.OkHttpClient;
import android.content.Context;// ...// 創建 OkHttpClient 實例
OkHttpClient okHttpClient = new OkHttpClient();// 創建 ImagePipelineConfig 并使用 OkHttpNetworkFetcher
Context context = getApplicationContext(); // 獲取上下文
ImagePipelineConfig config = OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient).build();// 初始化 Fresco
Fresco.initialize(context, config);
6.1.4 集成后的調用流程

當 Fresco 需要從網絡加載圖片時,會調用?OkHttpNetworkFetcher?的?fetch?方法。該方法會使用 OkHttp 發起網絡請求,獲取圖片數據。以下是詳細的調用流程分析:

java

// 獲取圖片請求
ImageRequest imageRequest = getImageRequestFromSomewhere();// 獲取網絡請求處理器
NetworkFetcher<OkHttpNetworkFetchState> networkFetcher = ImagePipelineFactory.getInstance().getNetworkFetcher();// 創建網絡請求狀態
OkHttpNetworkFetchState fetchState = networkFetcher.createFetchState(imageRequest, null);// 發起網絡請求
networkFetcher.fetch(fetchState, new NetworkFetcher.Callback() {@Overridepublic void onResponse(OkHttpNetworkFetchState fetchState,InputStream responseData,int responseContentLength,int byteRangeFrom,int byteRangeTo) {// 處理響應數據,例如將數據傳遞給解碼器try {// 這里可以將 responseData 傳遞給解碼器進行解碼// 示例代碼,假設 decodeImage 是解碼方法CloseableImage closeableImage = decodeImage(responseData);if (closeableImage != null) {// 處理解碼后的圖片}} catch (Exception e) {e.printStackTrace();}}@Overridepublic void onFailure(OkHttpNetworkFetchState fetchState, Throwable throwable) {// 處理請求失敗的情況Log.e("OkHttpNetworkFetcher", "Network request failed: " + throwable.getMessage());}@Overridepublic void onCancellation(OkHttpNetworkFetchState fetchState) {// 處理請求取消的情況Log.d("OkHttpNetworkFetcher", "Network request cancelled");}
});

6.2 與 Glide 集成

雖然 Fresco 和 Glide 都是強大的圖片加載框架,但在某些情況下,可能需要在項目中同時使用它們。可以通過一些方式實現二者的集成。

6.2.1 實現圖片加載代理類

創建一個代理類,根據不同的條件選擇使用 Fresco 或 Glide 進行圖片加載。

java

import android.content.Context;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.request.ImageRequest;// 圖片加載代理類
public class ImageLoaderProxy {private static final boolean USE_FRESCO = true; // 示例開關,可根據實際情況調整public static void loadImage(Context context, String url, ImageView imageView) {if (USE_FRESCO) {// 使用 Fresco 加載圖片if (imageView instanceof SimpleDraweeView) {SimpleDraweeView draweeView = (SimpleDraweeView) imageView;ImageRequest request = ImageRequest.fromUri(url);draweeView.setImageRequest(request);}} else {// 使用 Glide 加載圖片Glide.with(context).load(url).into(imageView);}}
}
6.2.2 在項目中使用代理類

在項目中需要加載圖片的地方,使用?ImageLoaderProxy?類進行圖片加載。

java

import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ImageView imageView = findViewById(R.id.imageView);String imageUrl = "https://example.com/image.jpg";// 使用代理類加載圖片ImageLoaderProxy.loadImage(this, imageUrl, imageView);}
}
6.2.3 集成的優勢和注意事項
  • 優勢:可以根據不同的需求靈活選擇使用 Fresco 或 Glide 的優勢功能。例如,Fresco 在處理大圖和內存管理方面表現出色,而 Glide 在簡單圖片加載和動畫支持方面有優勢。
  • 注意事項:需要注意避免兩個框架的緩存沖突,確保在使用過程中不會出現重復加載和內存浪費的問題。

6.3 與 RxJava 集成

RxJava 是一個在 Java 虛擬機上使用可觀測的序列來組成異步的、基于事件的程序的庫。將 Fresco 與 RxJava 集成可以方便地處理異步圖片加載和事件流。

6.3.1 添加依賴

在項目的?build.gradle?文件中添加 RxJava 的依賴。

groovy

implementation 'io.reactivex.rxjava3:rxjava:3.1.5'
6.3.2 實現 RxJava 包裝器

創建一個包裝類,將 Fresco 的圖片加載操作封裝成 RxJava 的?Observable

java

import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.schedulers.Schedulers;// RxJava 包裝器類
public class FrescoRxWrapper {public static Observable<CloseableImage> loadImage(String url) {return Observable.create(emitter -> {ImageRequest request = ImageRequestBuilder.newBuilderWithSource(android.net.Uri.parse(url)).build();ImagePipeline imagePipeline = Fresco.getImagePipeline();com.facebook.imagepipeline.core.DataSource<com.facebook.common.references.CloseableReference<CloseableImage>>dataSource = imagePipeline.fetchDecodedImage(request, null);dataSource.subscribe(new com.facebook.imagepipeline.core.BaseBitmapDataSubscriber() {@Overrideprotected void onNewResultImpl(android.graphics.Bitmap bitmap) {if (dataSource.isFinished() && bitmap != null) {// 這里可以根據實際情況將 Bitmap 封裝成 CloseableImage// 示例代碼,假設存在一個方法將 Bitmap 轉換為 CloseableImageCloseableImage closeableImage = convertBitmapToCloseableImage(bitmap);emitter.onNext(closeableImage);emitter.onComplete();}}@Overrideprotected void onFailureImpl(com.facebook.imagepipeline.core.DataSource<com.facebook.common.references.CloseableReference<CloseableImage>> dataSource) {emitter.onError(dataSource.getFailureCause());}}, Schedulers.io());});}private static CloseableImage convertBitmapToCloseableImage(android.graphics.Bitmap bitmap) {// 實現將 Bitmap 轉換為 CloseableImage 的邏輯// 示例代碼,假設使用 CloseableStaticBitmapreturn new com.facebook.imagepipeline.image.CloseableStaticBitmap(bitmap,com.facebook.imagepipeline.bitmaps.SimpleBitmapReleaser.getInstance());}
}
6.3.3 在項目中使用 RxJava 包裝器

在項目中可以使用 RxJava 的操作符來處理圖片加載事件。

java

import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.Disposable;public class MainActivity extends AppCompatActivity {private ImageView imageView;private Disposable disposable;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);imageView = findViewById(R.id.imageView);String imageUrl = "https://example.com/image.jpg";// 使用 RxJava 包裝器加載圖片disposable = FrescoRxWrapper.loadImage(imageUrl).observeOn(AndroidSchedulers.mainThread()).subscribe(closeableImage -> {if (closeableImage instanceof com.facebook.imagepipeline.image.CloseableStaticBitmap) {com.facebook.imagepipeline.image.CloseableStaticBitmap staticBitmap =(com.facebook.imagepipeline.image.CloseableStaticBitmap) closeableImage;android.graphics.Bitmap bitmap = staticBitmap.getUnderlyingBitmap();imageView.setImageBitmap(bitmap);}}, throwable -> {// 處理加載失敗的情況throwable.printStackTrace();});}@Overrideprotected void onDestroy() {super.onDestroy();if (disposable != null && !disposable.isDisposed()) {disposable.dispose();}}
}
6.3.4 集成的好處
  • 異步處理:使用 RxJava 可以方便地進行異步圖片加載,避免阻塞主線程。
  • 事件流處理:可以使用 RxJava 的各種操作符對圖片加載事件進行過濾、轉換等處理,提高代碼的可讀性和可維護性。

七、擴展模塊的性能優化

7.1 自定義解碼器性能優化

  • 使用硬件加速:在自定義解碼器中,如果可能的話,盡量使用 Android 系統的硬件加速功能來提高解碼速度。例如,在解碼 JPEG 圖片時,可以使用?BitmapFactory.Options?的?inPreferredConfig?屬性設置為?Bitmap.Config.RGB_565,這樣可以減少內存占用并提高解碼速度。

java

// 在自定義解碼器中使用硬件加速
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
  • 緩存解碼結果:對于一些經常使用的圖片,可以將解碼后的結果進行緩存,避免重復解碼。可以使用內存緩存或磁盤緩存來存儲解碼后的圖片。

java

// 示例:使用 LruCache 進行內存緩存
private LruCache<String, CloseableImage> decodeCache = new LruCache<>(10);@Override
public CloseableImage decodeImage(EncodedImage encodedImage, int length, ImageDecodeOptions options) {String cacheKey = encodedImage.getSourceUri().toString();CloseableImage cachedImage = decodeCache.get(cacheKey);if (cachedImage != null) {return cachedImage;}// 進行解碼操作CloseableImage decodedImage = performDecode(encodedImage, length, options);if (decodedImage != null) {decodeCache.put(cacheKey, decodedImage);}return decodedImage;
}

7.2 自定義圖片處理器性能優化

  • 避免重復處理:在自定義圖片處理器中,要確保處理邏輯不會對同一張圖片進行重復處理。可以通過設置標記或使用緩存來避免重復操作。

java

// 在自定義圖片處理器中避免重復處理
private Set<String> processedImages = new HashSet<>();@Override
public void process(Bitmap destBitmap, Bitmap sourceBitmap) {String imageKey = getImageKey(sourceBitmap);if (processedImages.contains(imageKey)) {return;}// 進行圖片處理操作performProcessing(destBitmap, sourceBitmap);processedImages.add(imageKey);
}private String getImageKey(Bitmap bitmap) {// 生成圖片的唯一鍵return String.valueOf(bitmap.hashCode());
}
  • 使用高效算法:在進行圖片處理時,盡量使用高效的算法和數據結構,減少計算量和內存占用。例如,在進行模糊處理時,可以使用 RenderScript 來提高處理速度。

7.3 自定義緩存策略性能優化

  • 合理設置緩存大小:在自定義緩存類中,要根據設備的內存情況和項目的需求,合理設置緩存的大小。避免緩存過大導致內存溢出,也避免緩存過小導致緩存命中率過低。

java

// 合理設置內存緩存大小
MemoryCacheParams memoryCacheParams = new MemoryCacheParams(10 * 1024 * 1024, // 最大緩存大小Integer.MAX_VALUE, // 最大緩存項數量10 * 1024 * 1024, // 最大緩存項大小Integer.MAX_VALUE, // 最大緩存項年齡Integer.MAX_VALUE // 最大緩存項數量
);
  • 優化緩存淘汰策略:在自定義緩存類中,要實現合理的緩存淘汰策略,確保經常使用的圖片能夠保留在緩存中,而不常用的圖片能夠及時被淘汰。

java

// 實現自定義的緩存淘汰比較邏輯
@Override
public int compare(Entry<CacheKey, CloseableImage> lhs, Entry<CacheKey, CloseableImage> rhs) {// 根據訪問時間進行比較,訪問時間越久的越先淘汰long lhsAccessTime = lhs.getLastAccessTime();long rhsAccessTime = rhs.getLastAccessTime();return Long.compare(lhsAccessTime, rhsAccessTime);
}

八、擴展模塊的異常處理

8.1 自定義解碼器異常處理

在自定義解碼器中,可能會出現各種異常,例如輸入流讀取錯誤、圖片格式不支持等。需要對這些異常進行捕獲和處理。

java

@Override
public CloseableImage decodeImage(EncodedImage encodedImage, int length, ImageDecodeOptions options) {InputStream inputStream = encodedImage.getInputStream();if (inputStream == null) {return null;}try {// 進行解碼操作return performDecode(inputStream, length, options);} catch (IOException e) {// 處理輸入流讀取錯誤Log.e("CustomDecoder", "Error reading input stream: " + e.getMessage());return null;} catch (UnsupportedOperationException e) {// 處理圖片格式不支持的異常Log.e("CustomDecoder", "Unsupported image format: " + e.getMessage());return null;} finally {try {inputStream.close();} catch (IOException e) {// 處理關閉輸入流時的異常Log.e("CustomDecoder", "Error closing input stream: " + e.getMessage());}}
}

8.2 自定義圖片處理器異常處理

在自定義圖片處理器中,可能會出現 Bitmap 操作異常、RenderScript 異常等。需要對這些異常進行捕獲和處理。

java

@Override
public void process(Bitmap destBitmap, Bitmap sourceBitmap) {try {// 進行圖片處理操作performProcessing(destBitmap, sourceBitmap);} catch (NullPointerException e) {// 處理 Bitmap 為空的異常Log.e("CustomProcessor", "Bitmap is null: " + e.getMessage());} catch (RuntimeException e) {// 處理 RenderScript 異常Log.e("CustomProcessor", "RenderScript error: " + e.getMessage());}
}

8.3 自定義緩存策略異常處理

在自定義緩存策略中,可能會出現緩存寫入錯誤、緩存讀取錯誤等異常。需要對這些異常進行捕獲和處理。

java

@Override
public CloseableReference<CloseableImage> get(CacheKey key) {try {// 從緩存中讀取數據return cache.get(key);} catch (Exception e) {// 處理緩存讀取錯誤Log.e("CustomCache", "Error reading from cache: " + e.getMessage());return null;}
}@Override
public boolean cache(CacheKey key, CloseableReference<CloseableImage> value) {try {// 將數據寫入緩存return cache.cache(key, value);} catch (Exception e) {// 處理緩存寫入錯誤Log.e("CustomCache", "Error writing to cache: " + e.getMessage());return false;}
}

九、擴展模塊在實際項目中的應用案例

9.1 支持特殊圖片格式的電商應用

在一個電商應用中,可能會遇到一些特殊格式的商品圖片,如某種加密的圖片格式。可以使用自定義解碼器來支持這種特殊格式的圖片。

java

// 自定義加密圖片解碼器
public class EncryptedImageDecoder implements ImageDecoder {@Overridepublic CloseableImage decodeImage(EncodedImage encodedImage, int length, ImageDecodeOptions options) {InputStream inputStream = encodedImage.getInputStream();if (inputStream == null) {return null;}try {// 解密圖片數據InputStream decryptedStream = decryptStream(inputStream);// 使用默認解碼器進行解碼ImageDecoder defaultDecoder = ImageDecoderRegistry.getInstance().getDecoder(ImageFormat.JPEG);return defaultDecoder.decodeImage(new EncodedImage(decryptedStream), length, options);} catch (Exception e) {Log.e("EncryptedDecoder", "Error decrypting image: " + e.getMessage());return null;} finally {try {inputStream.close();} catch (IOException e) {Log.e("EncryptedDecoder", "Error closing input stream: " + e.getMessage());}}}private InputStream decryptStream(InputStream inputStream) throws IOException {// 實現解密邏輯// 示例代碼,假設使用 AES 加密byte[] encryptedData = readAllBytes(inputStream);byte[] decryptedData = decrypt(encryptedData);return new ByteArrayInputStream(decryptedData);}private byte[] readAllBytes(InputStream inputStream) throws IOException {ByteArrayOutputStream buffer = new ByteArrayOutputStream();int nRead;byte[] data = new byte[16384];while ((nRead = inputStream.read(data, 0, data.length)) != -1) {buffer.write(data, 0, nRead);}buffer.flush();return buffer.toByteArray();}private byte[] decrypt(byte[] encryptedData) {// 實現 AES 解密邏輯// 示例代碼,這里只是簡單返回原始數據return encryptedData;}
}

9.2 圖片特效處理的社交應用

在一個社交應用中,用戶可能希望對上傳的圖片添加一些特效,如模糊、銳化等。可以使用自定義圖片處理器來實現這些特效。

java

// 自定義模糊圖片處理器
public class BlurPostprocessor implements Postprocessor {@Overridepublic String getName() {return "BlurPostprocessor";}@Overridepublic void process(Bitmap destBitmap, Bitmap sourceBitmap) {// 使用 RenderScript 進行模糊處理RenderScript rs = RenderScript.create(Fresco.getContext());Allocation input = Allocation.createFromBitmap(rs, sourceBitmap);Allocation output = Allocation.createTyped(rs, input.getType());ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));script.setRadius(10f);script.setInput(input);script.forEach(output);output.copyTo(destBitmap);rs.destroy();}
}

9.3 優化緩存策略的新聞應用

在一個新聞應用中,可能會有大量的圖片需要加載和緩存。可以使用自定義緩存策略來優化緩存性能,提高圖片加載速度。

java

// 自定義緩存鍵工廠,根據圖片的分類和尺寸生成緩存鍵
public class NewsCacheKeyFactory implements CacheKeyFactory {@Overridepublic CacheKey getBitmapCacheKey(ImageRequest imageRequest, Object callerContext) {String url = imageRequest.getSourceUri().toString();String category = getCategoryFromUrl(url);int width = imageRequest.getResizeOptions() != null ? imageRequest.getResizeOptions().width : 0;int height = imageRequest.getResizeOptions() != null ? imageRequest.getResizeOptions().height : 0;String cacheKeyString = url + "_" + category + "_" + width + "x" + height;return new SimpleCacheKey(cacheKeyString);}@Overridepublic CacheKey getEncodedCacheKey(ImageRequest imageRequest, Object callerContext) {String url = imageRequest.getSourceUri().toString();String category = getCategoryFromUrl(url);return new SimpleCacheKey(url + "_" + category);}private String getCategoryFromUrl(String url) {// 從 URL 中提取圖片的分類信息// 示例代碼,假設 URL 中包含分類信息if (url.contains("sports")) {return "sports";} else if (url.contains("entertainment")) {return "entertainment";}return "other";}
}

十、總結

Fresco 的擴展模塊為開發者提供了強大的定制能力,可以根據項目的具體需求對框架進行擴展和優化。通過自定義圖片解碼器、圖片處理器、緩存策略以及與第三方庫的集成,開發者可以實現對新圖片格式的支持、圖片特效處理、緩存性能優化等功能。

在實現擴展模塊時,需要注意性能優化和異常處理,確保擴展功能的穩定性和高效性。同時,通過實際項目案例可以看到,擴展模塊在不同類型的應用中都有廣泛的應用場景,可以為應用的用戶體驗和性能提升帶來顯著的效果。

隨著 Android 開發技術的不斷發展,Fresco 的擴展模塊也將不斷完善和豐富,為開發者提供更多的可能性。開發者可以充分利用這些擴展功能,打造出更加優秀的 Android 應用。

以上就是對 Android Fresco 框架擴展模塊的深入分析,希望對開發者在使用和擴展 Fresco 框架時有所幫助。在實際開發過程中,開發者可以根據具體需求靈活運用這些擴展功能,不斷探索和創新。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/76494.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/76494.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/76494.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

藍橋杯最后十天沖刺 day 2 雙指針的思想

雙指針思想介紹 雙指針&#xff08;Two Pointers&#xff09;是一種在數組或鏈表等線性結構中常用的算法技巧&#xff0c;通過使用兩個指針&#xff08;索引或引用&#xff09;以不同的速度或方向遍歷數據結構&#xff0c;從而高效解決問題。雙指針通常用于優化暴力解法&#…

Axure 使用筆記

1.Axure如何制作頁面彈窗 https://blog.csdn.net/SDTechnology/article/details/143948691 2.axure 怎么點擊按鈕打開新頁面 &#xff08;1&#xff09;新建交互 &#xff08;2&#xff09;單擊是觸發 &#xff08;3&#xff09;選擇打開鏈接 &#xff08;4&#xff09;選擇…

STM32實現一個簡單電燈

新建工程的步驟 建立工程文件夾&#xff0c;Keil中新建工程&#xff0c;選擇型號工程文件夾里建立Start、Library、User等文件夾&#xff0c;復制固件庫里面的文件到工程文件夾工程里對應建立Start、Library、User等同名稱的分組&#xff0c;然后將文件夾內的文件添加到工程分組…

html5炫酷圖片懸停效果實現詳解

html5炫酷圖片懸停效果實現詳解 這里寫目錄標題 html5炫酷圖片懸停效果實現詳解項目介紹技術棧核心功能實現1. 頁面布局2. 圖片容器樣式3. 炫酷懸停效果縮放效果傾斜效果模糊效果旋轉效果 4. 懸停文字效果5. 性能優化6. 響應式設計 項目亮點總結 項目介紹 本文將詳細介紹如何使…

Playwright與Browser Use:領略AI賦能UI自動化測試的魔法魅力

目錄 Browser Use是什么&#xff1f; Playwright簡介 框架設計的核心目標與原則 Playwright 在 UI 自動化測試中的優勢 如何高效攔截錯誤 實現視頻錄制 UI自動化框架設計的挑戰 測試框架的結構與模塊化設計 自動化測試不是銀彈 走進Browser Use 橫空出世的背景與意義…

Uniapp 實現微信小程序滑動面板功能詳解

文章目錄 前言一、功能概述二、實現思路三、代碼實現總結 前言 Uniapp 實現微信小程序滑動面板功能詳解 一、功能概述 滑動面板是移動端常見的交互組件&#xff0c;通常用于在頁面底部展開內容面板。本文將介紹如何使用 Uniapp 開發一個支持手勢滑動的底部面板組件&#xff0…

【FAQ】HarmonyOS SDK 閉源開放能力 —Push Kit(12)

1.問題描述&#xff1a; pushdeviceid的長度是固定的嗎&#xff1f; 解決方案&#xff1a; 在鴻蒙系統中&#xff0c;設備ID的長度是固定的。 2.問題描述&#xff1a; 通過REST API三方推送IM類消息&#xff0c;如何實現應用處于前臺時不展示三方推送通知。 解決方案&…

【小兔鮮】day02 Pinia、項目起步、Layout

【小兔鮮】day02 Pinia、項目起步、Layout 1. Pinia2. 添加Pinia到Vue項目3. 案例&#xff1a;Pinia-counter基礎使用3.1 Store 是什么&#xff1f;3.2 應該在什么時候使用 Store? 4. Pinia-getters和異步action4.1 getters4.2 action如何實現異步 1. Pinia Pinia 是 Vue 的專…

Android學習之計算器app(java + 詳細注釋 + 源碼)

運行結果&#xff1a; 基礎的四則運算&#xff1a; 可能會出現的問題以及解決方法&#xff1a; 問題1&#xff1a;出現多個操作符。 例子&#xff1a;12 解決方法&#xff1a; 在用戶點擊操作符之后&#xff0c;去檢查之前的最后一位&#xff0c;如果最后一位也是操作符的話…

GMap.NET + WPF:構建高性能 ADS-B 航空器追蹤平臺

ADS-B 簡介 ADS - B&#xff08;Automatic Dependent Surveillance - Broadcast&#xff0c;廣播式自動相關監視&#xff09;是一種先進的航空監視技術。它依靠飛機上的機載設備&#xff0c;自動收集諸如飛機的位置、高度、速度、航向等關鍵數據&#xff0c;并周期性地以廣播的…

關于testng.xml無法找到類的問題

問題&#xff1a;testng.xml添加測試類的時候飄紅 解決辦法&#xff1a; 1.試圖通過自動生成testng.xml插件去解決&#xff0c;感覺也不是這個問題&#xff0c;沒有嘗試&#xff1b; 2.以為是創建包的方式不對&#xff0c;重新刪除后新建--還是找不到 想新建類的時候發現從m…

數據在內存中存儲(C語言)

文章目錄 前言一、整數在內存中的存儲1.1 計算機存儲數據的基本單位示例代碼 1.2 無符號整數的存儲1.3 有符號整數的存儲&#xff08;補碼&#xff09;示例代碼 二、大小端字節序和字節序判斷2.1 什么是大小端&#xff1f;示例代碼 2.2 為什么會有大小端&#xff1f;2.3 字節序…

Python爬蟲第2節-網頁基礎和爬蟲基本原理

目錄 一、網頁基礎 1.1 網頁的組成 1.2 網頁的結構 1.3 節點樹及節點間的關系 1.4 選擇器 二、爬蟲的基本原理 2.1 爬蟲概述 2.2 能抓怎樣的數據 2.3 JavaScript 渲染頁面 一、網頁基礎 使用瀏覽器訪問網站時&#xff0c;我們會看到各式各樣的頁面。你是否思考過&…

python-leetcode 64.在排序數組中查找元素的第一個和最后一個位置

題目&#xff1a; 給一個按照非遞減順序排列的整數數組nums,和一個目標值target,請找出給定目標值在數組中的開始位置和結束位置。 如果數組中不存在目標值target,返回[-1,-1] 方法一&#xff1a;二分查找 直觀的思路肯定是從前往后遍歷一遍。用兩個變量記錄第一次和最后一次…

分享一些新版GPT-4o使用方式!能多模態生圖!

目前GPT-4o的整體測評&#xff0c;真的很驚艷。 不知道又有多少人因為OpenAI的這次更新而失業&#xff0c;當然只要AI用得好&#xff0c;會有更多人因之而受益&#xff01;很多人表示不知道怎么用&#xff0c;對于門外漢來說&#xff0c;4o似乎有點高端。 今天就給大家介紹幾…

軟件工程面試題(二十四)

1、連接池的原理 j2ee 服務器啟動時會建立一定數量的池連接,并一直維持不少于此數量的池連接。當客戶端程序需要連接時,吃驅動程序會返回一個未使用的池連接并將其標記為忙。如果當前 沒有空閑連接,池驅動就建立一定新的 連接 2、用javascript編寫腳本小程序,實現點擊全選…

Android:Dialog的使用詳解

Android中Dialog的使用詳解 Dialog&#xff08;對話框&#xff09;是Android中常用的UI組件&#xff0c;用于臨時顯示重要信息或獲取用戶輸入。 1. 基本Dialog類型 1.1 AlertDialog&#xff08;警告對話框&#xff09; 最常用的對話框類型&#xff0c;可以設置標題、消息、…

arinc818 fpga單色圖像傳輸ip

arinc818協議支持的常用線速率如下圖 隨著圖像分辨率的提高&#xff0c;單lane的速率無法滿足特定需求&#xff0c;一種方式是通過多個LANE交叉的去傳輸圖像&#xff0c;另外一種是通過降低圖像的帶寬&#xff0c;即通過只傳單色圖像達到對應的效果 程序架構如下圖所示&#x…

透視投影(Perspective projection)與等距圓柱投影(Equirectangular projection)

一、透視投影 1.方法概述 Perspective projection&#xff08;透視投影&#xff09;是一種模擬人眼觀察三維空間物體時的視覺效果的投影方法。它通過模擬觀察者從一個特定視點觀察三維場景的方式來創建二維圖像。在透視投影中&#xff0c;遠處的物體看起來比近處的物體小&…

三.微服務架構中的精妙設計:服務注冊/服務發現-Eureka

一.使用注冊中心背景 1.1服務遠程調用問題 服務之間遠程調?時, 我們的URL是寫死的 String url "http://127.0.0.1:9090/product/" orderInfo.getProductId(); 缺點&#xff1a; 當更換機器, 或者新增機器時, 這個URL就需要跟著變更, 就需要去通知所有的相關服…