I am new to Android development and Java and was wondering if somebody could help me with the following:
I have created an application that runs a server thread listening on a specified port. I would like to print messages received from a connected client into a TextView in the activity.
The server thread is in a separate class. The run method in this class listens for a client connection and reads any data received into a String.
What would be the best way for me to transfer the contents of this String back to the activity so that it can update the TextView?
From my (limited) understanding, only the ui thread should update a TextView and I can't find a way to get runOnUiThread to update the TextView.
Added code as requested.
Activity code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView messages = (TextView) findViewById(R.id.messages);
try {
newThread server = new newThread(this, messages);
} catch(Exception e) {
Toast.makeText(ChatActivity.this, e.toString(), Toast.LENGTH_LONG).show();
}
}
Run method in newThread class:
public void run()
{
serv = new ServerSocket(8000);
while(true)
{
cli = serv.accept();
user = cli.getInetAddress().toString();
BufferedReader cli_in = new BufferedReader(new InputStreamReader(cli.getInputStream()));
OutputStreamWriter cli_out = new OutputStreamWriter(cli.getOutputStream());
while((buf = cli_in.readLine()) != null)
{
// Update the messages TextView with buf
}
}
}
To avoid making things too cluttered I have omitted what irrelevant code I can.
Basically, in the inner while loop in run() I would like to pass the "buf" String to the activity so that the messages textview can be updated with it's content.
Cheers
Maybe a bad idea, but how about using AsyncTask? Didn't try if this would work, but it just might, since onProgressUpdate has access to UI thread.
private TextView messages;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
messages = (TextView) findViewById(R.id.messages);
ReceiveTask receive = new ReceiveTask();
receive.execute(100)
}
private void updateTextView(String text)
{
messages.setText(text);
}
private class ReceiveTask extends AsyncTask<Integer, String, Long> {
#Override
protected void onPreExecute() {
}
protected Long doInBackground(Integer... urls) {
newThread nt = new newThread();
while(true)
{
publishProgress(run());
}
return (long)0;
}
protected void onProgressUpdate(String... value) {
updateTextView(value[0]); //method in Activity class, to update TextView
}
protected void onPostExecute(Long result) {
}
}
Basically publishProgress will send data to onProgressUpdate, which will then send data to method (updateTextView) in main class and update TextView.
Usually it helps if you tell people you're working on a chat. Also, run() will need to be modified, to return string back, and remove while(true) loop from it. This is NOT the best idea, I suggest you go through a few tutorials on how to make an android chat first.
Related
I am building an application that is pretty dependent on async requests to function.
I have the main Activity called MainActivity. This really doesn't do much apart from contain layouts, however it does have a recycleviewer.
I then have a couple of http requests that are done to populate the recycle viewer.
To do this I have wrote a java class as follows:
public class dataforUi extends AsyncTask<String, String, JsonObject> {
private ArrayList<UiElements> els;
protected void onPreExecute() {
progressDialog.setMessage("Downloading your data...");
progressDialog.show();
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface arg0) {
RedditRequests.this.cancel(true);
}
});
}
protected JsonObject doInBackground(String... params) {
Do the http request here, get the result and populate uiElements with it
}
#Override
protected void onPostExecute(JsonObject jsonObject) {
super.onPostExecute(jsonObject);
progressDialog.hide();
}
I have a few more classes like this but hopefully it serves as an example of what I'm trying to do.
Then back in Main Activity, I have this code:
public void getUiElements() {
dataforUi ui = new dataforUi(findViewById(android.R.id.content));
try {
ui.execute("https://t").get();
ArrayList<UiElements> r = ui.getEls();
Log.d("MainFeedScreen", "Size of r is:" + r.size());
UiAdapter = new UiAdapter(r);
mRecyclerView.setAdapter(UiAdapter);
} catch (Exception e) {
}
}
This works fine, but it is very jolty due to the use of .get() on execute to make it blocking. If i remove .get() the progress bar shows up and disappears when the task is done, but my ui thread has progressed past this and ha tried to populate my view with an Empty Array and therefore nothing shows.
I have done a bit of looking into it but cant find a conclusive way of managing the notification of the UI thread that an activity is done.
Would really appericiate any advice on this one.
You need to fix your design.
On post execute, use local broadcast to notify your MainActivity that the AsyncTask is done.
Try using a separate thread to process your data. I use a ListView in stead of a RecyclerView, but the principle is the same.
I have no problems with jolting views.
protected void onPostExecute(String result) {
final String value = result;
// dismiss the dialog after getting all data
progressDialog.dismiss();
if (!value.isEmpty()) {
// updating UI from a new thread
runOnUiThread(new Runnable() {
public void run() {
// ListData is my custom class that holds my data
ArrayList<ListData> arrayDriverListData = new ArrayList<ListData>();
ListDataAdapter adapter = new ListDataAdapter(ListActivity.this, arrayListData);
ListData data;
boolean b = true;
try {
// My data is from a Json source from node 'history'
JSONObject object = new JSONObject(value);
JSONArray array = object.getJSONArray("history");
int len = array.length();
if (len > 0) {
for (int i = 0; i < len; i++) {
final JSONObject o = array.getJSONObject(i);
// Parse my data and add it to my adapter
adapter.add(data);
}
}
} catch (JSONException jex) {
Log.e(TAG, "" + jex.getMessage());
}
// setListAdapter is my call to update my list view
setListAdapter(adapter);
}
});
}
}
Now just update the UI thread
private void setListAdapter(ListDataAdapter adapter){
// my ListView
lvHistory.setAdapter(adapter);
}
I would like to do step by step upload date to web service.
My code:
private Thread WebServiceThread;
public void onCreate(Bundle savedInstanceState) {
//...
WebServiceThread = new WebService();
WebServiceThread.start();
}
private class WebService extends Thread {
public void run() {
try {
new WebServiceUpload().execute("");
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT)
.show();
}
}
}
private class WebServiceUpload extends AsyncTask<String, Void, String> {
protected String doInBackground(String... data) {
// upload part
}
protected void onPostExecute(String result) {
//...
WebServiceThread = new WebService();
WebServiceThread.start();
//<Tab>__what to do here__</Tab>
//...
}
}
Now can run, but cause the device slow.
Please tell me how to close parent thread or restart parent thread way to solve this problem. (or other practice to same target.)
You don't have to chain threads like that. Just create a single AsyncTask extension that uploads the data step by step in doInBackground. If you want to publish progress reports, you can do that by calling publishProgress.
Your method of creating a WebServiceUpload from a worker thread is really bizarre and will most likely not work. AsyncTask is designed to be started from the UI thread. Just call your new WebServiceUpload().execute() from the main thread when you want to start the upload steps.
In your onPostExecute check if thread is running then force it to stop.
protected void onPostExecute(String result) {
//...
**if (WebServiceThread.isAlive())
WebServiceThread.stop();**
WebServiceThread = new WebService();
WebServiceThread.start();
//<Tab>__what to do here__</Tab>
//...
}
I want to write a function that listens a server by sending get requests reqularly. The next request shouldn't be sent to server until a response is received from the first request. To simulate this, I write the following code which sends consecutive get request to server 5 times:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
TextView tv = new TextView(this);
for (int i = 0; i < 5; i++)
{
tv.setText(retrieve("http://www.mydomain.com/http.php?key=" + i));
setContentView(tv);
}
}
"String retrieve(String url) { ... }" is function that returns content of url, in this case just a number sent by the following PHP file ("http.php"):
<?php
sleep(1);
echo $_GET['key']+1;
?>
Although I put 1 second sleep to see values on my android app, the only thing I see is "5" after some seconds. So are these 5 requests are asyn or are they consecutive or is there another problem?
The code inside the method onCreate is executed only once before you get to see anything, thats probably why you can only see 5 (the last one). Also, you shouldn't do network calls from there since that will block the UI main thread for a while and probably provoke a force close in your app.
You should implement a method to run in background. Something similar to this:
private int i;
public void onCreate(Bundle savedInstanceState) {
i = 0
new ServerRetreiveTask().execute("http://www.mydomain.com/http.php?key=" + i);
}
private class ServerRetreiveTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String urls) {
return retrieve(url);
}
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
if (i<5){
i++:
new ServerRetreiveTask().execute("http://www.mydomain.com/http.php?key=" + i);
}
}
}
Read this document for more info about asynchronous task http://developer.android.com/resources/articles/painless-threading.html
Alternative way using Threads (not recommended):
new Thread(new Runnable() {
#Override
public void run() {
final String response;
for (int i = 0; i < 5; i++)
{
response = retrieve("http://www.mydomain.com/http.php?key=" + i);
runOnUiThread(new Runnable() {
#Override
public void run() {
tv.setText(response);
}
});
}
}
}).start();
I'm facing a problem,On a button press i try to read the DataInputstream which has data coming in and display the data.
I'm using a while loop to read the data. But the dynamic update of the Textview doesnt happen.
TextView datatextview = (TextView)findViewById(R.id.data);
DataInputStream Din = new DataInputStream(socket.getInputStream());
Button getData= (Button)findViewById(R.id.getdata);
getData.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
//.......stuff .......
try{
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int bytesRead = -1;
String message1 = "";
while (true) {
message1 = "";
data = Din.readLine();
bytesRead = (reading).length();
if (bytesRead != -1) {
Log.v(TAG,"data"+data); //I'm getting the data correctly
//But not able to update it in the TextView :(
datatextview.setText(data); //doesnt work
}
}
You have to exit your method and relinquish control of the UI thread in order for your views to update. You should probably be using AsyncTask with its progress updating functionality to do this, so that you're not hogging the UI thread.
Something like:
public class MyTask extends AsyncTask<Void, String, Void> {
private final TextView progress;
public MyTask(TextView progress) {
this.progress = progress;
}
#Override
protected void onPreExecute() {
progress.setText("Starting...");
}
#Override
protected Void doInBackground(Void... unused) {
... your buffer code, including publishProgress(data); in your while loop ...
}
#Override
protected void onProgressUpdate(String... data) {
progress.setText(data[0]);
progress.invalidate(); // not sure if this is necessary or not
}
#Override
protected void onPostExecute(Void unused) {
progress.setText("Finished!");
}
}
Then you can create and execute your AsyncTask using:
new MyTask(datatextview).execute();
Edit - make sure to use #Override which helps alert you if your method signatures are not correct.
Matthew is right here but to elaborate...
If you are doing this from a thread you are probably stuck looping so fast the UI never gets a chance to redraw with your new values. If you want to force a redraw of the UI from a thread call View.invalidate().
If you are doing this from your main thread (which you should definitely reconsider) again you are stuck looping and UI can't redraw...you would want to all View.postInvalidate() to force a UI redraw.
I seem to be having trouble with updating a TextView from a thread. I have a GameConnection class (which manages a socket connection) which I want to use across activities. It calls a local "onMessage", which then uses the target handler to call dispatch Message. The "Handler" in this case, is in my GameBrowser activity.
Here's code from the GameConnection class.
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(),true);
String message = "".intern();
// as a newline character is read, we interpret it as a message
while ((message = in.readLine()) != null && isConnected){
onMessage(message);
}
As said above, a local method "onMessage" method handles dispatching of the message.
private void onMessage(String message){
... // create message from String
handler.dispatchMessage( msg );
}
However, when I get the response in the GameBrowser class, I get a CalledFromWrongThreadException . Initially, I was using a callback method, which of course wasn't working. So, after some research, I've found that I have to use a Handler, but I can't seem to get it right.
public class GameBrowser extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(C.tag, "GameBrowser.onCreate addr:" + this);
handler = new Handler(new HandlerCallback());
connection.addMessageListener(handler);
connection.connect();
txtGameLabel = (TextView)findViewById( R.id.txtGamesLabel);
setContentView(R.layout.game_browser);
}
private class HandlerCallback implements Callback{
#Override
public boolean handleMessage(Message msg) {
if (txtGameLabel == null){
txtGameLabel = (TextView)findViewById( R.id.txtGamesLabel);
}
String message = msg.getData().getString("message");
Log.d(C.tag, "GameBrowser recieved message " + message);
txtGameLabel.setText("Data: " + message);
return true;
}
}
}
I figured out what I was doing wrong. Instead of calling the handler from the socket thread, I used a callback, then used Runnable to post to the handler in the GameConnection class. When onMessage executes "run", which executes "updateTextField", we're back in the main thread.
#Override
public void onMessage(final String message) {
handler.post(new Runnable(){
#Override
public void run() {
updateTextField(message);
}
});
}
private void updateTextField(String message){
if (txtGameLabel == null)
txtGameLabel = (TextView)findViewById( R.id.txtGamesLabel);
txtGameLabel.setText(message);
}