ImageLoader防盗链

本文介绍如何在ImageLoader中实现防盗链功能,通过对ImageLoader源码解析,找到网络请求的位置并添加自定义防盗链逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0.背景

公司是做和金融相关的,最近领导要整加密,对于图片这种要做防盗链处理,与一般的使用http自带的refer不同的是这次处理是需要向http请求头中添加请求参数,之前的加载图片的框架使用的ImageLoader,但并未听说过有这方面的api(如果有麻烦告诉下,谢谢)。幸好对http还是有简单了解,知道这个的处理无非就是在http请求中添加头或者其他信息。本着这个方向,开始向ImageLoader源码中挖掘(来不及看分析的同学可以直接移步到2.添加防盗链逻辑)。
##1.寻找网络请求
我们在使用ImageLoader的时候一般是先在Application中进行初始化,然后在配置ImageLoaderConfig:

public static final DisplayImageOptions imageLoaderBanner = new DisplayImageOptions.Builder()
            .showImageOnLoading(R.mipmap.loading)
            .showImageForEmptyUri(R.mipmap.empty)
            .showImageOnFail(R.mipmap.fail).cacheInMemory(true)
            .cacheOnDisc(true).considerExifParams(true)
            .bitmapConfig(Bitmap.Config.RGB_565).build();

最后,调用:

ImageLoader.getInstance().displayImage(url, imageView, imageLoaderConfig);

既然url是从调用的方法传入的,那么我们有理由认为网路请求就在这个方法里。点击进去查看,可以看到如下代码:

 if(bmp != null && !bmp.isRecycled()) {
  ...
  } else {
    if(options.shouldShowImageOnLoading()) {                         
    imageAware.setImageDrawable(options.getImageOnLoading(this.configuration.resources));
    } else if(options.isResetViewBeforeLoading()) {                       
    imageAware.setImageDrawable((Drawable)null);
    }
imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri));
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, defineHandler(options));
   if(options.isSyncLoading()) {
     displayTask.run();
   } else {
     this.engine.submit(displayTask);
   }
 }

上面的if判断走的是缓存,else中是从网络中获取,里面的LoadAndDisplayImageTask就是异步加载图片的任务。点击到LoadAndDisplayImageTask中,查看里面的run方法:

public void run() {
     ...
     Bitmap bmp;
     label129: {
         ...          
      bmp = this.tryLoadBitmap();
	     ...
       }
      ...
    }

其实这一大段中,我们只关心他的bmp是怎么来的,里面调用了 bmp = this.tryLoadBitmap(),继续查看里面代码:

private Bitmap tryLoadBitmap() throws LoadAndDisplayImageTask.TaskCancelledException {
	Bitmap bitmap = null;
	...
    bitmap = this.decodeImage(imageUriForDecoding);
    ...          
    return bitmap;
    }

同样,我们只要知道bitmap的生成,即this.decodeImage(imageUriForDecoding)这个方法,继续查看

private Bitmap decodeImage(String imageUri) throws IOException {
        ViewScaleType viewScaleType = this.imageAware.getScaleType();
        ImageDecodingInfo decodingInfo = new ImageDecodingInfo(this.memoryCacheKey, imageUri, this.uri, this.targetSize, viewScaleType, this.getDownloader(), this.options);
        return this.decoder.decode(decodingInfo);
    }

这时候调用的是decoder.decode的方法,decoder又是从engine.configuration.decoder来的,这个在ImageLoader中就已经赋值了,是new ImageLoaderEngine(ImageLoaderConfiguration),所以此时的engine就是从ImageLoader中的ImageLoaderConfiguration中得到的。而我们在一般使用时候没有对ImageLoaderConfiguration的decoder赋值,因此在ImageLoaderConfiguration中执行下面逻辑:

 if(this.decoder == null) {
	this.decoder = DefaultConfigurationFactory.createImageDecoder(this.writeLogs);
	}

继续

 public static ImageDecoder createImageDecoder(boolean loggingEnabled) {
	return new BaseImageDecoder(loggingEnabled);
 }

public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException {
        InputStream imageStream = this.getImageStream(decodingInfo);
        if(imageStream == null) {
            L.e("No stream for image [%s]", new Object[]{decodingInfo.getImageKey()});
            return null;
        } else {
            Bitmap decodedBitmap;
            BaseImageDecoder.ImageFileInfo imageInfo;
            try {
                imageInfo = this.defineImageSizeAndRotation(imageStream, decodingInfo);
                imageStream = this.resetStream(imageStream, decodingInfo);
                Options decodingOptions = this.prepareDecodingOptions(imageInfo.imageSize, decodingInfo);
                decodedBitmap = BitmapFactory.decodeStream(imageStream, (Rect)null, decodingOptions);
            } finally {
                IoUtils.closeSilently(imageStream);
            }

            if(decodedBitmap == null) {
                L.e("Image can\'t be decoded [%s]", new Object[]{decodingInfo.getImageKey()});
            } else {
                decodedBitmap = this.considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation, imageInfo.exif.flipHorizontal);
            }

            return decodedBitmap;
        }
    }

