Содержимое адаптера изменилось, но ListView не получил уведомления. (передача списка из основного потока)

Я разрабатываю приложение для чата, и мои адаптеры (BaseAdapter), которые содержат журналы чата, находятся в Сервисе, потому что переменная, которая дает мне ресурсы IRC, находится в Сервисе. Когда активность привязана, мои фрагменты чата получают адаптеры от службы. Сначала я пытался уведомить DataSetChanged() из самого адаптера, затем обнаружил, что мне нужно вызвать его из основного потока пользовательского интерфейса. Итак, я отправляю широковещательную рассылку во фрагмент, чтобы уведомить адаптер внутри, фрагмент использует основной поток пользовательского интерфейса. Теперь я получаю эту проблему, потому что я загружаю список вне потока пользовательского интерфейса (из службы) и уведомляю об этом в основном потоке. Но этот адаптер нужен на сервисе. Что я должен делать?

Это мой адаптер. Есть один из них для каждого разговора. Этот адаптер остается в эксплуатации:

public class Conversa extends BaseAdapter {

private Context context;
public ArrayList<String> log;
private String target;

public Conversa(Context context, String target) {
    this.context = context;
    this.target = target;
    log = new ArrayList<String>();
}

@Override
public int getCount() {
    return log.size();
}

@Override
public Object getItem(int pos) {
    return log.get(pos);
}

@Override
public long getItemId(int arg0) {
    return 0;
}

@Override
public View getView(int pos, View convertView, ViewGroup parent) {
    if (convertView == null) {
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.msg_layout, parent, false);
    }

    TextView msg = (TextView) convertView.findViewById(R.id.msg);
    msg.setText(log.get(pos));

    return convertView;
}

public ArrayList<String> getLog() {
    return log;
}

public String getTarget() {
    return target;
}

public void setTarget(String target) {
    this.target = target;
}

}

Это мой фрагмент чата:

public class ChatFragment extends Fragment {

private String target;
private ListView listview;
private EditText edittext;
private Conversa conversa;
private MainActivity activity;
private BroadcastReceiver BR_update_list;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    target = getArguments().getString(IRCService.EXTRA_TARGET);

    activity = (MainActivity) getActivity();

    conversa = activity.service.getConversa(target);

    registerUpdateReceiver();

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    setHasOptionsMenu(true);

    View view;

    view = inflater.inflate(R.layout.chat_layout, container, false);

    edittext = (EditText) view.findViewById(R.id.chatinput);
    listview = (ListView) view.findViewById(R.id.chatlist);

    listview.setAdapter(conversa);

    // EM CASO DE RETORNO DO SERVICE EM BACKGROUND, ROLAR A LISTA ATEH O FIM
    if (!conversa.getLog().isEmpty()) {
        scrollMyListViewToBottom();
    }

    // LISTENER QUE RECEBERA O "ENVIAR" DO TECLADO DO ANDROID
    edittext.setOnEditorActionListener(new OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId,
                KeyEvent event) {
            boolean handled = false;
            if (actionId == EditorInfo.IME_ACTION_SEND) {
                // ESCONDE O TECLADO APOS ENVIAR
                InputMethodManager in = (InputMethodManager) getActivity()
                        .getSystemService(Context.INPUT_METHOD_SERVICE);
                in.hideSoftInputFromWindow(
                        edittext.getApplicationWindowToken(),
                        InputMethodManager.HIDE_NOT_ALWAYS);

                sendMessage(target, edittext.getText().toString());

                // ALTERA O FLAG
                handled = true;

            }

            return handled;
        }
    });

    return view;
}

@Override
public void onDestroy() {
    activity.unregisterReceiver(BR_update_list);
    super.onDestroy();
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    if (target.equals(IRCService.CANAL)) {
        inflater.inflate(R.menu.canal_menu, menu);
    } else {
        inflater.inflate(R.menu.pvt_menu, menu);
    }
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.menu_closepvt:
        activity.removePVTTab(conversa.getTarget());
        return true;

    case R.id.menu_desconectar:
        activity.service.IRCdisconnect();
        return true;

    case R.id.menu_mudarnick:
        // TODO IMPLEMENTAR MUDANÇA DE NICK

        return true;
    }

    return false;

}

private void registerUpdateReceiver() {

    BR_update_list = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            conversa.notifyDataSetChanged();
        }
    };

    //a categoria sera o que definira qual fragment devera ser atualizado
    IntentFilter filter = new IntentFilter(IRCService.ACTION_RECEIVE_MSG);
    filter.addCategory(target);

    activity.registerReceiver(BR_update_list, filter);

}

