Running AsyncTask with button performClick - android

I have a fragment that contains a Button btn_connect that when it is pressed a WiFi Direct connection is established between 2 devices. This fragment implements ConnectionInfoListener. So it has onConnectionInfoAvailable function where I want to execute an AsyncTask class. The problem that I have is that in one Activity, I am doing:
fragment.mContentView.findViewById(R.id.btn_connect).performClick();
And the button is being clicked and the connection is established so the code goes into the onConnectionInfoAvailable function but the AsyncTask is not being executed.
#Override
public void onConnectionInfoAvailable(final WifiP2pInfo info) {
//..code..
Log.d("Test 1", "Test 1");
new MasterServerTask().execute();
}
public class MasterServerTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... arg0) {
//**************
Log.d("IM INSIDE ASYNCTASK CLASS", "SOCKET");
try {
serverSocket = new ServerSocket(8090);
} catch (IOException e) {
e.printStackTrace();
}
while (true) {//wait for clients
Socket socket = null;
try {
socket = serverSocket.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.d("ACCEPTED A SLAVE DEVICE "+num_clients, "ACCEPTED A SLAVE DEVICE "+num_clients);
num_clients++;
OutputStream os=null;
try {
os = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
proxy.addSlaveOutputStream(os);
}
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}
mContentView.findViewById(R.id.btn_connect).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {//Phone that connects first is NOT the group owner
// port = Integer.parseInt(editTextPort.getText().toString());
Log.d("IM IN THE OTHER FRAGMENT", "Connect");
WifiP2pConfig config = new WifiP2pConfig();
config.groupOwnerIntent = 0;
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel",
"Connecting to :" + device.deviceAddress, true, true
);
((DeviceActionListener) getActivity()).connect(config);
}
});
Is there an easy workaround solution for this?

Check how/where you are calling WifiP2pManager.initialize() to create the WifiP2pManager.Channel object. The Looper you provide it is the one which will receive all callbacks for your instance of WifiP2pManager.ConnectionInfoListener. If you are giving it a background thread then the AsyncTask will not execute - it must be started from the main (UI) thread.

The comments on the question were really helpful. The reason why the AsyncTask was not getting executed is because it was called from another task that is currently being executed. So in order for it to work, I replaced the AsyncTask with Thread classes. All the code in the doInBackground() was placed inside the thread's run() function. Now the performClick() executes a Thread, not an AsyncTask and it worked.

Related

Android bluetooth: A thread started from UI thread blocks the UI thread

