2016-07-12 20 views
0

私のアプリでは、私のスマートフォン(PHONE1と呼ぶ)から別のもの(PHONE2)にメッセージと通知を送信します。メッセージはPHONE2によって受信されます。 PHONE2はPHONE1に応答メッセージを送信します。 PHONE1は通知を受信し、メッセージを読み取ります。 PHONE1はPHONE2に返信メッセージを送信し、アプリがクラッシュします。メッセージチャットが正常に機能しない

Messages.java:

public class Messages extends AppCompatActivity { 

private static final String TAG = "ChatActivity"; 

Context context; 
private ChatArrayAdapter chatArrayAdapter; 
private ListView listView; 
private EditText chatText; 
private Button buttonSend; 
private boolean side = false; 
String from, to, mess; 


@RequiresApi(api = Build.VERSION_CODES.KITKAT) 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_messages); 


    buttonSend = (Button) findViewById(R.id.send); 

    listView = (ListView) findViewById(R.id.chatMessage); 

    chatArrayAdapter = new ChatArrayAdapter(getApplicationContext(), R.layout.row_messages_right); 
    listView.setAdapter(chatArrayAdapter); 

    chatText = (EditText) findViewById(R.id.msg); 
    chatText.setOnKeyListener(new View.OnKeyListener() { 
     public boolean onKey(View v, int keyCode, KeyEvent event) { 
      if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { 
       return sendChatMessage(); 
      } 
      return false; 
     } 
    }); 
    buttonSend.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View arg0) { 
      sendChatMessage(); 
     } 
    }); 

    listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL); 
    listView.setAdapter(chatArrayAdapter); 

    //to scroll the list view to bottom on data change 
    chatArrayAdapter.registerDataSetObserver(new DataSetObserver() { 
     @Override 
     public void onChanged() { 
      super.onChanged(); 
      listView.setSelection(chatArrayAdapter.getCount() - 1); 
     } 
    }); 


    Timer timer = new Timer(); 
    timer.scheduleAtFixedRate(new TimerTask() { 
     @Override 
     public void run() { 
      receiveChatMessage(); 
     } 
    }, 0, 1000); 



} 





@RequiresApi(api = Build.VERSION_CODES.KITKAT) 
private boolean receiveChatMessage(){ 
     InternalDatabaseOperations DB = new InternalDatabaseOperations(this); 
     Cursor CR = DB.getInformation(DB); 
     CR.moveToLast(); 
     from = CR.getString(0); 
     to = CR.getString(1); 
    if(!Objects.equals(mess, CR.getString(2))){ 
     mess = CR.getString(2); 
     chatArrayAdapter.add(new ChatMessage(!side, mess)); 
     return true; 
    } 
     return false; 
} 

//Send chat message 
private boolean sendChatMessage() { 
    mess = chatText.getText().toString(); 
    chatArrayAdapter.add(new ChatMessage(side,mess)); 
    chatText.setText(""); 
     InternalDatabaseOperations DB = new InternalDatabaseOperations(this); 
     Cursor CR = DB.getInformation(DB); 
     CR.moveToLast(); 
     from = CR.getString(1); 
     to = CR.getString(0); 
     BackgroundTaskSendingMessage sendingMessage = new BackgroundTaskSendingMessage(this); 
      sendingMessage.execute(to, from, mess); 
      return true; 

    } } 

chatArrayAdapter.java:

class ChatArrayAdapter extends ArrayAdapter<ChatMessage> { 

private TextView chatText; 
private List<ChatMessage> chatMessageList = new ArrayList<>(); 
private Context context; 

@Override 
public void add(ChatMessage object) { 
    chatMessageList.add(object); 
    super.add(object); 
} 

public ChatArrayAdapter(Context context, int textViewResourceId) { 
    super(context, textViewResourceId); 
    this.context = context; 
} 

public int getCount() { 
    return this.chatMessageList.size(); 
} 

//Retrieve message position 
public ChatMessage getItem(int index) { 
    return this.chatMessageList.get(index); 
} 

//Change layout inflater if necessary 
public View getView(int position, View convertView, ViewGroup parent) { 
    ChatMessage chatMessageObj = getItem(position); 
    View row = convertView; 
    LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    if (chatMessageObj.left) { 
     row = inflater.inflate(R.layout.row_messages_right, parent, false); 
    }else{ 
     row = inflater.inflate(R.layout.row_messages_left, parent, false); 
    } 
     chatText = (TextView) row.findViewById(R.id.msgr); 
     chatText.setText(chatMessageObj.message); 
     return row; 
    } 
} 

MessagingServiceNotification:

