使用Picasso批量缩放加载图片时性能卡顿的优化方案咨询
优化方案
一、核心问题分析
原代码性能差的原因主要有三点:
- 每次加载图片都新建
OkHttpClient和Picasso实例,每个实例都包含独立的线程池、缓存系统,重复创建会快速耗尽内存和CPU资源。 - 用
LinearLayout配合NestedScrollView加载大量图片时,所有ImageView会被一次性创建并加载,没有视图复用机制,内存占用急剧上升,导致卡顿。 - 直接调用
frameHolder.getWidth()可能在布局未完成时返回0,导致图片resize逻辑失效,加载原图进一步加重性能负担。
二、分步优化
1. 替换布局为RecyclerView
把NestedScrollView里的LinearLayout换成RecyclerView,利用其视图复用机制,只创建屏幕可见范围内的ImageView,大幅降低内存占用。
布局修改示例:
<!-- 替换原来的NestedScrollView+LinearLayout --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/image_recycler" android:layout_width="match_parent" android:layout_height="match_parent"/>
编写RecyclerView的Adapter:
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImageViewHolder> { private Context mContext; private List<ImageContent> mImages; private int mTargetWidth; private Picasso mPicasso; public ImageAdapter(Context context, List<ImageContent> images, int targetWidth, Picasso picasso) { mContext = context; mImages = images; mTargetWidth = targetWidth; mPicasso = picasso; } @NonNull @Override public ImageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { ImageView imageView = new ImageView(mContext); imageView.setLayoutParams(new RecyclerView.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT )); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); return new ImageViewHolder(imageView); } @Override public void onBindViewHolder(@NonNull ImageViewHolder holder, int position) { ImageContent img = mImages.get(position); // 动态添加请求头,复用全局Picasso实例 mPicasso.load(img.getUrlContentData()) .resize(mTargetWidth, 0) .onlyScaleDown() // 仅缩小图片,避免放大浪费资源 .transform(new RequestTransformer() { @Override public Request transformRequest(Request request) { return request.newBuilder() .addHeader(img.getHeaderKey(), img.getHeaderValue()) .build(); } }) .into(holder.imageView); } @Override public int getItemCount() { return mImages.size(); } static class ImageViewHolder extends RecyclerView.ViewHolder { ImageView imageView; public ImageViewHolder(@NonNull ImageView itemView) { super(itemView); imageView = itemView; } } }
2. 全局复用OkHttpClient和Picasso实例
只创建一次OkHttpClient和Picasso,避免重复初始化线程池、缓存等资源:
// 在Activity的onCreate方法中初始化全局实例 OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); Picasso picasso = new Picasso.Builder(this) .downloader(new OkHttp3Downloader(okHttpClient)) .build();
3. 正确获取目标宽度
确保获取到有效的适配宽度,避免resize失效:
// 获取屏幕宽度(适配全屏场景) DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); int targetWidth = displayMetrics.widthPixels; // 若需匹配父布局宽度,可在布局完成后获取 frameHolder.post(() -> { int parentWidth = frameHolder.getWidth(); // 初始化Adapter时传入parentWidth });
4. 可选:改用Glide加载图片
Glide在内存缓存、自动内存管理上表现更优,适合大量图片场景,实现示例:
// 在Adapter的onBindViewHolder中替换为Glide加载 Glide.with(mContext) .load(img.getUrlContentData()) .addHeader(img.getHeaderKey(), img.getHeaderValue()) .override(mTargetWidth, Target.SIZE_ORIGINAL) .fitCenter() .into(holder.imageView);
三、最终整合代码示例
public class YourActivity extends AppCompatActivity { private RecyclerView mImageRecycler; private Picasso mPicasso; private int mTargetWidth; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.your_activity_layout); // 获取适配宽度 DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); mTargetWidth = displayMetrics.widthPixels; // 初始化全局图片加载实例 OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); mPicasso = new Picasso.Builder(this) .downloader(new OkHttp3Downloader(okHttpClient)) .build(); // 初始化RecyclerView mImageRecycler = findViewById(R.id.image_recycler); mImageRecycler.setLayoutManager(new LinearLayoutManager(this)); } public void displayImages(List<ImageContent> images) { ImageAdapter adapter = new ImageAdapter(this, images, mTargetWidth, mPicasso); mImageRecycler.setAdapter(adapter); } }
内容的提问来源于stack exchange,提问作者Xiidref
相关产品推荐
相关产品推荐