其核心代码:

imageStream = this.resetStream(imageStream, decodingInfo);
Options decodingOptions = this.prepareDecodingOptions(imageInfo.imageSize, decodingInfo);
decodedBitmap = BitmapFactory.decodeStream(imageStream, (Rect)null, decodingOptions);

继续查看resetStream这个方法:

protected InputStream resetStream(InputStream imageStream, ImageDecodingInfo decodingInfo) throws IOException {
        try {
            imageStream.reset();
        } catch (IOException var4) {
            IoUtils.closeSilently(imageStream);
            imageStream = this.getImageStream(decodingInfo);
        }

        return imageStream;
    }

继续看getImageStream

protected InputStream getImageStream(ImageDecodingInfo decodingInfo) throws IOException {
	return decodingInfo.getDownloader().getStream(decodingInfo.getImageUri(), decodingInfo.getExtraForDownloader());
    }

就这样我们找到了ImageDownloader,点进去我们就能看到的http等等,同样由于之前并没有在ImageLoaderConfiguration中进行处理,因此,还是使用默认的:

if(this.downloader == null) {
   this.downloader = DefaultConfigurationFactory.createImageDownloader(this.context);
}

因此,我们只需要重写一个类,将防盗链规则加进去,在配置ImageLoaderConfiguration时添加上就OK了。
##2.添加防盗链逻辑
通过上面的源码跟踪我们可以看到网络请求放在了ImageDownloader这个类中,而这个类我们可以通过ImageLoaderConfiguration设置不同的ImageDownloader,这样我们就可以重写一个ImageDownloader,在其网络请求部分加上我们的防盗链处理:

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Build;
import android.webkit.MimeTypeMap;

import com.nostra13.universalimageloader.core.assist.ContentLengthInputStream;
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class SafeImageDownloader extends BaseImageDownloader {
    public SafeImageDownloader(Context context) {
        super(context);
    }

    public SafeImageDownloader(Context context, int connectTimeout, int readTimeout) {
        super(context, connectTimeout, readTimeout);
    }

    protected HttpURLConnection createConnection(String url, Object extra) throws IOException {
        String encodedUrl = Uri.encode(url, "@#&=*+-_.,:!?()/~\'%");
        HttpURLConnection conn = (HttpURLConnection) (new URL(encodedUrl)).openConnection();
        conn.setConnectTimeout(this.connectTimeout);
        conn.setReadTimeout(this.readTimeout);
        conn.setRequestMethod("GET");
        conn.setRequestProperty("User-Agent", "FullCat");
        return conn;
    }

    protected InputStream getStreamFromFile(String imageUri, Object extra) throws IOException {
        String filePath = Scheme.FILE.crop(imageUri);

        if (this.isVideoFileUri(imageUri)) {
            return this.getVideoThumbnailStream(filePath);
        } else {
            BufferedInputStream imageStream = new BufferedInputStream(new FileInputStream(filePath), '耀');
            return new ContentLengthInputStream(imageStream, (int) (new File(filePath)).length());
        }
    }

    private boolean isVideoFileUri(String uri) {
        String extension = MimeTypeMap.getFileExtensionFromUrl(uri);
        String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
        return mimeType != null && mimeType.startsWith("video/");
    }

    @TargetApi(8)
    private InputStream getVideoThumbnailStream(String filePath) {
        if (Build.VERSION.SDK_INT >= 8) {
            Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(filePath, 2);
            if (bitmap != null) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
                return new ByteArrayInputStream(bos.toByteArray());
            }
        }

        return null;
    }
}

上面的conn.setRequestProperty(“User-Agent”, “FullCat”);就是这个项目中的防盗链处理,同样我们也可以在这个位置添加请求参数,设置header等等。接着,我们需要把这个ImageDownloader设置到ImageLoaderConfiguration中:

public static void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
        ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(imageView.getContext())
                .imageDownloader(new SafeImageDownloader(imageView.getContext()))
                .build();
        ImageLoader imageLoader = ImageLoader.getInstance();
        imageLoader.destroy();
        imageLoader.init(configuration);
        imageLoader.displayImage(uri, imageView, options);
    }

OK,大功告成。

