I would like to ask about sockets in Android. I rummaged through half of the Internet and did not find what I needed.

The task is to write a client for Android in Java, which would send and receive messages. When I wrote to the computer, everything was wonderful, I immediately ran into the problem of multithreading (you cannot call network tasks from the main thread). I figured out more or less, forced me to send messages to the server, but I can’t get the TextView to accept and update the dialogue history.

Here the client code, the server even rewrote as a simple echo server, sends back what the client sent. Below client code, help with the implementation of receiving messages from the server.

import android.os.AsyncTask; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import java.io.BufferedReader; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; public class MainActivity extends AppCompatActivity { private Socket socket; private Button SendBut; private EditText editText; private TextView textView; private PrintWriter os; private BufferedReader is; networking net; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SendBut = (Button) findViewById(R.id.SendBut); editText = (EditText) findViewById(R.id.editText); textView = (TextView) findViewById(R.id.TxtFrSrv); net = new networking(); net.execute(); } public void onClck(View view) { String txt = null; txt = editText.getText().toString(); os.println(txt); } public class networking extends AsyncTask < String, Void, Void > { @Override protected Void doInBackground(String...params) { try { socket = new Socket("10.0.2.2", 4444); os = new PrintWriter(new PrintWriter(new OutputStreamWriter(socket.getOutputStream())), true); } catch (IOException e) { e.printStackTrace(); } return null; } } } 

I tried to portray something similar to this, but with zero value it is impossible to start ...

 public class getMsg extends Thread{ public void run(){ try { is=new BufferedReader(new InputStreamReader(socket.getInputStream())); final String finc; finc=is.readLine(); while (!finc.equals("break")){ MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { textView.setText(finc); } }); } } catch (IOException e) { e.printStackTrace(); } } } 

    5 answers 5

    Use retrofit Luke!

    Retrofit + rxJava + retrolambda

    Let the answer from the server such

     {"text":"текст сообщения"} 

    How to process

     public class Message{ @SerializedName("text") public String message; } public interface RequestApi { String BASE_URL = "http://server/"; RequestApi api = new Retrofit.Builder() .baseUrl(RequestApi.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .subscribeOn(AndroidSchedulers.mainThread() .build() .create(RequestApi.class); @GET("?method=getMsg") Observable<Message> getMessage(); } public class MainActivity extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SendBut = (Button) findViewById(R.id.SendBut); editText = (EditText) findViewById(R.id.editText); textView = (TextView) findViewById(R.id.TxtFrSrv); RequestApi.api.getMessage().subscribe(m->textView.setText(m.message)); } } 
    • This is the best option, but RxJava and Retrofit will not save you from problems with changing the configuration. You need to make a callback or use EventBus to process requests. Well, or bind RxJava to the life cycle of the activation. - mit

    Here is the solution to your problem.

     private class Networking extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... params) { try { Socket socket = new Socket("10.0.2.2", 4444); PrintWriter writer = new PrintWriter(new PrintWriter(new OutputStreamWriter(socket.getOutputStream())), true); writer.write(params[0]); writer.flush(); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); StringBuilder result = new StringBuilder(); String line; while ( (line = reader.readLine()) != null) { result.append(line); } return result.toString(); } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(String s) { textView.setText(s); } } 

    And AsyncTask necessary to call AsyncTask from the onClick() method

     new Networking().execute(text); 
    • AsyncTask is not the best option for working with the network. If at the time of working with the network the user changes the configuration, for example, turns the smartphone over (changes the screen orientation), then the activation will be recreated in the onPostExecute method, if you try to make textView.setText (s), you will get an NPE in the UI stream that will drop everything attachment. - mit
    • totally agree with you. But as the author of the question tried to solve the problem with this class, I suggested to him how to do it - miha_dev
    • @mit, AsyncTask good choice, everyone is just used to libraries and they don’t know which minus to come up with. - Flippy
     public void GetMsg(){ new Thread(new Runnable() { @Override public void run() { try { socket=new Socket(Host,4444); } catch (IOException e) { e.printStackTrace(); } try{ is = new BufferedReader(new InputStreamReader(socket.getInputStream())); } catch (IOException e) { e.printStackTrace(); } while (true){ String msg = null; try { msg=is.readLine(); } catch (IOException e) { e.printStackTrace(); } if (msg==null){ break; } else { displayMsg(msg); } } } }).start(); } public void displayMsg(String msg){ final String msg1 = msg; handler.post(new Runnable() { @Override public void run() { appendTextAndScroll(msg1); } }); } 

    Thank you all very much for your help, I rewrote the client a little and solved this problem with this code, I hope it will be useful to someone. The method GetMsg call in onCreate ().

    • In fact, you did not change anything, you wrote more code, because Originally incorrectly used AsyncTask, trying to post changes to the UI stream from the doInBackground method. AsyncTask in Android - is designed to replace direct work with Thread, which facilitates work with threads. - mit
    • Well, I disagree, I completely removed AsyncTask and did everything just in separate threads, maybe you are right, I didn’t understand AsyncTask well enough and therefore I tried to implement this task through it. - Snapesik
    • AsyncTask - works on Thread and Runnable, but hides them and makes working with threads more convenient. You simply wrote your AsyncTask, so in fact you didn’t change anything in the algorithm. Not having dealt with AsyncTask, you created your own "bicycle". In Android, it is recommended to use AsyncTask for tasks with background tasks. - mit
    • So you yourself said that AsyncTask is not the best way to work with the network, so I, based on your own words, made the absolutely right decision - Snapesik
    • Yes, AsyncTask is not the best solution for working with the network, but in this case I am commenting on your code. I repeat once again that you did not change anything, you just wrote more code. And your option is no better (and possibly worse, because it is a “bicycle” that does not take into account all the features of working with streams) than AsyncTask. In Android, when working with long background tasks (in particular with the network), it is recommended to use services. Google I / O 2010 Example based on REST - mit

    Why do you want TextView to do this? How do you imagine it? TextView is just a layout object.

    If you create a writer ,

     os = new PrintWriter(new PrintWriter(new OutputStreamWriter(socket.getOutputStream())), true); 

    then obviously you want him to write to the outgoing getOutputStream() . If you need to receive information, then most likely you need a reader that you will read it from the incoming stream - getInputStream() .

    • I don’t want to force textview to process the incoming stream, I just want it to update itself when a new incoming message arrives, for this you need to write some sort of class, but I don’t get it, I really don’t really imagine how to implement it. - Snapesik

    I would make a loop with waiting Thread.sleep(400) and check if the message came, if the message is !=null , then publishProgress(message) and in onProgressUpdate (! I don’t remember exactly what the method is called, I need to override it in AsyncTask) On TextView which needs to be initialized in advance, hammered the text.
    If I understand the essence of the problem