Пропадает флаг
Добавлено: 30 мар 2012, 23:20
Начал ловить в своем приложении плавающий глюк 
Вернее, он постоянный и я уже четко определил условия, после которых он начинает проявляться, но причину понять не могу, надеюсь подскажете что-то дельное.
Итак, опишу барахлящую часть.
Классы:
1) класс ActivityMessage активити с ListView, отображает сообщения (суть приложения - обмен сообщениями)
2) класc MessageService, сингелтон, хранит массив сообщений, отдает их для активити по его запросу.
3) класс NetworkService, "ходит" за сообщениями на сервер, сохраняет их в MessageService
Содержание классов, все лишнее порезал.
NetworkService проверяет раз в N секунд наличие обновлений на сервере и при наличии новых сообщений получает их и вызывает MessageService.NewIncomingMessage()
Этот метод добавляет новое сообщения в коллекцию messages и устанавливает флаг hasNewMessages в true.
Метод MessageService.list возвращает массив сообщений из коллекции
ActivityMessage при создании в onCreate вызывает метод update() и метод startUpdateList()
Метод update() :
а) получает из MessageService list - все сообщения, которые надо вывести в ListView
б) создает messageListAdapter - самописный адаптер, с inflate из xml и прочим
в) устанавливает для ListView этот адаптер, ну и перематывает его в конец.
Метод startUpdateList(), на который главные подозрения:
Этот метод создает новый таймер и таймертаск, в котором раз в 3 секунды проверяется флаг messageService.HasNewMessages() и если он установлен в true, то обновляет ListView при помощи метода update() и чистит флаг, означая, что новые сообщения отобразились в ListView.
Так же обратите внимание, что при нажатии Back активити финиширует finish().
Общая идея - сообщения поступают с сервера в рандомные моменты времени, когда поступили - попали в массив и флаг о том что они есть стал true
Активити по таймеру проверяет этот флаг и когда видит его в true, то обновляет ListView и обнуляет флаг.
Теперь описание самого глюка.
Когда запускаю приложение и перехожу на эту активити то все работает нормально, при наличии новых сообщений они в течении максимум 3 секунд отображаются в ListView.
Но когда я жму back, а затем снова захожу на эту активити то новые сообщения иногда отображаются, а иногда нет. Они гарантированно приходят, так как в MessageService.messages они есть, но вот механизм с флагом уже не срабатывает. Создается впечатление, что таймер, который создался при первом заходе на активити, после выхода с нее не уничтожился и продолжает работать. Затем я снова захожу на активити, создается второй таймер, но т.к. первый уже работает (якобы), то он забирает и обнуляет флаг раньше второго, актуального в данный момент таймера, поэтому новые сообщения нормально приходят, записываются в коллекцию messages, но отобразить их не получается. Иногда начинает работать, возможно происходит конкуренция между таймерами (если этот фантомный таймер действительно есть, просто у меня другого объяснения пока нет).
Вернее, он постоянный и я уже четко определил условия, после которых он начинает проявляться, но причину понять не могу, надеюсь подскажете что-то дельное.
Итак, опишу барахлящую часть.
Классы:
1) класс ActivityMessage активити с ListView, отображает сообщения (суть приложения - обмен сообщениями)
2) класc MessageService, сингелтон, хранит массив сообщений, отдает их для активити по его запросу.
3) класс NetworkService, "ходит" за сообщениями на сервер, сохраняет их в MessageService
Содержание классов, все лишнее порезал.
Код: Выделить всё
import java.util.Timer;
import java.util.TimerTask;
public class ActivityMessage extends ListActivity implements OnClickListener {
private String collocutor;
private Timer timer;
private TimerTask updateList;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_message);
/// try to get inner data
Intent intent = getIntent();
boolean fromChats = intent.getBooleanExtra("havePhone", false);
if(fromChats) {
String phone = intent.getStringExtra("phone");
this.collocutor = phone;
update(collocutor);
}
startUpdateList(); // prepare timer
}
@Override
public void onBackPressed() {
Intent intent = new Intent(this, ActivityChats.class);
startActivity(intent);
this.finish();
}
/// Method updates ListView
private void update(String collocutor) {
MessageService messageService = MessageService.getInstance(getApplicationContext());
ArrayList<MessageEntity> list = messageService.list(collocutor);
messageListAdapter = new MessageListAdapter(this, R.layout.list_item_message_enemy, list);
setListAdapter(messageListAdapter);
listView.setSelection(listView.getCount() - 1);
}
}
/// method for ListView update
private void startUpdateList() {
timer = new Timer();
updateList = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
MessageService messageService = MessageService.getInstance(getApplicationContext());
if (messageService.HasNewMessages()) {
update(collocutor);
messageService.clearHasNewMessages();
}
}
});
}
};
timer.schedule(updateList, 1000, 3000);
}
}
Код: Выделить всё
public class MessageService {
private static MessageService instance;
private Context context;
private ArrayList<MessageEntity> messages; // messages
private boolean hasNewMessages = false;
/// constructor
private MessageService(Context context) {
this.context = context;
messages = new ArrayList<MessageEntity>();
dbHelper = new DBHelper(context);
}
/// constructor
public static MessageService getInstance(Context context) {
if (instance == null) {
instance = new MessageService(context);
}
return instance;
}
/// return list of the messages with specified collocutor
public ArrayList<MessageEntity> list(String collocutor) {
ArrayList<MessageEntity> result = new ArrayList<MessageEntity>();
for (int i = 0; i < messages.size(); i++) {
if (messages.get(i).getPhoneFrom().equalsIgnoreCase(collocutor)) {
result.add(messages.get(i));
}
if (messages.get(i).getPhoneTo().equalsIgnoreCase(collocutor)) {
result.add(messages.get(i));
}
}
return result;
}
/// Method, when new message has come
public void NewIncomingMessage(String from, String text) {
// Этот метод вызывается из NetworkService сервиса, который "ходит"
// за сообщениями и при получении новых сообщений вызывет этот метод
MessageEntity message = new MessageEntity(from, text, false);
ChatService.getInstance(context).CreateChat(from); // create new chat (inner check, is exist)
messages.add(message);
hasNewMessages = true; // <--- Устанавливаю флаг, говорящий о том, что в массиве есть
// новые сообщения и их надо отобразить на ListView
}
public boolean HasNewMessages() {
return hasNewMessages;
}
public void clearHasNewMessages() {
hasNewMessages = false;
}
}
Этот метод добавляет новое сообщения в коллекцию messages и устанавливает флаг hasNewMessages в true.
Метод MessageService.list возвращает массив сообщений из коллекции
ActivityMessage при создании в onCreate вызывает метод update() и метод startUpdateList()
Метод update() :
а) получает из MessageService list - все сообщения, которые надо вывести в ListView
б) создает messageListAdapter - самописный адаптер, с inflate из xml и прочим
в) устанавливает для ListView этот адаптер, ну и перематывает его в конец.
Метод startUpdateList(), на который главные подозрения:
Этот метод создает новый таймер и таймертаск, в котором раз в 3 секунды проверяется флаг messageService.HasNewMessages() и если он установлен в true, то обновляет ListView при помощи метода update() и чистит флаг, означая, что новые сообщения отобразились в ListView.
Так же обратите внимание, что при нажатии Back активити финиширует finish().
Общая идея - сообщения поступают с сервера в рандомные моменты времени, когда поступили - попали в массив и флаг о том что они есть стал true
Активити по таймеру проверяет этот флаг и когда видит его в true, то обновляет ListView и обнуляет флаг.
Теперь описание самого глюка.
Когда запускаю приложение и перехожу на эту активити то все работает нормально, при наличии новых сообщений они в течении максимум 3 секунд отображаются в ListView.
Но когда я жму back, а затем снова захожу на эту активити то новые сообщения иногда отображаются, а иногда нет. Они гарантированно приходят, так как в MessageService.messages они есть, но вот механизм с флагом уже не срабатывает. Создается впечатление, что таймер, который создался при первом заходе на активити, после выхода с нее не уничтожился и продолжает работать. Затем я снова захожу на активити, создается второй таймер, но т.к. первый уже работает (якобы), то он забирает и обнуляет флаг раньше второго, актуального в данный момент таймера, поэтому новые сообщения нормально приходят, записываются в коллекцию messages, но отобразить их не получается. Иногда начинает работать, возможно происходит конкуренция между таймерами (если этот фантомный таймер действительно есть, просто у меня другого объяснения пока нет).