Can't send data back with the UI from an AsyncTask - java

I have this AsyncTask which I use to send a chat message over the internet. The problem is that when I execute the task nothing happens - at least not on the UI. I suspect that onProgressUpdate() does not execute at all. The idea is that when the task is started, a message will be sent over the internet and an EditText on the UI will be updated with the new text. Here is the whole class:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import android.os.AsyncTask;
import android.widget.EditText;
public class Messager extends AsyncTask<SocketAndEditText, Void, Void> {
private MulticastSocket socket;
private EditText host;
private EditText port;
private EditText sendMessage;
private EditText messageBoard;
private InetAddress serverAddress;
private int pt;
private String newConverstion;
private String message;
#Override
protected Void doInBackground(SocketAndEditText... soEd) {
// get the text that they contain and add the new messages to the old ones
//host = soEd[0].getHost();
//port = soEd[0].getPort();
messageBoard = soEd[0].getMessageBoard();
sendMessage = soEd[0].getSendMessage();
message = sendMessage.getText().toString();
String conversation = messageBoard.getText().toString();
newConverstion = conversation.concat("\n[You] ").concat(message);
return null;
}
protected void onProgressUpdate(Integer... progress) {
// make the messages text view editable
messageBoard.setFocusable(true);
messageBoard.setText(newConverstion); // add the new message to the text view
messageBoard.setFocusable(false); // make the messages text view not editable
// erase the text on the second text view that has just been sent
sendMessage.setText("");
sendMessage(message);
}
public void sendMessage(String message) {
// convert the host name to InetAddress
try {
serverAddress = InetAddress.getByName("localhost");
} catch (Exception e) {}
pt = 4456;
// create socket and start communicating
try {
socket = new MulticastSocket(pt);
socket.joinGroup(serverAddress);
} catch (IOException e) {}
// Send message to server
// convert message to bytes array
byte[] data = (message).getBytes();
// create and send a datagram
DatagramPacket packet = new DatagramPacket(data, data.length, serverAddress, pt);
try {
socket.send(packet);
} catch (IOException e) {}
}
}
What could be wrong?

The onProgressUpdate() won't be called if you don't call publishProgress() yourself. See the 4 steps of AsyncTask.
As Boris pointed out. You should call sendMessage() in doInBackground() and update UI in onPostExecute().

