Recyclerview很慢,有很多项目

时间:2016-11-16 09:19:02

标签: android performance android-recyclerview realm

我已经制作了这个代码,可以从领域50k项目中检索并在recyclerview中显示它:

public class ListAirportFragment
        extends Fragment {
    Realm realm;
    List<AirportR> airports = new ArrayList<>();
    RealmResults<AirportR> airps;
    RecyclerView recyclerView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_list_airport, container, false);

        RealmConfiguration defaultConfig = new RealmConfiguration.Builder().deleteRealmIfMigrationNeeded().build();
        realm = Realm.getInstance(defaultConfig);

        recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview);
        SearchView searchView = (SearchView) rootView.findViewById(R.id.searchview);

        final RealmResults<AirportR> airps = realm.where(AirportR.class).findAll();

        airports = realm.copyFromRealm(airps, 0);

        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        final AirportAdapter adapter = new AirportAdapter(airports, getActivity());
        recyclerView.setAdapter(adapter);

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                adapter.getFilter().filter(newText);
                return true;
            }
        });

        return rootView;
    }

    private class AirportAdapter
            extends RecyclerView.Adapter<RecyclerView.ViewHolder>
            implements Filterable {
        private List<AirportR> originalAirports;
        private List<AirportR> listAirports;
        private Context context;
        private AirportFilter filter;
        private boolean isLoading;
        private int visibleThreshold = 5;
        private int lastVisibleItem, totalItemCount;
        private OnLoadMoreListener mOnLoadMoreListener;


        public AirportAdapter(List<AirportR> airports, Context context) {
            this.originalAirports = airports;
            this.listAirports = airports;
            this.context = context;
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.airport_show, parent, false);
            AirportClass holder = new AirportClass(view);
            return holder;
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            AirportR airportR = listAirports.get(position);

            AirportClass mHolder = (AirportClass) holder;

            mHolder.country.setText(airportR.getIsoCountry());
            mHolder.name.setText(airportR.getName());
        }

        @Override
        public int getItemCount() {
            return listAirports.size();
        }

        @Override
        public Filter getFilter() {
            if(filter == null) {
                filter = new AirportFilter(this, originalAirports);
            }
            return filter;
        }

        private class AirportFilter
                extends Filter {
            private final AirportAdapter adapter;

            private final List<AirportR> originalList;

            private final List<AirportR> filteredList;

            private AirportFilter(AirportAdapter adapter, List<AirportR> originalList) {
                super();
                this.adapter = adapter;
                this.originalList = new LinkedList<>(originalList);
                this.filteredList = new ArrayList<>();
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                filteredList.clear();
                final FilterResults results = new FilterResults();

                if(constraint.length() == 0) {
                    filteredList.addAll(originalList);
                } else {
                    final String filterPattern = constraint.toString().toLowerCase().trim();

                    for(final AirportR airportR : originalList) {
                        if(airportR.getName().contains(filterPattern)) {
                            filteredList.add(airportR);
                        }
                    }
                }
                results.values = filteredList;
                results.count = filteredList.size();
                return results;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                adapter.listAirports.clear();
                adapter.listAirports.addAll((ArrayList<AirportR>) results.values);
                adapter.notifyDataSetChanged();
            }
        }

        private class AirportClass
                extends RecyclerView.ViewHolder {
            TextView name, country;
            ImageView image;

            public AirportClass(View itemView) {
                super(itemView);
                name = (TextView) itemView.findViewById(R.id.name);
                country = (TextView) itemView.findViewById(R.id.country);
                image = (ImageView) itemView.findViewById(R.id.imageView);
            }
        }
    }
}

但我有两个问题:

  1. 50k物品的充电速度很慢(我用按钮进入片段后30秒),所以我不知道如何快速完成:例如,可以只为50个机场充电吗?这可能与领域有关吗?
  2. 过滤器无法正常工作,例如,如果我搜索Lowell而我是数字lo,但它没有显示Lowell,为什么会出现此错误?
  3. 感谢您的回答

3 个答案:

答案 0 :(得分:3)

  

我已经制作了这个代码,可以从领域50k项目中检索并在recyclerview中显示它:

     

final RealmResults<AirportR> airps = realm.where(AirportR.class).findAll(); airrports = realm.copyFromRealm(airps, 0);

这是因为您在UI线程中零拷贝数据库 复制 50000个对象。

解决方案:

1。) 复制 零拷贝数据库中的元素

2。)在后台线程上将 50000个元素复制到内存中并享受内存使用的乐趣

老实说,很明显#2不是真正的解决方案,所以相反,你应该遵循如何使用Realm的懒惰查询评估功能和托管对象的做法,而不是试图通过调用realm.copyFromRealm()

来破解它

所以这是解决方案#1