I am learning Android bluetooth programming. I copied most of this code from Google's Android developer website for learning. The idea is listening for connection on server is done in a new thread without blocking the UI thread. When connection request is received then connection is done on another thread and finally communication is done on another thread.
The problem is when I start the listening thread from UI thread, it block automatically and no UI is displayed (freezes). Here is the sample code:
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.activity_main);
...
badapter = BluetoothAdapter.getDefaultAdapter();
if (badapter == null) {
Toast.makeText(this, "No bluetooth device.", Toast.LENGTH_SHORT).show();
return;
}
if (!badapter.isEnabled()) {
Toast.makeText(this, "Bluetooth is disabled.", Toast.LENGTH_SHORT).show();
return;
}
pairedDevices = new HashMap<String, String>();
discoveredDevices = new HashMap<String, String>();
showDevices();
registerBroadcastReceiver();
//this thread blocks UI thread
ListenThread listen = new ListenThread();
listen.run();
}
And the listen thread:
public class ListenThread extends Thread {
MainActivity main;
CommunicateThread communicateThread;
private final BluetoothServerSocket serverSocket;
public ListenThread() {
main = MainActivity.getInstance();
BluetoothServerSocket tmp = null;
try {
tmp = main.badapter.listenUsingRfcommWithServiceRecord(main.NAME, main.MYUUID);
} catch (final IOException e) {
main.handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(main, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
serverSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
//keep listening until exception occurs or a socket is returned
while (true) {
try {
socket = serverSocket.accept();
} catch (final IOException e) {
main.handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(main, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
});
break;
}
// If a connection was accepted
if (socket != null) {
//call communication thread once connection is established
communicateThread = new CommunicateThread(socket);
communicateThread.run();
try {
serverSocket.close();
} catch (final IOException e) {
main.handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(main, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
break;
}
}
}
}
You are calling listen.run() on the main thread which makes it run on the main thread. You should call listen.start() which will spawn off a separate thread where the run() method will be executed.
The Runnable given to the handler will be executed on the main thread though as the Handler is for the main thread.
I had the same problem. What I understand is that every time you make a hardware call, in this case, the Bluetooth, you should do it in another thread. I moved the isEnabled() call to other thread and it solved the problem.

onProgressUpdate only called at the end of AsyncTask (socket creation)

I'm trying to display a "waiting" dialog while the connection is being established by the socket. Do you have any idea why this bit of code is not working ?
onProgressUpdate gets called at the end of the doInBackground. I intend to show it before connectionSocket.connect times out.
The following bit
dialog = connection.dialog("progress");
dialog.show();
works well on its own!
#Override
protected Boolean doInBackground(String... ip) {
Log.i("CONNECTION","doInBackground : Creating socket");
Boolean result = false;
try {
publishProgress();
connectionSocket = new Socket();
connectionSocket.connect(new InetSocketAddress(ip[0], connection.getServerPort()), 5000);
publishProgress();
Log.i("CONNECTION","doInBackground : Socket created");
result = true;
} catch (UnknownHostException e) {
Log.i("CONNECTION","doInBackground : Error creating socket. UnknownHostException");
} catch (IOException ioe) {
Log.i("CONNECTION","doInBackground : Error creating socket. IOException");
}
return result;
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
Log.i("CONNECTION","onProgressUpdate");
dialog = connection.dialog("progress");
dialog.show();
}
Thank you for helping :*
You need to show the dialog in onPreExecute, instead of onProgressUpdate. OnProgressUpdate is to be used for long operations where you have specific percentage updates on the task at hand.
#Override
protected void onPreExecute() {
dialog = connection.dialog("progress");
dialog.show();
}

using AsyncTask for socket

I have this android code:
private class myTask extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
try {
client = new Socket("192.168.1.2", 4444);
oos = new ObjectOutputStream(client.getOutputStream());
ois = new ObjectInputStream(client.getInputStream());
oos.writeUTF("LOGIN");
oos.flush();
String emailText = email.getText().toString();
oos.writeUTF(emailText);
oos.flush();
String passwordText = password.getText().toString();
oos.writeUTF(passwordText);
oos.flush();
string = ois.readUTF();
}catch (ConnectException e){
return "Host not found";
}catch (IOException e) {
return "Exception Caught";
}
return null;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if ("Host not found".equalsIgnoreCase(result)){
Toast.makeText(getApplicationContext(), "Host not found" ,Toast.LENGTH_LONG).show();
}else if("Exception Caught".equalsIgnoreCase(result)){
Toast.makeText(getApplicationContext(), "Connection error" ,Toast.LENGTH_LONG).show();
}else{
Toast.makeText(getApplicationContext(), "Connection established" ,Toast.LENGTH_LONG).show();
}
Toast.makeText(getApplicationContext(), string ,Toast.LENGTH_LONG).show();
if(string.equals("Login successfully Done!")){
startActivity(new Intent(Login.this, User.class));
}
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
register = (TextView)findViewById(R.id.register);
login = (Button)findViewById(R.id.login);
email = (EditText)findViewById(R.id.email);
password = (EditText)findViewById(R.id.password);
connection = (TextView)findViewById(R.id.connection);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
register.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View arg0) {
startActivity(new Intent(Login.this, Register.class));
}
});
login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new myTask().execute();
}
});
}
but my connection create everytime i click on login button. I want my app connect to server at start and then when i press login button it sends request to server..How i need to change my code?
thanks
I think the easiest way to do that would be to move away from AsyncTasks and move to Threads. Create a new Thread in your onCreate function that creates the socket and connects it, then have it wait for a notification from the UI thread (easiest way to do this is to wait on a semaphore or message queue). Then have the onClick of the button notify the socket thread that it needs to login. With this design, you'll have it connect at startup but not login until you request it.
Your biggest worry here is if your server times out your connection for inactivity. But I assume you have control of the server to prevent that.
The problem here is that you're creating the socket in your doInBackground() method. This means that every time you execute an instance of AsyncTask that socket will be recreated. What you probably want to do is subclass Application or some sort of singleton and initialize your socket in there. Then make your AsyncTask do something like:
doInBackground(Object... params) {
// do stuff
Socket socket = Application.getSocket();
// do stuff with the socket
}
This introduces a bit of complexity as you need to handle properly opening/closing your socket at the proper points in your application lifecycle, but it is the cleanest solution that comes to mind.

