Параллельное исключение модификации, несмотря на ожидание финиша
Вот раздел моего 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");