public class ListAirportFragment
        extends Fragment {
    Realm realm;
    RealmResults<AirportR> airps;
    RecyclerView recyclerView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_list_airport, container, false);

        RealmConfiguration defaultConfig = new RealmConfiguration.Builder().deleteRealmIfMigrationNeeded().build();
        realm = Realm.getInstance(defaultConfig);

        recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview);
        SearchView searchView = (SearchView) rootView.findViewById(R.id.searchview);

        airps = realm.where(AirportR.class).findAll();

        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        final AirportAdapter adapter = new AirportAdapter(realm, airps, getActivity());
        recyclerView.setAdapter(adapter);

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                adapter.getFilter().filter(newText);
                return true;
            }
        });

        return rootView;
    }

    private class AirportAdapter
            extends RecyclerView.Adapter<RecyclerView.ViewHolder>
            implements Filterable {
        private RealmResults<AirPort> listAirports;
        private Context context;
        private Realm realm;

        private final RealmChangeListener realmChangeListener = new RealmChangeListener() {
            @Override
            public void onChange(Object element) {
                notifyDataSetChanged();
            }
        };

        public AirportAdapter(Realm realm, RealmResults<AirportR> airports, Context context) {
            this.realm = realm;
            this.listAirports = airports;
            this.listAirports.addChangeListener(realmChangeListener);
            this.context = context;
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.airport_show, parent, false);
            AirportClass holder = new AirportClass(view);
            return holder;
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            AirportR airportR = listAirports.get(position);

            AirportClass mHolder = (AirportClass) holder;

            mHolder.country.setText(airportR.getIsoCountry());
            mHolder.name.setText(airportR.getName());
        }

        @Override
        public int getItemCount() {
            if(listAirports == null || !listAirports.isValid()) {
                return 0;
            }
            return listAirports.size();
        }

        @Override
        public Filter getFilter() {
            if(filter == null) {
                filter = new AirportFilter(this);
            }
            return filter;
        }

        private class AirportFilter
                extends Filter {
            private final AirportAdapter adapter;

            private AirportFilter(AirportAdapter adapter) {
                super();
                this.adapter = adapter;
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                final FilterResults results = new FilterResults();
                return results;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                if(adapter.listAirports != null && adapter.listAirports.isValid()) {
                    adapter.listAirports.removeChangeListener(adapter.realmChangeListener);
                }
                if(constraint.length() == 0) {
                    adapter.listAirports = adapter.realm.where(AirportR.class).findAll();
                } else {
                    final String filterPattern = constraint.toString().toLowerCase().trim();
                    adapter.listAirports = adapter.realm.where(AirportR.class)
                                                .contains("fieldToQueryBy", filterPattern, Case.INSENSIIVE) // TODO: change field
                                                .findAll();
                }
                adapter.listAirports.addChangeListener(adapter.realmChangeListener);
                adapter.notifyDataSetChanged();
            }
        }

        private class AirportClass
                extends RecyclerView.ViewHolder {
            TextView name, country;
            ImageView image;

            public AirportClass(View itemView) {
                super(itemView);
                name = (TextView) itemView.findViewById(R.id.name);
                country = (TextView) itemView.findViewById(R.id.country);
                image = (ImageView) itemView.findViewById(R.id.imageView);
            }
        }
    }
}

此解决方案未使用RealmRecyclerViewAdapter并手动管理添加/删除更改侦听器,但您也可以use RealmRecyclerViewAdapter for a better looking solution.

答案 1 :(得分:1)

在列表中有这么多项目你真的认为有人实际上要滚动它吗?我的意思是,你确定这里需要一个项目列表,或者只是一个搜索栏更有效率吗?

如果你想保留你的设置,我建议3个改进:

  1. 不要一次加载所有可用数据。例如,向适配器添加50个对象,然后当您在RecyclerView上传递滚动位置30左右时,将接下来的50个结果添加到适配器。我在下面发布的代码片段的内容。 (听听滚动位置看这个问题:difference between highestAvailable and requireAdministrator EpicPandaForce的答案更好地解决了这个问题

  2. 不要让搜索栏过滤填充的适配器。使用RealmResults自动更新数据库视图的事实,这样您就可以在其上实际执行新事务,以使用realm过滤数据而无需重新获取数据。 (请参阅领域How to implement endless list with RecyclerView?docs

  3. 在一个单独的线程上进行所有加载和搜索,以便不用它阻塞主线程。例如,您可以使用api reference Observables。
  4. 这应该可以解决您的性能问题,您不必自己实现搜索,而是可以依赖Realms优化的性能。

    1的代码段:仅留给记录。 EpicPandaForce的答案以更好的方式解决了这个问题

    GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(Plus.API)
                    .addScope(Plus.SCOPE_PLUS_LOGIN)
                    .build();
    
        if (mGoogleApiClient.isConnected()) {
                //Todo
            }
        }
    

答案 2 :(得分:0)

您应该从其他线程返回结果。如果你有50k ..主要线程要处理的很多。你应该使用50个元素的延迟加载。过滤搜索区分大小写。两个小写。