Есть ли способ создать представления на Android параллельно? - PullRequest
0 голосов
/ 03 февраля 2020

Я разрабатываю приложение, в котором в определенной части его выполнения пользователь сможет просматривать данные в текстовом файле в виде графика. Однако, поскольку текстовый файл может иметь несколько столбцов (и он предназначен для создания графика для каждого столбца), использование только потока пользовательского интерфейса может заблокировать приложение на несколько мгновений. Итак, мой вопрос: есть ли класс Android, который позволяет мне создавать эти графики (View s) параллельно и, когда все графики готовы, добавить их в родительский макет?

Моей первой мыслью было использовать ExecutorService следующим образом (для простоты, используя TextView вместо GraphView, класса из библиотеки, которую я использую в своем проекте):

  • createTextsInParallel реализация:
public void createTextsInParallel(){
    ExecutorService executor = (ExecutorService) Executors.newFixedThreadPool(3);

    List<TextBuilderTask> taskList = new ArrayList<>();
    for (int i = 0; i < arrays.size(); i++){    // arrays is an ArrayList<ArrayList<Double>>
        if (arrays.get(i) == null) continue;
        taskList.add(new TextBuilderTask(this, arrays.get(i), i));
    }

    List<Future<TextView>> futureList = null;
    try {
        futureList = executor.invokeAll(taskList);

    } catch (InterruptedException e){
        e.printStackTrace();
    }

    executor.shutdown();

    if (futureList != null){
        for (int i = 0; i < futureList.size(); i++){
            TextView tv = futureList.get(i).get();
            parentLayout.addView(tv);           // parentLayout is a LinearLayout 
        }
    }
}
  • TextBuilderTask реализация:
class TextBuilderTask implements Callable<TextView> {
    protected MyActivity activity;
    protected ArrayList<Double> array;
    protected int pos;

    public TextBuilderTask(MyActivity activity, ArrayList<Double> array, int pos){
        this.activity = activity;
        this.array = array;
        this.pos = pos;
    }

    @Override
    public TextView call() throws Exception {
        TextView tv = new TextView(this.activity);
        tv.setText(String.format("%d: %s", this.pos, Arrays.toString(this.array)));
        return tv;
    }
}

Но вышеприведенное выдает следующее исключение:

Caused by: java.lang.RuntimeException: Can't create handler inside thread Thread[pool-1-thread-1,5,main] that has not called Looper.prepare()

Так куда мне звонить Looper.prepare()? Исходя из предыдущих вопросов SO, всякий раз, когда я звоню Looper.prepare(), я должен куда-то звонить Looper.loop() и Looper.quit(). Но так как я использую ExecutorService для выполнения этой работы, я не уверен, куда мне звонить. Любая помощь приветствуется.

1 Ответ

1 голос
/ 03 февраля 2020

Я не смог запустить ваш код, так как он содержит некоторые ошибки и не компилируется сразу, но я подозреваю, что проблема в том, что вы создаете экземпляры представлений во вторичном потоке (который вызывает call()). Я знаю, что это именно ваша цель, но это может быть не очень хорошая идея, потому что это зависит от реализации View, которую вы создаете. Например, если их конструктор создает несколько обработчиков, им потребуется подготовить петлитель. Вы можете попытаться сделать это в методе call(), но все еще существует тот факт, что в этот момент их обработчики могут работать в непредвиденном потоке.

Похоже, это также спецификация платформы c Таким образом, он может работать на некоторых телефонах, а не на других, в зависимости от фактической реализации этого конкретного представления. Я бы посоветовал взглянуть на этот пост здесь, и, возможно, вы можете попытаться следовать некоторым из этих предложений (например, использовать AsyncLayoutInflater): Раздуть представление в фоновом потоке

...