onProgressUpdate should be invoked explicitly from within doInBackground as seen here. It is not the correct method to use in your case. I would rather expect that the setting of the text field should be done in onPostExecute. Reason being that the value of newConverstion is determined just after the remote call and it might take a while to complete. If you do it before the asynctask has finished execution you risk NPE.
Edit Adding some code:
public class Messager extends AsyncTask<SocketAndEditText, Void, Void> {
//skipping some field declaration
#Override
protected Void doInBackground(SocketAndEditText... soEd) {
// get the text that they contain and add the new messages to the old ones
//host = soEd[0].getHost();
//port = soEd[0].getPort();
messageBoard = soEd[0].getMessageBoard();
sendMessage = soEd[0].getSendMessage();
message = sendMessage.getText().toString();
sendMessage(message); //NOTE: added the remote call in the background method. This is the only thing that really SHOULD be done in background.
String conversation = messageBoard.getText().toString();
newConverstion = conversation.concat("\n[You] ").concat(message);
return null;
}
protected void onPostExecute(Void result) {
// make the messages text view editable
messageBoard.setFocusable(true);
messageBoard.setText(newConverstion); // add the new message to the text view
messageBoard.setFocusable(false); // make the messages text view not editable
// erase the text on the second text view that has just been sent
sendMessage.setText("");
}
Basically the most important thing is to place the most time consuming calls in the background of the task. In your case this is sendMessage. From then on you can do whatever fixes you wish in the postExecute and the preExecute. I am not quite sure what your intention for the onProgressUpdate was. Holever I just translated it to using onPostExecute. If you need to temporarily disable the field you can disable it in onPreExecute and enable it onPostExecute.

Related

How to get every time messages from service to activity using runOnUiThread

I have been stuck with one problem. I need some people which check a part of my code and help me with problem and critize my code (I write code but I haven't people which can say this is wrong or something in this pattern)
Generally.
My service get message from bluetooth (HC-05) and I can see values in Log.d, in service.
A part code of my service which get message.
private class ConnectedThread extends Thread{
private final BluetoothSocket bluetoothSocket;
private final InputStream inputStream;
private final OutputStream outputStream;
public ConnectedThread(BluetoothSocket socket){
Log.d(TAG,"ConnectedThread: Starting");
bluetoothSocket=socket;
InputStream tmpInput = null;
OutputStream tmpOutput = null;
try{
tmpInput = bluetoothSocket.getInputStream();
tmpOutput = bluetoothSocket.getOutputStream();
}catch (IOException e){
e.printStackTrace();
active=false;
}
inputStream=tmpInput;
outputStream=tmpOutput;
}
#Override
public void run() {
byte[] buffer = new byte[1024];
int bytes;
while(active){
try {
bytes = inputStream.read(buffer);
final String comingMsg = new String(buffer,0,bytes);
Log.d(TAG,"InputStream: " + comingMsg);
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Message message = new Message();
message.obj = comingMsg;
message.what = 1; // I need it to prevent NullObjReference
Log.d(TAG,"Handler run(): " + message.obj);
mHandler.sendMessage(message);
}
});
}catch (IOException e){
Log.e(TAG,"Write: Error reading input." + e.getMessage());
active=false;
break;
}
}
}
...some code is hidden because it is diploma thesis
}
The problem is get message every time from this service to another activity where all is happen.
I tried a lot of things (with Threads,Looper,runOnUiThread, handleMessage and callback), checked a lot of posts in stackoverflow and I tried to combine with my project but all time I had nullobjectreference (for that i tried to use msg.what to check) , black screen when tried to move to my home activity (it is main) and update my textView or typical crash app.
Now I want only to get message from service to textview. When everything starts working fine, I want to parse string (for example 3 first chars) and send message to one of six textviews.
A part of codes from onCreate before method runThread() is started:
Log.d(TAG,"Check intent - result");
if(getIntent().getIntExtra("result",0)==RESULT_OK){
mDevice = getIntent().getExtras().getParcelable("bonded device");
startConnection(mDevice,MY_UUID);
Log.d(TAG,"Check is active service ");
checkIfActive();;
}
Log.d(TAG,"Check intent - connect_to_paired");
if(getIntent().getIntExtra("connect_to_paired",0)==RESULT_OK){
mDevice = getIntent().getExtras().getParcelable("bonded_paired_device");
startConnection(mDevice,MY_UUID);
Log.d(TAG,"Check is active service ");
checkIfActive();
}
public void checkIfActive(){
Log.d(TAG,"CheckIfActive: Started");
while(myBluetoothService.active!=true) {
Log.d(TAG,"CheckIfActive() active is "+ myBluetoothService.active);
if (myBluetoothService.active) {
Log.d(TAG, "CheckIfActive: Running method runOnUiThread - myBluetoothService.active is "+myBluetoothService.active);
runThread();
}
}
}
Method runThread() which should work everytime after connected with bluetooth device:
public void runThread(){
//I used there Thread but when connection was fail,
// method created multiply threads when I tried to connect next time
runOnUiThread(new Runnable() {
#Override
public void run() {
handler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
while (true) {
switch (msg.what) {
//when is one, service has messages to send
case 1:
String message = myBluetoothService.mHandler.obtainMessage().toString();
rearLeft.setText(message);
break;
default:
super.handleMessage(msg);
}
}
}
};
}
});
}
UPDATE:
Is it good idea ? Maybe I can put JSON Object to service to send message and in the HomeActivity, I can try get values from JSON. Is it fast ? I send a lot of data, because bluetooth receive data of distance from 4 ultrasound sensors in 4 times in lasts until few milliseconds, everytime.
Here is screen how sees my data in service when I have debug logs.
Next idea, but still nothing:
HomeActivity (my main)
public void runThread(){
runOnUiThread(new Runnable() {
#Override
public void run() {
//Looper.prepare();
new Handler() {
#Override
public void handleMessage(Message msg) {
rearLeft.setText(msg.obj.toString());
}
};
//Looper.loop();
//Log.d(TAG, myBluetoothService.mHandler.getLooper().toString());
//rearLeft.setText(myBluetoothService.mHandler.getLooper().toString());
}
});
}
Service which should send data from bluetooth to UI Thread is the same (Check first code).
Screen from HomeActivity where you can see 6 text views. Now I want put all text to one view which will be refresh by get next message.
Ok this post a bit help me to solve problem:
Sending a simple message from Service to Activity
Maybe this link could help another people.
Thanks for help, now understand why i should use broadcast receiver to do this.

How to dynamically append a new line to a multiline textview

