Как заполнить огромный TableLayout, не вызывая тайм-аут ARN? - PullRequest
0 голосов
/ 08 мая 2020

У меня есть TableLayout с несколькими сотнями строк таблиц.

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

Истекло время диспетчеризации ввода (ожидание отправки ключевого события, потому что сфокусированное окно не завершило обработку всех входных событий, которые были ранее доставлен на него. Длина исходящей очереди: 0. Длина очереди ожидания: 1.)

ОС Android обрабатывает создание ANR, когда обнаруживает, что поток пользовательского интерфейса / основной поток блокируется в течение 5 секунд .

Я думал об использовании AsyncTask, но он будет устаревшим в Android 11.

Это мой поток:

private class PopulateTableThread extends Thread {
    private Context context;
    private ArrayList<Player> players;

    public PopulateTableThread(Context context, ArrayList<Player> players) {
        super();
        this.context = context;
        this.players = players;
    }

    public void run() {
        for (int i=0; i<players.size(); i++) {
            TableRow row = new TableRow(context);
            row.setLayoutParams(rowParams);
            row.setBackgroundResource(R.drawable.table_row_not_selected);

            String strings[] = {players.get(i).getName(),
                    "" + players.get(i).getTeam().getName(),
                    "" + players.get(i).getPositionInString(context),
                    "" + players.get(i).getYearsOfContractFormatted(),
                    "" + players.get(i).getValue() + " M€",
                    "" + players.get(i).getSalary() + " M€",
                    "" + players.get(i).getAge(),
                    "" + players.get(i).getBaseLevel()};

            for (int j = 0; j < strings.length; j++) {
                TextView text = new TextView(context);
                text.setBackgroundResource(R.drawable.table_cell_background);

                if (j == 0) {
                    text.setLayoutParams(nameParams);
                    text.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
                    text.setPadding((int) getResources().getDimension(R.dimen.spacing_small), 0,  (int) getResources().getDimension(R.dimen.spacing_small), 0);
                    text.setMaxLines(1);
                    text.setEllipsize(TextUtils.TruncateAt.END);
                }else if (j == 1) {
                    text.setLayoutParams(teamNameParams);
                    text.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
                    text.setPadding((int) getResources().getDimension(R.dimen.spacing_small), 0,  (int) getResources().getDimension(R.dimen.spacing_small), 0);
                    text.setMaxLines(1);
                    text.setEllipsize(TextUtils.TruncateAt.END);
                } else if (j == 2 || j == 3) {
                    text.setLayoutParams(positionAndContractParams);
                    text.setGravity(Gravity.CENTER);
                } else if (j == 4) {
                    text.setLayoutParams(valueParams);
                    text.setGravity(Gravity.CENTER);
                } else if (j == 5) {
                    text.setLayoutParams(salaryParams);
                    text.setGravity(Gravity.CENTER);
                } else if (j == 6 || j == 7) {
                    text.setLayoutParams(ageAndLevelParams);
                    text.setGravity(Gravity.CENTER);
                }

                if (j == 3 && strings[3].split("/")[0].equals("1")) {
                    text.setBackgroundResource(R.drawable.table_cell_background_danger);
                }

                text.setMinHeight((int) getResources().getDimension(R.dimen.cell_height));
                text.setText(strings[j]);
                row.addView(text);
            }

            ProgressBar levelProgress = new ProgressBar(new ContextThemeWrapper(context, R.style.ProgressBar), null, 0);
            levelProgress.setBackgroundResource(R.drawable.table_cell_background);
            levelProgress.setLayoutParams(levelBarParams);
            levelProgress.setMinimumHeight((int) getResources().getDimension(R.dimen.cell_height));
            levelProgress.setProgress(players.get(i).getBaseLevel());
            levelProgress.setPadding((int) getResources().getDimension(R.dimen.spacing_small), 0, (int) getResources().getDimension(R.dimen.spacing_small), 0);
            row.addView(levelProgress);

            final int finalIndex = i;
            row.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (searchProgressBar.getVisibility() == View.VISIBLE) {
                        return;
                    }
                    SoundEffectsManager.getInstance().play(context, SoundEffectsManager.button);
                    displaySignDialog(players.get(finalIndex));
                }
            });

            Message message = new Message();
            message.what = HANDLER_ROW_CREATED;
            message.obj = row;
            updateTableHandler.sendMessage(message);
        }
        Message message = new Message();
        message.what = HANDLER_TABLE_FILLED;
        updateTableHandler.sendMessage(message);
    }
}

И это обработчик, который обновление потока пользовательского интерфейса, когда поток создал новую строку TableRow:

    updateTableHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case HANDLER_ROW_CREATED:
                    TableRow row = (TableRow) msg.obj;
                    tableLayout.addView(row);
                    break;
                case HANDLER_TABLE_FILLED:
                    tableLayout.setAlpha(1f);
                    searchProgressBar.setVisibility(View.GONE);
                    break;
            }
        }
    };

Как я могу избежать этих отчетов ARN?

Попытка выполнить решение Дэвида Вассера, но с той же проблемой:

private class PopulateTableThread extends Thread {
    private Context context;
    private ArrayList<Player> players;

    public PopulateTableThread(Context context, ArrayList<Player> players) {
        super();
        this.context = context;
        this.players = players;
    }

