Каков наилучший способ обновить Realm из ответа сокета, чтобы остановить медленный пользовательский интерфейс? - PullRequest
0 голосов
/ 03 июля 2019

Я разрабатываю приложение, которое широко использует Socket.io. Приложение хранит билеты в Realm, которые могут быть погашены или не погашены, эти билеты сохраняются в соответствии с заказами, сокет сообщает приложению, что ему нужно обновить билет и, следовательно, заказ, а затем обновить набор данных в RecyclerView, чтобы соответствовать новому правильному данные. Я также использую ExpandableRecyclerView, у которого есть родительский тип (порядок) и дочерний тип данных (билеты)

Проблема возникает, когда возникает много сообщений, в которых приложение сообщает, что нужно обновить заявки в реальном времени, а затем обновить пользовательский интерфейс. Это становится очень медленным, потому что оно пытается сделать так много с Realm и с Обновлением пользовательского интерфейса.

В настоящее время я пытался использовать AysncTask или несколько комбинаций Threads и Runnables, которые отлично справляются с небольшим количеством обновлений, именно тогда начинается большое количество обновлений, когда возникают медленные медленные проблемы.

Я пытался обновить только определенные строки в RecyclerView, используя HashMap с индексом String для заявки orderId и его позиции в RecyclerView, я получаю позицию через orderId и обновляю только эту строку.

Я кратко изучил Java ExecutorService, но, похоже, это тоже не помогло.

Я подозреваю, что проблема в том, что я делаю что-то не так с потоками и создаю слишком много вместо того, чтобы помещать их в очередь.

Это то, что слушает сокет, здесь билет сохраняется в Realm, а затем обновляется пользовательский интерфейс.

        socket.on("tickets", new Emitter.Listener() {
            @Override
            public void call(final Object... objects) {
                Thread thread = new Thread(){
                    @Override
                    public void run() {
                        super.run();
                        Realm realm = Realm.getDefaultInstance();
                        try {
                            Log.d("socket res", objects[0].toString());

                            JSONArray resultsArray = (JSONArray) objects[0];

                            if(!realm.isInTransaction()){
                                realm.beginTransaction();
                            }

                            List<TicketsRealmObject> ticketsRealmObjects = new ArrayList<>();
                            List<OrdersRealmObject> ordersRealmObjects = new ArrayList<>();

                            for(int i = 0; i < resultsArray.length(); i++){
                                JSONObject jsonObject = resultsArray.getJSONObject(i);
                                TicketsRealmObject ticketsRealmObject =
                                        new TicketsRealmObject(jsonObject,
                                                jsonObject.getString("oid"), eventId);
                                ticketsRealmObjects.add(ticketsRealmObject);
                                heldBarcodesToUpdate.put(jsonObject.getString("oid"));

                                OrdersRealmObject ordersRealmObject =
                                        realm.where(OrdersRealmObject.class).
                                                equalTo("eventId", eventId).
                                                equalTo("orderId",
                                                        ticketsRealmObject.getOrderId()).findFirst();
                                if(ordersRealmObject != null){
                                    RealmList<TicketsRealmObject> realmList = new RealmList<>();
                                    realmList.add(ticketsRealmObject);
                                    ordersRealmObject.updateRealmList(realmList);
                                    ordersRealmObjects.add(ordersRealmObject);
                                }

                            }

                            if(!holdSocketsTraffic){
                                realm.copyToRealmOrUpdate(ticketsRealmObjects);
                                realm.copyToRealmOrUpdate(ordersRealmObjects);
                                realm.commitTransaction();
                                realm.close();
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        if(tickets_view_pager.getCurrentItem() == 0){
                                            updateOrdersFragmentList(heldBarcodesToUpdate);
                                        } else if(tickets_view_pager.getCurrentItem() == 1){
                                            updateStatsFragment();
                                        }
                                    }
                                });
                            } else {
                                heldTicketsList.addAll(ticketsRealmObjects);
                                if(realm.isInTransaction()){
                                    realm.cancelTransaction();
                                }
                                realm.close();
                            }

                        } catch (Exception e){
                            e.printStackTrace();
                            realm.close();
                        }
                    }
                };
                Handler handler = new Handler(Looper.getMainLooper());
                handler.post(thread);

            }
        });