AsyncTask.get() no progress bar

My app sends data to the server. It generally works fine until the user is in a bad signal area. If the user is in a good signal area the the following code works fine and the data is sent.
String[] params = new String[]{compID, tagId, tagClientId, carerID,
formattedTagScanTime, formattedNowTime, statusForWbService, getDeviceName(), tagLatitude, tagLongitude};
AsyncPostData apd = new AsyncPostData();
apd.execute(params);
.
private class AsyncPostData extends AsyncTask<String, Void, String> {
ProgressDialog progressDialog;
String dateTimeScanned;
#Override
protected void onPreExecute()
{
// progressDialog= ProgressDialog.show(NfcscannerActivity.this,
// "Connecting to Server"," Posting data...", true);
int buildVersionSdk = Build.VERSION.SDK_INT;
int buildVersionCodes = Build.VERSION_CODES.GINGERBREAD;
Log.e(TAG, "buildVersionSdk = " + buildVersionSdk
+ "buildVersionCodes = " + buildVersionCodes);
int themeVersion;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD) {
themeVersion = 2;
}else{
themeVersion = 1;
}
progressDialog = new ProgressDialog(NfcscannerActivity.this, themeVersion);
progressDialog.setTitle("Connecting to Server");
progressDialog.setMessage(" Sending data to server...");
progressDialog.setIndeterminate(true);
try{
progressDialog.show();
}catch(Exception e){
//ignore
}
};
#Override
protected String doInBackground(String... params) {
Log.e(TAG, "carerid in doinbackground = " + params[3] + " dateTimeScanned in AsyncPost for the duplecate TX = " + params[4]);
dateTimeScanned = params[4];
return nfcscannerapplication.loginWebservice.postData(params[0], params[1], params[2], params[3], params[4],
params[5], params[6], params[7] + getVersionName(), params[8], params[9]);
}
#Override
protected void onPostExecute(String result)
{
super.onPostExecute(result);
try{
progressDialog.dismiss();
}catch(Exception e){
//ignore
}
if( result != null && result.trim().equalsIgnoreCase("OK") ){
Log.e(TAG, "about to update DB with servertime");
DateTime sentToServerAt = new DateTime();
nfcscannerapplication.loginValidate.updateTransactionWithServerTime(sentToServerAt,null);
nfcscannerapplication.loginValidate.insertIntoDuplicateTransactions(dateTimeScanned);
tagId = null;
tagType = null;
tagClientId = null;
//called to refresh the unsent transactions textview
onResume();
}else if(result != null && result.trim().equalsIgnoreCase("Error: TX duplicated")){
Log.e(TAG, "response from server is Duplicate Transaction ");
//NB. the following time may not correspond exactly with the time on the server
//because this TX has already been processed but the 'OK' never reached the phone,
//so we are just going to update the phone's DB with the DupTX time so the phone doesn't keep
//sending it.
DateTime sentToServerTimeWhenDupTX = new DateTime();
nfcscannerapplication.loginValidate.updateTransactionWithServerTime(sentToServerTimeWhenDupTX,null);
tagId = null;
tagType = null;
tagClientId = null;
}else{
Toast.makeText(NfcscannerActivity.this,
"No phone signal or server problem",
Toast.LENGTH_LONG).show();
}
}
}//end of AsyncPostData
.
The app in bad signal areas tends to show the progress bar for a few minutes before showing a black screen for a while rendering the app unusable.
I thought a way around this would be to do the following.
String[] params = new String[]{compID, tagId, tagClientId, carerID,
formattedTagScanTime, formattedNowTime, statusForWbService, getDeviceName(), tagLatitude, tagLongitude};
AsyncPostData apd = new AsyncPostData();
try {
apd.execute(params).get(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
This will cause the AsyncTask to cancel after 10 seconds, but as it is executing there is a black screen until the data is sent followed by the progressbar for a few millisecs.
Is there a way to show the progressbar whilst executing an AsyncTask.get()?
thanks in advance. matt.
Also are there any ideas why the black screen comes when the user is in bad signal area and therefor no response from the server. This senario seems to cause the app alot of problems where it's behavior is unusual afterwards like sending extra transactions at a later date.
[edit1]
public class SignalService extends Service{
NfcScannerApplication nfcScannerApplication;
TelephonyManager SignalManager;
PhoneStateListener signalListener;
private static final int LISTEN_NONE = 0;
private static final String TAG = SignalService.class.getSimpleName();
#Override
public void onCreate() {
super.onCreate();
// TODO Auto-generated method stub
Log.e(TAG, "SignalService created");
nfcScannerApplication = (NfcScannerApplication) getApplication();
signalListener = new PhoneStateListener() {
public void onSignalStrengthChanged(int asu) {
//Log.e("onSignalStrengthChanged: " , "Signal strength = "+ asu);
nfcScannerApplication.setSignalStrength(asu);
}
};
}
#Override
public void onDestroy() {
super.onDestroy();
// TODO Auto-generated method stub
Log.e(TAG, "SignalService destroyed");
SignalManager.listen(signalListener, LISTEN_NONE);
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
// TODO Auto-generated method stub
Log.e(TAG, "SignalService in onStart");
SignalManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
SignalManager.listen(signalListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTH);
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
You do not need a timer at all to do what you're attempting (for some reason I thought you were trying to loop the AsyncTask based on your comments above which resulted in mine.). If I understand correctly you're issue is with the loss of service. You have an AsyncTask that you start which may or may not finish depending on certain conditions. Your approach was to use get and cancle the task after a fixed time in the event that it did not finish executing before then - the assumption being if the task didn't finish within the 10 second cut off, service was lost.
A better way to approach this problem is to use a boolean flag that indcates whether network connectivity is available and then stop the task from executing if service is lost. Here is an example I took from this post (I apologize for the formatting I'm on a crappy computer with - of all things - IE8 - so I can't see what the code looks like).
public class MyTask extends AsyncTask<Void, Void, Void> {
private volatile boolean running = true;
private final ProgressDialog progressDialog;
public MyTask(Context ctx) {
progressDialog = gimmeOne(ctx);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
// actually could set running = false; right here, but I'll
// stick to contract.
cancel(true);
}
});
}
#Override
protected void onPreExecute() {
progressDialog.show();
}
#Override
protected void onCancelled() {
running = false;
}
#Override
protected Void doInBackground(Void... params) {
while (running) {
// does the hard work
}
return null;
}
// ...
}
This example uses a progress dialog that allows the user to cancle the task by pressing a button. You're not going to do that but rather you're going to check for network connectivty and set the running boolean based on whether your task is connected to the internet. If connection is lost - running will bet set to false which will trip the while loop and stop the task.
As for the work after the task complete. You should NEVER use get. Either (1) put everything that needs to be done after the doInBackgroundCompletes in onPostExecute (assuming its not too much) or (2) if you need to get the data back to the starting activity use an interface. You can add an interface by either adding as an argument to your tasks constructor or using a seperate method that sets the interface up. For example
public void setInterface(OnTaskComplete listener){
this.listener = listener;
}
Where OnTaskComplete listener is declared as an instance variable in your AsyncTask. Note the approach I am describing requires using a seperate AsyncTask class. Your's is private right now which means you need to change your project a little.
UPDATE
To check connectivity I would use something like this.
public boolean isNetworkOnline() {
boolean status=false;
try{
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getNetworkInfo(0);
if (netInfo != null && netInfo.getState()==NetworkInfo.State.CONNECTED) {
status= true;
}else {
netInfo = cm.getNetworkInfo(1);
if(netInfo!=null && netInfo.getState()==NetworkInfo.State.CONNECTED)
status= true;
}
}catch(Exception e){
e.printStackTrace();
return false;
}
return status;
}
You can check to see if there is an actual network connection over which your app can connect to ther server. This method doesn't have to be public and can be part of you're AsyncTask class. Personally, I use something similar to this in a network manager class that I use to check various network statistics (one of which is can I connect to the internet).
You would check connectivity before you started executing the loop in your doInBackground method and then you could periodicly update throughout the course of that method. If netowkr is available the task will continue. If not it will stop.
Calling the AsyncTask built in cancle method is not sufficient becuase it only prevent onPostExecute from running. It does not actually stop the code from execting.

how can I chnage a TextViews text from a thread?

I'm trying to write code to pull a server every second for updated messages. The messages then get displayed in a text view. If I do not change the text in the text view it runs fine. It will crash if I try to change the textview on the thread. IF i change it not on the thread works fine.
I'm assuming the thread cannot access the main threads memory? How can I set the text in the view with the text just loaded over the internet?
In the code below I have a thread that does a endless loop with a sleep. It calls a method called SendMessage. Send Message loads in text over the internet and at the end tries to update the View with it. It causes a exception when this happens.
code:
public class cChat extends cBase implements OnClickListener {
/** Called when the activity is first created. */
TextView mUsers;
TextView mComments;
int i=0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat);
mUsers=( TextView) findViewById(R.id.viewusers);;
mComments=( TextView) findViewById(R.id.viewchats);;
Thread thread = new Thread()
{
#Override
public void run() {
try {
int t=0;
while(true)
{
SendMessage();
sleep(1000*5);
t++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
}
public void onClick(View v) {
} // end function
// send a uypdate message to chat server
// return reply in string
void SendMessage()
{
try {
URL url = new URL("http://50.63.66.138:1044/update");
System.out.println("make connection");
URLConnection conn = url.openConnection();
// set timeouts to 5 seconds
conn.setConnectTimeout(1000*5);
conn.setReadTimeout(5*1000);
conn.setDoOutput(true);
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// String line;
String strUsers=new String("");
String strComments=new String("");
String line=new String();
int state=0;
while ((line= rd.readLine() ) != null) {
switch(state){
case 0:
if ( line.contains("START USER"))
state=1;
if ( line.contains("START COMMENTS"))
state=2;
break;
case 1:
if ( line.contains("END USER"))
state=0;
else
{
strUsers+=line;
strUsers+="\n";
}
break;
case 2:
if ( line.contains("END COMMENTS"))
state=0;
else {
strComments+=line;
strComments+="\n";
}
break;
} // end switch
} // end loop
// the next line will cause a exception
mUsers.setText(strUsers);
mComments.setText(strComments);
} catch (Exception e) {
i++; // use this to see if it goes here in debugger
// System.out.println("exception");
// System.out.println(e.getMessage());
}
} // end methed
}
use runOnUiThread as
YOUR_CURRENT_ACTIVITY.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// the next line will cause a exception
mUsers.setText(strUsers);
mComments.setText(strComments);
//....YOUR UI ELEMENTS
}
});
EDIT :
see doc runOnUiThread
You can use a handler to post tasks (Runnables) to the UI/Main Thread:
private Handler handler = new Handler();
//...
Thread thread = new Thread()
{
#Override
public void run() {
try {
int t=0;
while(true)
{
handler.post(new Runnable() {
#Override
public void run() {
SendMessage();
}
});
sleep(1000*5);
t++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
You can't touch an UI widget from a thread different than the one used to create it (the UI thread). But if you have a reference to the Activity, you can simply do:
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
mUsers.setText(strUsers);
mComments.setText(strComments);
}
});
which would require strUsers to be accessible by the anonymous class. For that you can simply do:
final String finalUseres = strUsers;
and use finalUsers within run().
Try using a Service to continuously pull/send data to server. This will reduce the load on your UI-Thread.
the Andoid UI toolkit is not thread-safe. So, you
must not manipulate your UI from a worker thread
To fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
you can also use AsyncTask.
see this tutorial on process and threads in android.

Categories

Resources