V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jeesk
V2EX  ›  Android

android 相册开发问题

  •  
  •   jeesk · 2022-08-21 10:43:51 +08:00 · 9272 次点击
    这是一个创建于 858 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近小弟实现了, 一款相册, 但是在实现流水线的时候,recycleview 嵌套 recycleview 加载图片的时候,内存迟迟没法销毁。 导致占用内存到 2g. 下面贴一贴我的代码

    
    public class PhotoAdapter extends RecyclerView.Adapter<PhotoAdapter.ViewHolder> {
    
        private Context context;
        private List<PhotoGroup> photoGroupList;
    
        // 构造方法(将图片数据传入)
        public PhotoAdapter(List<PhotoGroup> photoGroupList) {
            this.photoGroupList = photoGroupList;
        }
    
    
        @Override
        public int getItemViewType(int position) {
            if (position == 0) {
                return 0;
            }
            return -1;
        }
    
        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            if (context == null) {
                context = parent.getContext();
            }
            // 返回外层 分组的 item
            View view = LayoutInflater.from(context).inflate(R.layout.image_group_item, parent, false);
            final ViewHolder holder = new ViewHolder(view);
            return holder;
        }
    
        @Override
        public void onViewRecycled(@NonNull ViewHolder holder) {
            super.onViewRecycled(holder);
            Glide.with(holder.itemView).clear(holder.itemView);
            Glide.with(holder.itemView).clear(holder.cardView);
            Glide.with(holder.itemView).clear(holder.recyclerView);
        }
    
        //将数据绑定到控件上
        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            PhotoGroup PhotoGroup = photoGroupList.get(position);
    
            ChildAdapter childAdapter = (ChildAdapter) holder.recyclerView.getAdapter();
            if (childAdapter == null) {
                // 设定 image_group_item 布局(每列显示 4 个子布局 image_child_item )
                GridLayoutManager manager = new GridLayoutManager(context, 4);
                holder.recyclerView.setItemViewCacheSize(2000);
                holder.recyclerView.setHasFixedSize(true);
                holder.recyclerView.setNestedScrollingEnabled(false);
                holder.recyclerView.setLayoutManager(manager);
                // 设置子布局的适配器  [ PhotoGroup.getPhotoArrayList() 是一个分组中的图片数据] 
                holder.recyclerView.setAdapter(new ChildAdapter(PhotoGroup.getPhotoArrayList()));
            } else {
                // 重用子布局的适配,如果已经初始化过,直接调用 notifyDataSetChanged() 刷新布局就行
                childAdapter.setData(PhotoGroup.getPhotoArrayList());
    //            childAdapter.notifyDataSetChanged();
            }
    
        }
    
        @Override
        public int getItemCount() {
            return photoGroupList.size();
        }
    
        // ViewHolder 对应 image_group_item 文件,里面元素只有最外层 CardView 和一个嵌套 RecyclerView
        static class ViewHolder extends RecyclerView.ViewHolder {
            RelativeLayout cardView;
            RecyclerView recyclerView;
    
            public ViewHolder(View view) {
                super(view);
                cardView = (RelativeLayout) view;
                recyclerView = (RecyclerView) view.findViewById(R.id.tupian_item_recyclerView);
                recyclerView.addOnScrollListener(new ImageAutoLoadScrollListener(view.getContext()));
            }
        }
    
        /*****************************ChildAdapter ***************************************/
        // 嵌套子适配(内部类)
        public class ChildAdapter extends RecyclerView.Adapter<ChildViewHolder> {
    
            // 每一个组的图片数据
            private ArrayList<Photo> photoList;
    
            //构造函数
            public ChildAdapter(ArrayList<Photo> photoList) {
                this.photoList = photoList;
            }
    
            //用于更新图片数据
            public void setData(ArrayList<Photo> photoList) {
                this.photoList = photoList;
            }
    
            @NonNull
            @Override
            public ChildViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                if (context == null) {
                    context = parent.getContext();
                }
                View view = LayoutInflater.from(context).inflate(R.layout.image_child_item, parent, false);
                final ChildViewHolder holder = new ChildViewHolder(view);
                return holder;
    
            }
    
            @Override
            public void onViewRecycled(@NonNull ChildViewHolder holder) {
                super.onViewRecycled(holder);
                Glide.with(holder.itemView.getContext()).clear(holder.imageView);
            }
    
            @Override
            public void onBindViewHolder(@NonNull ChildViewHolder holder, int position) {
                Photo image = photoList.get(position);
    
                Uri uri = ContentUris.withAppendedId(
                        UriUtil.INSTANCE.getMediaType(image.getMediaType()),
                        image.getId()
                );
                Glide.with(holder.imageView.getContext())
                        .asDrawable()
                        .load(uri)
                        .thumbnail(0.1f)
                        .override(SizeUtils.dp2px(100), SizeUtils.dp2px(100))
                        .placeholder(R.drawable.folder_ripple)
                        .format(DecodeFormat.PREFER_RGB_565)
                        .skipMemoryCache(true)
                        .into(holder.imageView);
            }
    
            @Override
            public int getItemCount() {
                return photoList.size();
            }
    
        }
    }
    
    

    下面是我流水线布局的代码

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/main_recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/image_child_item_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="2dp"
        app:cardCornerRadius="4dp">
    
        <com.example.pic.picmanager.MyRoundImageView
            android:id="@+id/image_item_imageView"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:adjustViewBounds="true"
            android:scaleType="centerCrop"
            app:round="4dp" />
    
    </RelativeLayout>
    
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/image_group_item_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:cardCornerRadius="4dp"
        app:cardElevation="0dp">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/tupian_item_recyclerView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
    </RelativeLayout>
    
    8 条回复    2022-08-21 16:54:22 +08:00
    jeesk
        1
    jeesk  
    OP
       2022-08-21 10:46:17 +08:00
    这种嵌套布局导致 glide 加载图片无法释放内存。 难受中
    hahabird
        2
    hahabird  
       2022-08-21 10:54:53 +08:00
    holder.recyclerView.setItemViewCacheSize(2000);这句是设置缓存数量的吗?
    用 profile 看下内存占用?
    jeesk
        3
    jeesk  
    OP
       2022-08-21 11:05:57 +08:00
    @hahabird 我有 46G 照片。 这个嵌套实现的时间线已经到达 2g 内存占用了 。
    janus77
        4
    janus77  
       2022-08-21 12:39:46 +08:00
    嵌套本身就是复用会差点的……
    我看你对 child adapter 判断了为空,你可以打印日志,然后多滚动一下列表看看是否命中了不为空的条件。如果大多数都是走的为空,那说明你的 child adapter 一直是在 new 的,这样会影响内存
    你可以用对象池,把 child adapter 也做缓存,另外在 child adapter 里面的 glide 内存回收管理这块代码注意下
    hyyou2010
        5
    hyyou2010  
       2022-08-21 12:47:42 +08:00
    从另一个角度建议
    1 、从产品角度做简化,不要复杂的嵌套
    2 、试试用 compose 写界面,方便太多了,没有 adapter 这些
    jeesk
        6
    jeesk  
    OP
       2022-08-21 13:37:18 +08:00
    @hyyou2010 关键是别人都是这是这种界面呀。 虽然是搞着玩玩。 但是也要认真呀。
    devfeng
        7
    devfeng  
       2022-08-21 16:50:36 +08:00
    没懂,你这个场景不是直接用一个 RecyclerView+GridLayoutManager 就可以了吗,设置好 SpanSizeLookup
    seelight
        8
    seelight  
       2022-08-21 16:54:22 +08:00
    可以试一下 Compose, 无限嵌套, 没有性能影响, 还不用写 adapter.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5518 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 03:37 · PVG 11:37 · LAX 19:37 · JFK 22:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.