Параллельное исключение модификации, несмотря на ожидание финиша

Вот раздел моего onCreate, который иногда вызывает исключение:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_tilisting);
    _context = getApplicationContext();
    SDName = Environment.getExternalStorageDirectory();
    //listview = (ListView)findViewById(R.id.TIlistview);
    String TIdir = new File(SDName, "/TitaniumBackup/").toString();
    final ArrayList<String> apps = new ArrayList<String>();
    final StringBuffer done = new StringBuffer();
    Command command = new Command(0,"ls -a "+TIdir+"/*.properties") {
        @Override
        public void output(int arg0, String arg1) {
            synchronized(apps) {
                apps.add(arg1);
                if (!done.toString().equals("")) {
                    done.append("done");//oh no
                }
            }
        }
    };
    try {
        RootTools.getShell(true).add(command).waitForFinish();
        String attrLine = "";
        int ind;
        backups = new ArrayList<TIBackup>();
        synchronized(apps) {
            for (String app : apps) {
                try {
                    TIBackup bkup = new TIBackup(app);
                    FileInputStream fstream = new FileInputStream(app);
                    BufferedReader atts = new BufferedReader(new InputStreamReader(fstream));
                    while ((attrLine = atts.readLine()) != null) {
                        ind = attrLine.indexOf('=');
                        if (ind !=-1 && !attrLine.substring(0,1).equals("#"))
                        bkup.prop.put(attrLine.substring(0,ind), attrLine.substring(ind+1));
                    }
                    backups.add(bkup);
                    atts.close();
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            done.append("done");
        }
        setListAdapter( new StableArrayAdapter(this,backups));
    } catch (InterruptedException e) {
        //TODO:errors
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (TimeoutException e) {
        e.printStackTrace();
    }

for (String app : apps) {вызывает исключение, несмотря на waitforfinish() перед ним.

Этот обновленный код должен исправить это, добавив данные из выходных данных и ожидая любых отставших с синхронизированными в основном коде, но если вы установите точку останова в строке //oh no выше, он все еще достигает этой точки, где он пытается добавить элемент после запуска основного кода пользовательского интерфейса. Так что waitforfinish() не ждет? Как я могу предотвратить это состояние гонки?

Я также попробовал RootTask код ниже, но кажется, что он останавливается на последней readline?

    RootTask getProfile = new RootTask() {
        @Override
        public void onPostExecute(ArrayList<String> result) {
            super.onPostExecute(result);
            for (String r : result) {
                System.out.println(r);
            }
        }
    };
    getProfile.execute("ls /data/data/org.mozilla.firefox/files/mozilla/" );

onPostExecute никогда не запускается.

2 ответа

Решение

Это было частично вызвано недостатком дизайна в RootTools. Я считаю, что суть проблемы в том, что операция, которую вы выполняете над оболочкой, занимает больше времени, чем время ожидания по умолчанию, установленное для команд оболочки. Когда истекает время ожидания, он просто возвращает команду как завершенную, в чем и заключается недостаток проекта.

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

https://code.google.com/p/roottools/issues/detail?id=35

Пожалуйста, дайте мне знать, если у вас есть какие-либо вопросы или проблемы:)

Output() должен быть вызван во время waitForFinish() ждет. Что-то не так в коде, реализующем выполнение Команды.

Скорее всего: команда исполнителя (RootTools?) запускает команду в оболочке, получает несколько выходных строк, уведомляет вызывающий поток о ожидании и затем вызывает output() команды для каждой строки, которую он получил в качестве вывода. Я думаю, что это должно уведомить командный поток после output() был вызван на объекте команды, для всех выходных строк.

Тем не менее, вы можете обернуть список модифицирующего кода и перечислить итерационный код в synchronized(<some common object>){},

Обновить:

Так waitForFinish() не ждет? Как я могу предотвратить это состояние гонки?

Это ждет, но не для вашего кода. Synchronized Ключевое слово просто убедился, что output() из Command Объект не вызывается в то же время, когда вы итерируете apps коллекция. Он не планирует запуск двух потоков в определенной последовательности.

ПО МОЕМУ МНЕНИЮ, waitForFinish() это не очень хорошая модель, поэтому ожидание вызывающего потока приводит к потере цели отдельного исполнителя. Это лучше сформулировать как AsyncTask или принять прослушиватель событий для каждого Command объект.

Просто грубый пример, этот класс:

public class RootTask extends AsyncTask<String,Void,List<String>> {
    private boolean mSuccess;

    public boolean isSuccess() {
        return mSuccess;
    }

    @Override
    protected List<String> doInBackground(String... strings) {
        List<String> lines = new ArrayList<String>();

        try {
            Process p = Runtime.getRuntime().exec("su");
            InputStream is = p.getInputStream();
            OutputStream os = p.getOutputStream();

            os.write((strings[0] + "\n").getBytes());

            BufferedReader rd = new BufferedReader(new InputStreamReader(is));

            String line;

            while ((line = rd.readLine()) != null){
                lines.add(line);
            }

            mSuccess = true;
            os.write(("exit\n").getBytes());
            p.destroy();

        } catch (IOException e) {
            mSuccess = false;
            e.printStackTrace();
        }

        return lines;
    }
}

можно использовать как:

RootTask listTask = new RootTask{
  @Override
  public void onPostExecute(List<String> result){
      super.onPostExecute();
      apps.addAll(result);
      //-- or process the results strings--
  }
};

listTask.execute("ls -a "+TIdir+"/*.properties");
Другие вопросы по тегам