I have a multiline textview need to show the status of the server. So it should always add a new line when server status changes. However, When I use mTextview.Text=string.Format("connected\n"); or mTextview.SetText("...") The new line doesn't show up immediatelly but show when all the processes are finished. Can anyone help me change it into a automatically show TextView? THX
logTextView.Text = string.Format ("Client log:\n");
.......
logTextView.Text=string.Format("Socket connected to 172.27.27.1\n");
.......
logTextView.Text = logTextView.Text+string.Format ("Start send image to server\n");
.......
you should use:
YourTextView.setText("Connected\n");
and to add multiple lines use:
YourTextView.append("another line\n");
Create a new thread with that log process and immediately invoke it after some events occur.
For example:
class LogProcess implements Runnable {
private String message;
private TextView textView;
public LogProcess(TextView textView, String message) {
this.textView = textView;
this.message = message;
}
#Override
public void run() {
textView.append(message);
}
}
And then invoke it:
Thread logProcess = new Thread(new LogProcess(logTextView, message));
logProcess .start();

Android - AsyncTask or Thread while recovering data to SQLite

I'm having trouble figuring out how to make this work.
I'm developing an app in which I download data from a online txt, parse it and add it to my database. This is taking +-30 seconds, and there is only one part of the updater implemented.
When I try to use a Thread or Asynctask, I have problems when trying to pass the arguments to the void which is updating the Database.
How can I implement it on the good way?
Here is my code:
Main activity class:
public class Androideye_principal extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
//WhazzupUpdate.DownloadWhazzup(AllUsers, TotalConnections);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_androideye_principal);
PilotsSQLiteHelper pdbh = new PilotsSQLiteHelper(this, "PilotsDB", null, 1);
SQLiteDatabase dbPilots = pdbh.getWritableDatabase();
Context context = getApplicationContext();
String TotalConnections = new String();
WhazzupUpdate.DownloadWhazzup(dbPilots, TotalConnections, context);
dbPilots.close();
CharSequence text = "Total Users: " + TotalConnections;
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
[Continues with Buttons stuff and so on...]
Parser class:
public class WhazzupUpdate {
public static void DownloadWhazzup (SQLiteDatabase PilotsDB, String TotalConnections, Context context) {
try {
// Create a URL for the desired page
URL url = new URL("This is my url/whazzup.txt");
// Read all the text returned by the server
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
String str;
for (int i = 0; i<=4; i++) {
in.readLine(); } // Go to the number of connections
TotalConnections = in.readLine();
for (int i = 0; i<=3; i++) {
in.readLine(); } // Go to !CLIENTS
PilotsDB.execSQL("DELETE FROM Pilots");
while (((str = in.readLine()) != null) && !(in.readLine().contains("!AIRPORTS"))) {
// str is one line of text; readLine() strips the newline character(s)
String[] dataRow = str.split(":");
if (str.contains("PILOT")) {
ContentValues NewValue = new ContentValues();
NewValue.put("VID", dataRow[1]);
[More NewValue puts...]
PilotsDB.insert("Pilots", null, NewValue);
} //End IF Pilot
} // End While
in.close();
} catch (MalformedURLException e) {
} catch (IOException e) {
}
}}
As you see, I call WhazzupUpdate.DownloadWhazzup Method in the main activity, and this is when all is getting frozen, but don't know how to derivate it to another threat and keep the references to the Data Bases and so on...
Hope anyone can help me. Thanks in advance.
A Thread or AsyncTask would be fine here. I prefer using AsyncTask for most of my heavy-lifting. You can create an AsyncTask and do your work in doInBackground() as it works on a background Thread. Then you can update your UI elements if needed in any of its other methods.
onPostExecute() will run after a result is passed from doInBackground()
onProgressUpdate() will run if you need to update UI during doInBackground() operations by calling publishProgress(). Here you can show a ProgressBar if you need to
and
onPreExecute() will run when you first call your task before doInBackground() runs.
Running the code in a background thread using Thread or AsyncTask will allow your UI to be free while the heavy work is being done.
Example of AsyncTask
Using interface with AsyncTask to post data back yo UI
AsyncTask Docs

Communicate from Service to Activity via bound service