public void sendMessage(String target, String msg) {
    activity.service.sendMessage(target, msg);

    // APAGA O EDITTEXT
    edittext.setText("");
}

private void scrollMyListViewToBottom() {
    listview.post(new Runnable() {
        @Override
        public void run() {
            // Select the last row so it will scroll into view...
            listview.setSelection(conversa.getCount() - 1);
        }
    });
}

public void setQuote(String nick) {
    ((MainActivity) getActivity()).drawerlayout.closeDrawers();
    edittext.setText("");
    edittext.append(nick + ": ");
    edittext.requestFocus();
}

}

Это только мои методы получения сообщений и переменные адаптера в службе:

private List<Conversa> pvts;
private Conversa canal;
private UserList userList;


public void receivePVTMessage(String target, String nick, String msg) {

    Conversa pvt = getConversa(target);

    if (pvt == null) {
        addPVTConversa(target);
        pvt = getConversa(target);
    }

    pvt.log.add(Colors.removeFormattingAndColors("<" + nick + "> " + msg));

    // é dessa forma que o fragment fará o notifydatasetchanged na UI
    // thread, a categoria define qual fragment sera atualizado.
    Intent it = new Intent(IRCService.ACTION_RECEIVE_MSG);
    it.addCategory(target);
    sendBroadcast(it);

}

/**
 * Metodo usado para adicionar mensagens de usuarios na janela do chat
 */
public void receiveChatMessage(String nick, String msg) {
    canal.log
            .add(Colors.removeFormattingAndColors("<" + nick + "> " + msg));

    // é dessa forma que o fragment fará o notifydatasetchanged na UI
    // thread, a categoria define qual fragment sera atualizado.
    Intent it = new Intent(IRCService.ACTION_RECEIVE_MSG);
    it.addCategory(IRCService.CANAL);
    sendBroadcast(it);

}

/**
 * Metodo usado para adicionar mensagens gerais na janela do chat
 */
public void receiveChatMessage(String msg) {
    canal.log.add(Colors.removeFormattingAndColors(msg));

    // é dessa forma que o fragment fará o notifydatasetchanged na UI
    // thread, a categoria define qual fragment sera atualizado.
    Intent it = new Intent(IRCService.ACTION_RECEIVE_MSG);
    it.addCategory(IRCService.CANAL);
    sendBroadcast(it);

}

То же самое происходит в моем списке журнала сообщений и в моем списке пользователей (списке ников). Адаптер списка пользователей тоже работает, и у меня есть фрагмент списка, чтобы показать его; В обоих случаях я обновляю массивы из службы и уведомляю их об адаптерах с помощью sendBroadcast() и получаю фрагмент, содержащий адаптер;


person Matheus Henrique da Silva    schedule 30.03.2014    source источник
comment
Трудно сказать без кода.   -  person cyroxis    schedule 31.03.2014
comment
Хотя это может сработать, я бы сказал, что вы используете опасную структуру. В идеале служба отправляет сообщение, а затем в своей деятельности вы получаете это сообщение и заполняете адаптер (вы можете использовать свое уже закодированное широковещательное сообщение для отправки данных нового сообщения вместо недействительного запроса). Ошибка, которую вы получаете, в любом случае означает, что содержимое адаптера изменилось, И вы не вызвали notifyDataSetChanged(): ListView обнаружил, что содержимое изменилось «черным ходом». Это ломает внутренности ListView.   -  person rupps    schedule 31.03.2014
comment
Как только я обновляю список, я каждый раз отправляю широковещательную рассылку для уведомления данных. Проблема в многопоточности. Но мне нужно получать сообщения, даже если активность закрыта. Служба должна получать сообщения. Адаптеры должны быть в рабочем состоянии.   -  person Matheus Henrique da Silva    schedule 31.03.2014
comment
Я пытаюсь получить доступ к потоку пользовательского интерфейса из обработчика, но не уверен, что сработает...   -  person Matheus Henrique da Silva    schedule 31.03.2014
comment
Я могу отправить только 1 аргумент в метод handleMessage()... :(   -  person Matheus Henrique da Silva    schedule 31.03.2014


Ответы (1)


Я делаю то, что мне нужно, используя обработчик, чтобы получить доступ к основному потоку. Обработчик обработчика = новый обработчик (Looper.getMainLoop);

person Matheus Henrique da Silva    schedule 07.04.2014