    public void run() {
        Log.d("XXX_SeekerActivity","filling table with "+players.size()+" players");
        for (int i=0; i<players.size(); i++) {
            final String strings[] = {players.get(i).getName(),
                    "" + players.get(i).getTeam().getName(),
                    "" + players.get(i).getPositionInString(context),
                    "" + players.get(i).getYearsOfContractFormatted(),
                    "" + players.get(i).getValue() + " M€",
                    "" + players.get(i).getSalary() + " M€",
                    "" + players.get(i).getAge(),
                    "" + players.get(i).getBaseLevel()};

            final int finalI = i;
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    TableRow row = new TableRow(context);
                    row.setLayoutParams(rowParams);
                    row.setBackgroundResource(R.drawable.table_row_not_selected);

                    for (int j = 0; j < strings.length; j++) {
                        TextView text = new TextView(context);
                        text.setBackgroundResource(R.drawable.table_cell_background);

                        if (j == 0) {
                            text.setLayoutParams(nameParams);
                            text.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
                            text.setPadding((int) getResources().getDimension(R.dimen.spacing_small), 0,  (int) getResources().getDimension(R.dimen.spacing_small), 0);
                            text.setMaxLines(1);
                            text.setEllipsize(TextUtils.TruncateAt.END);
                        }else if (j == 1) {
                            text.setLayoutParams(teamNameParams);
                            text.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
                            text.setPadding((int) getResources().getDimension(R.dimen.spacing_small), 0,  (int) getResources().getDimension(R.dimen.spacing_small), 0);
                            text.setMaxLines(1);
                            text.setEllipsize(TextUtils.TruncateAt.END);
                        } else if (j == 2 || j == 3) {
                            text.setLayoutParams(positionAndContractParams);
                            text.setGravity(Gravity.CENTER);
                        } else if (j == 4) {
                            text.setLayoutParams(valueParams);
                            text.setGravity(Gravity.CENTER);
                        } else if (j == 5) {
                            text.setLayoutParams(salaryParams);
                            text.setGravity(Gravity.CENTER);
                        } else if (j == 6 || j == 7) {
                            text.setLayoutParams(ageAndLevelParams);
                            text.setGravity(Gravity.CENTER);
                        }

                        if (j == 2) {
                            switch (players.get(finalI).getPosition()) {
                                case Player.PLAYER_POSITION_GOALKEEPER:
                                    text.setBackgroundResource(R.drawable.table_cell_background_position_gk);
                                    break;
                                case Player.PLAYER_POSITION_DEFENDER:
                                    text.setBackgroundResource(R.drawable.table_cell_background_position_def);
                                    break;
                                case Player.PLAYER_POSITION_MIDFIELDER:
                                    text.setBackgroundResource(R.drawable.table_cell_background_position_mid);
                                    break;
                                case Player.PLAYER_POSITION_FORWARD:
                                    text.setBackgroundResource(R.drawable.table_cell_background_position_fw);
                                    break;
                            }
                        }

                        if (j == 3 && strings[3].split("/")[0].equals("1")) {
                            text.setBackgroundResource(R.drawable.table_cell_background_danger);
                        }

                        text.setMinHeight((int) getResources().getDimension(R.dimen.cell_height));
                        text.setText(strings[j]);
                        row.addView(text);
                    }

                    ProgressBar levelProgress = new ProgressBar(new ContextThemeWrapper(context, R.style.ProgressBar), null, 0);
                    levelProgress.setBackgroundResource(R.drawable.table_cell_background);
                    levelProgress.setLayoutParams(levelBarParams);
                    levelProgress.setMinimumHeight((int) getResources().getDimension(R.dimen.cell_height));
                    levelProgress.setProgress(players.get(finalI).getBaseLevel());
                    levelProgress.setPadding((int) getResources().getDimension(R.dimen.spacing_small), 0, (int) getResources().getDimension(R.dimen.spacing_small), 0);
                    row.addView(levelProgress);

                    final int finalIndex = finalI;
                    row.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            if (searchProgressBar.getVisibility() == View.VISIBLE) {
                                return;
                            }
                            SoundEffectsManager.getInstance().play(context, SoundEffectsManager.button);
                            displaySignDialog(players.get(finalIndex));
                        }
                    });

                    row.setOnTouchListener(new View.OnTouchListener() {
                        @Override
                        public boolean onTouch(final View view, MotionEvent motionEvent) {
                            if (searchProgressBar.getVisibility() == View.VISIBLE) {
                                return true;
                            }

                            switch (motionEvent.getAction()) {
                                case MotionEvent.ACTION_DOWN:
                                    delayColorizeHandler.postDelayed(new Runnable() {
                                        @Override
                                        public void run() {
                                            Util.getInstance().colorizeBackgroundCheckingApiLevel(view);
                                        }
                                    }, Util.WAIT_TIME_BEFORE_TOUCH_COLORIZE);
                                    break;
                                case MotionEvent.ACTION_CANCEL:
                                case MotionEvent.ACTION_UP:
                                    delayColorizeHandler.removeCallbacksAndMessages(null);
                                    Util.getInstance().clearBackgroundColorFilter(view);
                                    break;
                            }
                            return false;
                        }
                    });

                    tableLayout.addView(row);
                }
            });
        }
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tableLayout.setAlpha(1f);
                searchProgressBar.setVisibility(View.GONE);
            }
        });
    }
}

1 Ответ

0 голосов
/ 08 мая 2020

Вы не можете создавать элементы пользовательского интерфейса (TableRow, TextView, и т.д. c.) И управлять ими в фоновом потоке. Все это нужно делать напрямую в основном (UI) потоке. Вероятно, вы создаете блокировки объектов на частях инфраструктуры пользовательского интерфейса, которые блокируют основной (UI) поток и вызывают ANR.

Позаботьтесь о фоновом потоке l oop над игроками и получите данные для каждого из них. . Затем он может опубликовать Runnable в основном (UI) потоке (один раз для каждого игрока) с данными игрока, а в Runnable (который будет выполняться в основном (UI) потоке) вы можете создать элементы пользовательского интерфейса и добавьте их в свой макет.

Сделав это в al oop в фоновом потоке, вы не заблокируете поток пользовательского интерфейса.

...