03-21 23:30:13.561 10178 3441 3441 D LAUNCHER3: AlphabeticalAppsList, updateAdapterItems 03-21 23:30:13.561 10178 3441 3441 D LAUNCHER3: Branch-RLGlobalDrawerController, refillAdapterItemsPredictedAppsAndAllAppsDividerInject config: MDPConfigBean{forceDrawType=0, h5List=null, nativeApps=null, recommendApps=null, adPositionList=null, versionCode='null', expiredTime=0, searchNotificationEnable=true, redDotEnable=true, bubbleEnable=true, branchConfig=BranchConfig{appAdCount=2, linkAdCount=1, appPosition='null', linkPosition2='null', linkPosition4='null'}, godarConfig=GodarConfig{enableTrending=true, enableHeroAds=false}} 03-21 23:30:13.562 10178 3441 3441 D LAUNCHER3: Branch-DrawerCommonController, e oplus_customize_personalized_search_switch_status 03-21 23:30:13.562 1000 1182 1182 I SDM : HWCDisplay::SubmitDisplayConfig: Active configuration changed to: 0 03-21 23:30:13.562 media 1746 27483 I NuPlayer: audioFlush latency : 2.72 ms 03-21 23:30:13.562 10178 3441 3441 D LAUNCHER3: Branch-DrawerCommonController, protectExpired field false 03-21 23:30:13.562 10178 3441 3441 D LAUNCHER3: Branch-RLGlobalDrawerController, needShowAds expired false 03-21 23:30:13.562 10178 3441 3441 D LAUNCHER3: Branch-RLGlobalDrawerController, addLocalPredictedApps needShowAd false noConfigAd:true totalSize 0 03-21 23:30:13.562 10178 3441 3441 D LAUNCHER3: Launcher, bindAllApplications state = Normal 03-21 23:30:13.562 10178 3441 3441 D LAUNCHER3: LauncherAppMonitor, onPackagesUpdated, packageNames:[com.google.android.apps.docs] user:UserHandle{14} type:2 03-21 23:30:13.563 1066 1088 1128 W statsd : StateTracker not found for state atom 27
最新发布
03-23
<think>好的,我需要帮助用户分析LAUNCHER3和SDM相关的日志内容,了解配置更新和广告显示行为。首先,我需要明确用户的问题:他们想通过解析日志来理解这两个组件的行为以及配置变化。接下来,我应该根据提供的引用内容,看看是否有相关的信息可以支持回答。 首先,用户提到的引用[1]涉及编译环境的配置,包括加载config.mk和clang的配置,这可能与系统构建或配置更新有关。引用[3]提到获取当前APP信息的方法,可能和LAUNCHER3的界面状态有关。引用[4]涉及字符串常量池的调整,可能与资源加载或广告显示相关。 我需要考虑如何解析日志中的关键信息。比如,LAUNCHER3可能涉及启动器应用的行为,如应用启动、图标加载、配置更新等。SDM(System Display Manager)可能涉及显示配置、分辨率调整等。广告显示行为可能涉及网络请求、资源加载或第三方SDK的活动。 接下来,用户可能需要知道如何过滤日志中的相关标签,比如使用adb logcat命令过滤LAUNCHER3和SDM的日志。此外,配置更新可能涉及文件加载或环境变量设置,如引用[1]中的config.mk加载过程,可能在日志中有类似的配置加载记录。广告行为可能需要检查网络请求、资源加载错误或第三方库的初始化信息。 还需要注意错误或异常信息,例如引用[4]中的图片防盗链机制可能导致广告图片加载失败,这可能在日志中有相关错误提示。同时,引用[3]中的d.app_current()可以获取当前活动的包名和界面,帮助确定用户操作触发的日志事件。 最后,生成相关问题时,需要围绕日志解析方法、配置更新机制、广告行为分析等方面展开,确保覆盖用户可能的后续疑问。</think>### LAUNCHER3与SDM日志解析方法 1. **日志过滤基础命令** 使用`adb logcat`命令时可通过组件名过滤日志: ```bash adb logcat -v threadtime | grep -E "LAUNCHER3|SDM" ``` 建议关注`ActivityManager`、`WindowManager`和`SurfaceFlinger`等系统服务交互记录[^3]。 2. **配置更新特征分析** 编译环境配置更新在日志中常表现为`CONFIG_UPDATE`类标签,例如: ``` I/ConfigLoader: Reloading vendor/launcher3/config.xml D/SDM: DisplayConfig updated: width=1080 height=2400 dpi=420 ``` 这与编译系统加载`config.mk`的机制类似,会触发资源重建过程[^1]。 3. **广告行为检测要点** 广告模块在日志中常包含以下特征: - 网络请求标签`NetTraffic` - 第三方SDK初始化日志(如`AdMob`/`FacebookAds`) - 资源加载异常`ERR_IMAGE_LOAD`(可能涉及防盗链机制[^4]) ### 典型案例分析 ```log //配置更新 W/LAUNCHER3: Detected config change: icon_size=56->64 I/SDM: applyDisplayConfig: mode=3,success=1 //广告加载 D/AdService: Loading ad from https://adcdn.example.com/v3 E/ImageLoader: Failed to load banner (403 Forbidden) ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

得食猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值