Я разрабатываю приложение, которое широко использует 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.