Это то, что вызывается для обновления Recyclerview после сохранения обновленного билета в Realm.

    private void updateOrdersFragmentList(JSONArray orderIds){
        OrdersFragment ordersFragment = (OrdersFragment) adapter.getItem(0);
        ordersFragment.notifyDataSetChanged(orderIds);
        heldBarcodesToUpdate = new JSONArray();
    }
    public void notifyDataSetChanged(JSONArray orderIds){
        if(re_orders.getAdapter() != null){
            for(int i = 0; i < orderIds.length(); i++){
                try {
                    adapter.notifyParentChanged(adapter.getItemPosition(orderIds.getString(i)));
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

Вот расширяемый RecyclerView с его родительскими заказами и дочерними билетами.

public class ExpandableOrdersAdapter extends ExpandableRecyclerAdapter<OrdersRealmObject,
        TicketsRealmObject, ExpandableOrdersAdapter.ParentViewHolder,
        ExpandableOrdersAdapter.ChildViewHolder> {

    private Context context;
    private List<OrdersRealmObject> parentList;
    private HashMap<String, Integer> mapOfPositions = new HashMap<>();
    private ExpandableOrdersAdapterInterface listener;

    ExpandableOrdersAdapter(@NonNull List<OrdersRealmObject> parentList,
                            Context context, ExpandableOrdersAdapterInterface listener) {
        super(parentList);
        this.parentList = parentList;
        this.context = context;
        this.listener = listener;
    }

    @NonNull
    @Override
    public ParentViewHolder onCreateParentViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(context).inflate(R.layout.orders_item, viewGroup,
                false);
        return new ParentViewHolder(view);
    }

    @NonNull
    @Override
    public ChildViewHolder onCreateChildViewHolder(
            @NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(context).inflate(R.layout.ticket_item, viewGroup,
                false);
        return new ChildViewHolder(view);
    }

    @Override
    public void onBindParentViewHolder(@NonNull ParentViewHolder parentViewHolder,
                                       int parentPosition,
                                       @NonNull OrdersRealmObject ordersRealmObject) {
        mapOfPositions.put(parentList.get(parentPosition).getOrderId(), parentPosition);
        parentViewHolder.txt_name.setText(String.format("%s, %s",
                parentList.get(parentPosition).getSurname(),
                parentList.get(parentPosition).getFirstName()));
        parentViewHolder.txt_card_details.setText(String.format("%s %s",
                parentList.get(parentPosition).getCardType(),
                parentList.get(parentPosition).getCardDigits()));
        parentViewHolder.txt_postcode.setText(parentList.get(parentPosition).getPostCode());
    }

    @Override
    public void onBindChildViewHolder(
            @NonNull final ChildViewHolder childViewHolder,
            final int parentPosition, final int childPosition,
            @NonNull TicketsRealmObject ticketsRealmObject){

        final List<TicketsRealmObject> ticketList = parentList.get(parentPosition).getChildList();

        childViewHolder.ll_order_info.setVisibility(View.GONE);

        childViewHolder.txt_ticket_name.setText(ticketList.get(childPosition).getTicket());
        childViewHolder.txt_ticket_id.setText(ticketList.get(childPosition).getBarcode());

        if(ticketList.get(childPosition).getDate().equals("")){
            childViewHolder.btn_redeem.setVisibility(View.VISIBLE);
            childViewHolder.btn_un_redeem.setVisibility(View.GONE);
        } else {
            childViewHolder.btn_un_redeem.setVisibility(View.VISIBLE);
            childViewHolder.btn_redeem.setVisibility(View.GONE);
        }

        if(childViewHolder.getChildAdapterPosition() == ticketList.size() - 1){
            childViewHolder.ll_order_info.setVisibility(View.VISIBLE);
        }

        if(childPosition + 1 == ticketList.size()){
            childViewHolder.ll_order_info.setVisibility(View.VISIBLE);
        }

        childViewHolder.img_entered_tick.setVisibility(!ticketList.get(childPosition).
                getDate().equals("") ? View.VISIBLE : View.GONE);

        childViewHolder.btn_redeem.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                System.gc();
                childViewHolder.btn_un_redeem.setVisibility(View.VISIBLE);
                childViewHolder.btn_redeem.setVisibility(View.GONE);
                childViewHolder.img_entered_tick.setVisibility(View.VISIBLE);
                updateTicketPendingStatus(ticketList.get(
                        childViewHolder.getChildAdapterPosition()), "redeem");
                notifyParentChanged(parentPosition);
                listener.redeemOrUnredeemTicket(ticketList.get(childPosition), "redeem");
            }
        });

        childViewHolder.btn_un_redeem.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                System.gc();
                childViewHolder.btn_un_redeem.setVisibility(View.GONE);
                childViewHolder.btn_redeem.setVisibility(View.VISIBLE);
                childViewHolder.img_entered_tick.setVisibility(View.GONE);
                updateTicketPendingStatus(ticketList.get(
                        childViewHolder.getChildAdapterPosition()), "unredeem");
                notifyParentChanged(parentPosition);
                listener.redeemOrUnredeemTicket(ticketList.get(
                        childPosition), "unredeem");
            }
        });

        childViewHolder.btn_order_info.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                listener.viewOrderInfo(parentList.get(parentPosition).getOrderId());
            }
        });

    }

    private void updateTicketPendingStatus(TicketsRealmObject ticketsRealmObject, String event) {
        Realm realm = Realm.getDefaultInstance();
        realm.beginTransaction();
        ticketsRealmObject.setPending(true);

        switch (event){
            case "redeem":
                ticketsRealmObject.setDate(listener.getISO8601DateForEmit());
                ticketsRealmObject.setDateUnredeemed("");
                break;
            case "unredeem":
                ticketsRealmObject.setDate("");
                ticketsRealmObject.setDateUnredeemed(listener.getISO8601DateForEmit());
                break;
        }

        realm.copyToRealmOrUpdate(ticketsRealmObject);
        realm.commitTransaction();
        realm.close();
    }

    int getItemPosition(String orderId){
        return mapOfPositions.get(orderId);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    class ParentViewHolder extends com.bignerdranch.expandablerecyclerview.ParentViewHolder {

        private TextView txt_name, txt_card_details, txt_postcode;
        private ImageView img_expanded_icon;

        ParentViewHolder(@NonNull View view) {
            super(view);
            txt_name = view.findViewById(R.id.txt_name);
            txt_card_details = view.findViewById(R.id.txt_card_details);
            txt_postcode = itemView.findViewById(R.id.txt_postcode);
            img_expanded_icon = itemView.findViewById(R.id.img_expanded_icon);

            img_expanded_icon.setRotation(180);
        }

        @Override
        public void onExpansionToggled(boolean expanded) {
            if(!expanded){
                img_expanded_icon.setRotation(360);
            } else {
                img_expanded_icon.setRotation(180);
            }
        }
    }

    class ChildViewHolder extends com.bignerdranch.expandablerecyclerview.ChildViewHolder {

        private TextView txt_ticket_name, txt_ticket_id;
        private Button btn_redeem, btn_un_redeem, btn_order_info;
        private ImageView img_entered_tick;
        private LinearLayout ll_order_info;

        ChildViewHolder(@NonNull View itemView) {
            super(itemView);
            txt_ticket_name = itemView.findViewById(R.id.txt_ticket_name);
            txt_ticket_id = itemView.findViewById(R.id.txt_ticket_id);
            btn_redeem = itemView.findViewById(R.id.btn_redeem);
            btn_un_redeem = itemView.findViewById(R.id.btn_un_redeem);
            btn_order_info = itemView.findViewById(R.id.btn_order_info);
            img_entered_tick = itemView.findViewById(R.id.img_entered_tick);
            ll_order_info = itemView.findViewById(R.id.ll_order_info);
        }
    }

}


Я ожидал бы, что использование потока остановит приложение от запаздывания, поскольку я беру все, что могу, из потока пользовательского интерфейса, пока я наконец не обновлю RecyclerView с последним набором данных, я также ожидал бы, что RecyclerView будет обновляться быстро Я обновляю только одну строку за раз.

Но это все еще отстает, даже если я использую поток, работающий или AysncTask.

...