I've already bound an activity to my service following this tutorial.
http://developer.android.com/guide/components/bound-services.html
I'm able to call service functions, but what if I want to for example, change some of my textviews or disable some of the toggle buttons because of work done on the service (and from the service). Would there be an easy to way to do this?
You can use messages to send information between activities and services. This is an easy way to send simple data, but may not be the best option if you need to send data very frequently, or send complicated data. This is an example of some code I have in one of my apps with a service and an activity which communicate:
Code in the activity:
//this is where you set what you want to happen
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
//this switch reads the information in the message (usually just
//an integer) and will do something depending on which integer is sent
case 1: do_something();
case 2: do_something_2(); //etc.
default:
super.handleMessage(msg);
}
}
}
final Messenger myMessenger = new Messenger(new IncomingHandler());
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
myService = new Messenger(service);
myCallbackText = (TextView)findViewById(R.id.tv01); //This is a text view which will display status information as needed
myCallbackText.setText("Attached.");
try {
Message msg = Message.obtain(null,
1);
msg.replyTo = mMessenger; //here we send an instance of our messenger implementation as the replyTo address
mService.send(msg);
msg = Message.obtain(null,
3, this.hashCode(), 0);
mService.send(msg); //send a message with the value "3"
} catch (RemoteException e) {
//nothing you can do if the server isn't active
}
Toast.makeText(Service_testActivity.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();//confirmation that the connection happened successfully
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mCallbackText = (TextView)findViewById(R.id.tv01);//same textview as before
mCallbackText.setText("Disconnected.");
Toast.makeText(Service_testActivity.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
Code in the service:
In the service, you will want to have code (very similar to the code in the activity) to receive a message and save the msg.replyTo field as a Messenger object. There is an example somewhere which will have you make an object and then use an IncomingHandler like this:
ArrayList<Messenger> mClients = new ArrayList<Messenger>();
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
default:
super.handleMessage(msg);
}
}
}
This can allow your service to keep track of multiple clients at once and send messages to specified clients. To send a message simply use something like this:
mClients.get(1).send(Message.obtain(null, 3, new Random().nextInt(), 0));
//sends a message to the first client saved in the list

Android Network Application connected to Amazon EC2

I'm kind of new in this kind of development. To be short I'm working on a android app which sends a string to the cloud (I have a virtual server machine on Amazon), everything works well sending the string from my phone to the server machine, I print the string I'm sending and it works!. But when getting the response back from the server to my android app (I'm running it on my android phone) I don't get anything (The response should be a string + another string concatenated), that's it, easy right? But unfortunately I can't receive it back. I tested the server side and It's working properly (The amazon EC2). I'm not really sure if I can do what Im doing which is:
CREATING ransmission code to send the String by using a SOCKET TO SEND DATA TO THE CLOUD ON THE doInBackground() method from the AsyncTask class.
In the same method doInBackground I do the code to receive the response back by using a ServerSocket to receive the response back from the cloud. Is it possible or do I need another thread or something like that?
Here is my code:
`import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.*;
import android.os.AsyncTask;
import android.view.View;
public class ReadWebpageAsyncTask extends Activity {
private TextView textView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textView = (TextView) findViewById(R.id.TextView01);
}
private class DownloadWebPageTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls)
{
String response = "";
//Transmission
try
{
Socket socket = new Socket("MyAmazonServerIp", 5678);
DataOutputStream salida = new DataOutputStream (socket.getOutputStream());
salida.writeUTF("Llego Perfectamente");
socket.close();
salida.close();
}
catch (Exception e)
{
e.printStackTrace();
}
//Final ends
// Reception
boolean ak=true;
try
{
ServerSocket ServerSock = new ServerSocket(7896);
while(ak)
{
Socket cl=ServerSock.accept();
InetAddress ipC = cl.getInetAddress();
DataInputStream en= new DataInputStream(cl.getInputStream());
response= en.readUTF();
//response= response.toString();
ak=false;
}
}
catch(Exception exp)
{
exp.printStackTrace();
}
// Reception ends
return response;
} //doInBackground ends
#Override
protected void onPostExecute(String result)
{
textView.setText(result);
}
}
public void readWebpage(View view) {
DownloadWebPageTask task = new DownloadWebPageTask();
task.execute(new String[] { "????" });
}
}`
I will really appreciate any help since I've been working on this for days and I have not been able to solve it.
Thanks,
The way you are doing it can not work over a 3G or likely even a WiFi connection. It can't work over 3G because the address your Amazon server sees is the address of the proxy/firewall on your cell provider's network. No external application can open a socket directly to your phone. Instead you need to send the response back over the same socket the phone created to send the initial message.
Over WiFi, it will not work because of the NAT translation likely going on with your WiFi firewall.
This is done for many reasons, not the least of which is data usage and security. Specifically, think about if your phone was publicly addressable to the entire Internet? It would constantly be getting hit with attempts to hack it.
So basically, just keep the original socket open and listen for a response on that socket and on your cloud server just send the response back over teh same socket you receive the request on.

Categories

Resources