public class MyFirebaseMessagingService extends FirebaseMessagingService{ 


    @Override 
    public void onMessageReceived(RemoteMessage remoteMessage) { 

     String message = remoteMessage.getData().get("message"); 
     String from = remoteMessage.getData().get("From"); 
     String to = remoteMessage.getData().get("to"); 
     InternalDatabaseOperations DB = new InternalDatabaseOperations(this); 
     DB.putInformation(DB, from, to, message); 

     showNotification(message); 
    } 

    private void showNotification(String message) { 

     Intent i = new Intent(this, Messages.class); 
     i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 

     PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT); 

     NotificationCompat.Builder builder = new NotificationCompat.Builder(this) 
       .setAutoCancel(true) 
       .setContentTitle("BookStore") 
       .setContentText(message) 
       .setSmallIcon(R.drawable.book) 
       .setContentIntent(pendingIntent); 

     NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 
     manager.notify(0, builder.build()); 

    } 


} 

logcat:

14007-14681/gamingproject.sellmybooks E/AndroidRuntime: FATAL EXCEPTION: Timer-0 
                       Process: gamingproject.sellmybooks, PID: 14007 
                       android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 
                        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6353) 
                        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:875) 
                        at android.view.View.requestLayout(View.java:17524) 
                        at android.view.View.requestLayout(View.java:17524) 
                        at android.view.View.requestLayout(View.java:17524) 
                        at android.view.View.requestLayout(View.java:17524) 
                        at android.view.View.requestLayout(View.java:17524) 
                        at android.view.View.requestLayout(View.java:17524) 
                        at android.view.View.requestLayout(View.java:17524) 
                        at android.widget.AbsListView.requestLayout(AbsListView.java:2027) 
                        at android.widget.AbsListView.setSelectionFromTop(AbsListView.java:7045) 
                        at android.widget.ListView.setSelection(ListView.java:2018) 
                        at gamingproject.sellmybooks.Messages$3.onChanged(Messages.java:74) 
                        at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37) 
                        at android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50) 
                        at android.widget.ArrayAdapter.notifyDataSetChanged(ArrayAdapter.java:286) 
                        at android.widget.ArrayAdapter.add(ArrayAdapter.java:182) 
                        at gamingproject.sellmybooks.ChatArrayAdapter.add(ChatArrayAdapter.java:22) 
                        at gamingproject.sellmybooks.Messages.receiveChatMessage(Messages.java:104) 
                        at gamingproject.sellmybooks.Messages.access$300(Messages.java:23) 
                        at gamingproject.sellmybooks.Messages$4.run(Messages.java:83) 
                        at java.util.Timer$TimerImpl.run(Timer.java:284) 

は助けを事前にありがとうございます。

+2

ログに基づいて、別のスレッドからビューを更新しようとしています。これはできません。 –

+0

このエラーを調整するにはどうすればよいですか? – Carlo

+0

私はエキスパートではありませんが、データをメインスレッドに渡すことができるかどうかを確認してください。サービスを使用してメッセージを受信し、メインアクティビティにデータを渡します。もう一度、あいまいな言葉を残して申し訳ありません。 –

答えて

1

問題は、タイマーがバックグラウンドスレッドを使用していて、そのバックグラウンドスレッドからChatArrayAdapterを更新しようとしていることです。 the documentationから

各Timerオブジェクトに対応する が順次、タイマーのタスクをすべて実行するために使用されていることを、単一のバックグラウンドスレッドです。

タイマーのためであるとして、あなたはあなたのコードを維持した場合:

Timer timer = new Timer(); 
timer.scheduleAtFixedRate(new TimerTask() { 
    @Override 
    public void run() { 
     receiveChatMessage(); 
    } 
}, 0, 1000); 

あなただけのUIスレッド上でアダプタに項目を追加するコードを配置する必要がありますように見えます:

private boolean receiveChatMessage() { 
     InternalDatabaseOperations DB = new InternalDatabaseOperations(this); 
     Cursor CR = DB.getInformation(DB); 
     CR.moveToLast(); 
     from = CR.getString(0); 
     to = CR.getString(1); 
    if(!Objects.equals(mess, CR.getString(2))){ 
     mess = CR.getString(2); 

     //modified: 
     runOnUiThread(new Runnable() {    
      @Override 
      public void run() { 
      chatArrayAdapter.add(new ChatMessage(!side, mess)); 
      } 
     }); 

     CR.close(); //close your cursor to avoid memory leaks! 
     return true; 
    } 
    CR.close(); //close your cursor to avoid memory leaks! 
    return false; 
} 
+0

ありがとう – Carlo

関連する問題