Im trying to evaluateJavascript query with blocking function in my webview. Meaning function thread awaits result. However, evaluateJavascript and ValueCallback are both called on main thread, and main thread is paused awaiting for result, meaning result can never be caught with await. Here is my example,
private String getFirstUser(){
String evS = "document.getElementsByClassName(\"hm-user\")[0].innerHTML";
final CountDownLatch cdl = new CountDownLatch(1);
final StringBuilder sb = new StringBuilder();
try{
evaluateJavascript(evS, new ValueCallback<String>() {
#Override
public void onReceiveValue(String value) {
if(value != null && value.length() != 0 && !value.equals("null")){
sb.append(value);
}
cdl.countDown();
}
});
cdl.await(200, TimeUnit.MILLISECONDS);
}catch (Exception e){};
return sb.length() == 0 ? null : sb.toString();
}
What happens is, callback does not happen until await is unblocked, meaning function always return null?
What am I doing wrong? I have used this for http requests and it worked.
Edit: I am aware of similiar threads like Android main thread blocking WebView thread
however, there are no solutions available.
Since the call onReceiveValue won't be synchronous and would involve blocking your main Thread in case you want to make it synchronous, I would recommend you to avoid doing it. Instead of trying to implement it as synchronous call, implement callbacks and let it continue as asynchronous.
You can achieve it as follows:
private void getFirstUser(final ValueCallback<String> valueCallback){
String evS = "document.getElementsByClassName(\"hm-user\")[0].innerHTML";
final StringBuilder sb = new StringBuilder();
try{
evaluateJavascript(evS, valueCallback);
}catch (Exception e){
valueCallback.onReceiveValue(null);// You can pass any value instead of null.
};
}
While calling method simply do:
getFirstUser(new ValueCallback<String>() {
#Override
public void onReceiveValue(String value) {
if(value != null && value.length() != 0 && !value.equals("null")){
//do something with proper value
}else{
// take necessary action if value is null
}
}
});
Related
First off, let me say that I'm just starting my Android adventure and am learning on the code posted below.
So i have a Zebra barcode scanner and an Android device, which is supposed to handle the scanned barcodes. The two devices communicate with each other via BT connection (I got it working). Scanned barcodes are being handled by JsonObjectRequest (also working). Depending on the response (or lack of) from external service, scanner has to react in a certain way:
green/red LED on - beeper - green/red LED off
And here is where I am struggling:
If I have only beeper - everything works. If I have a LED on/off - only LED on works. If I have all 3 actions - none gets executed.
Now, strange thing is, that debugger shows those actions received and executed
D/MainActivity: Barcode Received
I/ViewRootImpl: CPU Rendering VSync enable = false
I/BluetoothScanner: executeCommand started. opcode = DCSSDK_SET_ACTION inXML = <inArgs><scannerID>5</scannerID><cmdArgs><arg-int>45</arg-int></cmdArgs></inArgs>
I/BluetoothScanner: 7 SSI bytes sent: 0x05 0xE7 0x04 0x00 0x04 0xFF 0x0C
I/BluetoothScanner: executeCommand returningDCSSDK_RESULT_SUCCESS
I/ViewRootImpl: CPU Rendering VSync enable = false
I/BluetoothScanner: executeCommand started. opcode = DCSSDK_SET_ACTION inXML = <inArgs><scannerID>5</scannerID><cmdArgs><arg-int>17</arg-int></cmdArgs></inArgs>
I/BluetoothScanner: 7 SSI bytes sent: 0x05 0xE6 0x04 0x00 0x11 0xFF 0x00
I/BluetoothScanner: soundBeeper command write successful. Wait for Status.
executeCommand returningDCSSDK_RESULT_SUCCESS
I/ViewRootImpl: CPU Rendering VSync enable = false
I/BluetoothScanner: executeCommand started. opcode = DCSSDK_SET_ACTION inXML = <inArgs><scannerID>5</scannerID><cmdArgs><arg-int>46</arg-int></cmdArgs></inArgs>
I/BluetoothScanner: 7 SSI bytes sent: 0x05 0xE8 0x04 0x00 0x04 0xFF 0x0B
executeCommand returningDCSSDK_RESULT_SUCCESS
Code, that I am using to construct those requests is based on an example app and documentation provided by Zebra see here the Zebra Android SDK and this is how I am calling those actions:
private class MyAsyncTask extends AsyncTask<String,Integer,Boolean> {
int scannerId;
StringBuilder outXML;
DCSSDKDefs.DCSSDK_COMMAND_OPCODE opcode;
private CustomProgressDialog progressDialog;
public MyAsyncTask(int scannerId, DCSSDKDefs.DCSSDK_COMMAND_OPCODE opcode){
this.scannerId=scannerId;
this.opcode=opcode;
this.outXML = outXML;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new CustomProgressDialog(MainActivity.this, "Execute Command...");
progressDialog.show();
}
#Override
protected Boolean doInBackground(String... strings) {
return executeCommand(opcode,strings[0],null,scannerId);
}
#Override
protected void onPostExecute(Boolean b) {
super.onPostExecute(b);
if (progressDialog != null && progressDialog.isShowing())
progressDialog.dismiss();
if(!b){
Toast.makeText(MainActivity.this, "Cannot perform the Action", Toast.LENGTH_SHORT).show();
}
}
}
#Override
public boolean executeCommand(DCSSDKDefs.DCSSDK_COMMAND_OPCODE opCode, String inXML, StringBuilder outXML, int scannerID) {
if (Application.sdkHandler != null)
{
if(outXML == null){
outXML = new StringBuilder();
}
DCSSDKDefs.DCSSDK_RESULT result=Application.sdkHandler.dcssdkExecuteCommandOpCodeInXMLForScanner(opCode,inXML,outXML,scannerID);
if(result== DCSSDKDefs.DCSSDK_RESULT.DCSSDK_RESULT_SUCCESS)
return true;
else if(result==DCSSDKDefs.DCSSDK_RESULT.DCSSDK_RESULT_FAILURE)
return false;
}
return false;
}
private final Handler dataHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
switch(msg.what){
case Constants.BARCODE_RECEIVED:
Barcode barcode = (Barcode) msg.obj;
sendApiHttpRequest(new String(barcode.getBarcodeData()));
break;
}
return false;
}
});
private void sendApiHttpRequest(String ticketId){
String url = "https://#################################/" + ticketId;
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, null, myJsonListener(), myJsonErrorListener());
// tag the request for ease of debugging
jsonObjectRequest.setTag(TAG);
// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest);
}
private Response.Listener<JSONObject> myJsonListener() {
return new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
boolean status;
try {
status = response.getBoolean("status");
if (status){
setScanResultOK();
}else{
setScanResultERR();
}
}catch(JSONException e){
setScanResultERR();
Log.e(TAG, "Failure", e);
}
}
};
}
private Response.ErrorListener myJsonErrorListener() {
return new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
setScanResultERR();
Log.i(TAG, "Error : " + error.getLocalizedMessage());
}
};
}
private void setScanResultOK(){
prepareInXML(RMDAttributes.RMD_ATTR_VALUE_ACTION_LED_GREEN_ON);
prepareInXML(RMDAttributes.RMD_ATTR_VALUE_ACTION_FAST_WARBLE_BEEP);
prepareInXML(RMDAttributes.RMD_ATTR_VALUE_ACTION_LED_GREEN_OFF);
TextView textViewScanResult = findViewById(R.id.txt_scan_result);
textViewScanResult.setText(R.string.scan_res_ok);
textViewScanResult.setTextAppearance(getApplicationContext(), R.style.roboto_medium_96dp_green);
}
private void setScanResultERR(){
prepareInXML(RMDAttributes.RMD_ATTR_VALUE_ACTION_LED_RED_ON);
prepareInXML(RMDAttributes.RMD_ATTR_VALUE_ACTION_LOW_LONG_BEEP_3);
prepareInXML(RMDAttributes.RMD_ATTR_VALUE_ACTION_LED_RED_OFF);
TextView textViewScanResult = findViewById(R.id.txt_scan_result);
textViewScanResult.setText(R.string.scan_res_err);
textViewScanResult.setTextAppearance(getApplicationContext(), R.style.roboto_medium_96dp_red);
}
private void performOpcodeAction(String inXML) {
if (scannerID != -1) {
new MyAsyncTask(scannerID, DCSSDKDefs.DCSSDK_COMMAND_OPCODE.DCSSDK_SET_ACTION).execute(new String[]{inXML});
} else {
Toast.makeText(this, "Invalid scanner ID", Toast.LENGTH_SHORT).show();
}
}
private void prepareInXML(int value){
String inXML = "<inArgs><scannerID>" + scannerID + "</scannerID><cmdArgs><arg-int>" +
value + "</arg-int></cmdArgs></inArgs>";
performOpcodeAction(inXML);
}
When I set up breakpoints and step through the code, all actions are executed and as soon as I run the app, I get those issues.
Can anyone please help me?
Here's what I understood from your code. Your are sending an HTTP request to a server and based on the response, your are going to perform a sequence of events such as LED and sound state toggling.
As a background, Asynctask is used to execute a piece of code on a background thread. In your case, you want to perform the commands. As from the name, they are asynchronous and will run in parallel with your main thread (at least the doInBackground).
In setScanResultOK and setScanResultERR, you are potentially instantiating 3 asynctasks. Only one of them will run as by default, asynctasks run on a single execution thread. If you want to run them altogether, execute them in a thread pool executor.
Now, you mentioned that you want to run them in sequence. I propose to refactor your code as such.
Create 2 asynctasks, 1 for success and 1 for error.
Perform the multiple prepareInXML calls in doInBackground
Instantiate an asynctask based on the response, and execute.
As an example:
#Override
protected Boolean doInBackground(String... strings) {
if (!prepareInXML(RMDAttributes.RMD_ATTR_VALUE_ACTION_LED_GREEN_ON)) {
return false;
}
if (!prepareInXML(RMDAttributes.RMD_ATTR_VALUE_ACTION_FAST_WARBLE_BEEP)) {
return false;
}
return prepareInXML(RMDAttributes.RMD_ATTR_VALUE_ACTION_LED_GREEN_OFF);
}
Of course this will require you to change some function signature to accommodate. Then process all UI changes in onPostExecute.
#Override
protected void onPostExecute(Boolean b) {
super.onPostExecute(b);
if (progressDialog != null && progressDialog.isShowing())
progressDialog.dismiss();
if(!b){
Toast.makeText(MainActivity.this, "Cannot perform the Action", Toast.LENGTH_SHORT).show();
}
/* perform UI changes particular to the type of task (success/fail) */
}
UPDATE:
Try adding a delay in between commands. The no op might be something that is actually a on-sound-off sequence happening real fast for us to notice.
For my app I have to run two operations, both being asynchronous:
read from a file ( I use this file to simulate reading from a data bus ) - async operation because I don't know "when" arrive a new
message/character on the bus. I search for a specific sequence
character, eg frame start_bytes = "xx" and the 4 following bytes are
"the data" I wait for.
read / update data to Firebase, depending on the "data" read from file - async operation due to addValueEventListener use.
I'm thinking a semaphore/mutex mechanism or a simple boolean flag that one task signal to the other one that a new data must be saved/updated to Firebase.
How can I synchronize these two operations ( by embedding them in a Task / AsyncTask / Thread)?
I ran a search for these topics but I found examples related to UI, ProgressBars and so on .. not really suited/useful to my situation.
read / update data in Firebase
myRefDevices.addValueEventListener(new ValueEventListener() {
// addValueEventListener
// This method is called once with the initial value and again
// whenever data at this location is updated.
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
boolean bChildFound = false;
DatabaseReference dbrefChildFound;
final CDeviceStatus obj_new = new CDeviceStatus();
for( DataSnapshot val : dataSnapshot.getChildren() )
{
if( val.getKey().contentEquals(MAC_ADDRESS[ iIterator ]) )
{
bChildFound = true;
dbrefChildFound = val.getRef();
obj_new.setiAvailable_A( val.getValue( CDeviceStatus.class ).getiAvailable_A() + 1 );
obj_new.setsID(val.getValue( CDeviceStatus.class).getsID() );
dbrefChildFound.setValue(obj_new);
}
}
if(!bChildFound)
{
Log.d("child=" + MAC_ADDRESS[ iIterator ], "not found");
}
if(++iIterator == 16)
{
iIterator = 0;
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
read from file :
try {
// open input stream text file for reading
Resources res = getResources();
InputStream instream = res.openRawResource( R.raw.simulated_bus );
// we convert it to bufferred input stream
BufferedInputStream bistreamSimulatedBus = new BufferedInputStream(instream);
try{
// if we want to stop reading from the file / simulated bus for whatever reason..
boolean bStayInLoop = true;
while ((bistreamSimulatedBus.available() > 0) && bStayInLoop)
{
try {
// throw new InterruptedException();
char c = (char) bistreamSimulatedBus.read();
if( COUNT_CHARACTERS_NEWLINE )
{
if ( '\n' == c ){
// we can count how much NewLine character we have
//iNL_Counter++;
}
}
...
}
catch ( InterruptedException e ) {
throw new RuntimeException( e );
}
}
} catch (IOException e) {
throw new RuntimeException( e );
}
finally {
// release any resource associated with streams
if ( null != instream ) {
instream.close();
}
if ( null != bistreamSimulatedBus ) {
bistreamSimulatedBus.close();
}
}
}
catch (Exception e) {
throw new RuntimeException( e );
}
Thank you.
Let us break the solution like this:
The basics
You have two operations : o1 and o2. You want the second operation to execute as soon as the first one has completed.
It clearly appears to me that you need an event-driven solution here.
Approach
Using the concept of Publisher/Subscriber design pattern, you can make the Initiator of o1 be the Publisher of an event. Then, when this particular operation o1 is completed, let the class (activity, fragment, service) notify the other class which we will call Subscriber.
Code
Add the following line to your build.gradle (app-level):
compile 'org.greenrobot:eventbus:3.0.0'
Then, simply create a simple Plain Old Java Object (POJO) that represents your event.
public class RequestCompletedEvent{ // add constructor and anything you want}
Next, to Publish the event, you simply call the post(POJO instance) like this:
EventBus.getDefault().post(new RequestCompletedEvent(true));
Then, finally, in the Subscriber class, simply listen for notifications by adding the following lines of code:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
Then still within the same class, use the Subscribe annotation to catch any signals:
#Subscribe
public void onEvent(RequestCompletedEvent event) {
/* Do something */
//trigger the second operation here;
startOperationTwo();
}
Summary
It would help to note here that the easiest way to pull this off is to use an async task (AsyncTask sub class) to read your files, then when successfully done, inside onPostExecute(), you can notify the Subscriber to initiate the next operation.
I hope this helps; and good luck! Let me know if you need further assistance!
I am developing an android application that consumes web service , the service output is XML
I am connecting to the web service using this code
public String converse(String host, int port, String path)
throws IOException, URISyntaxException {
BufferedReader in = null;
String serviceResponse = null ;
try {
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet();
String serviceUrl = "http://"+host+":"+port+path;
System.out.println("service url "+serviceUrl);
request.setURI(new URI(serviceUrl));
System.out.println("Request "+request.toString());
HttpResponse response = client.execute(request);
in = new BufferedReader
(new InputStreamReader(response.getEntity().getContent()));
StringBuffer sb = new StringBuffer("");
String line = "";
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();
serviceResponse = sb.toString();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return serviceResponse;
}
when the application launches with WI-FI every thing works fine and when I restart the application with 3G connection it hangs and displays a the following dialog
In addition to this code I am using this method inside another method
public void fillAdapter() {
//calling web service using the mentioned method
}
And this function used inside an async task to fill ListView adapter
protected class AsyncLoading extends AsyncTask<Void,Void, BaseModel[]>{
#Override
protected void onPreExecute(){
pd =ProgressDialog.show(BaseListActivity.this, "loading in progress", "waiting .");
}
#Override
protected BaseModel[] doInBackground(Void... params) {
fillAdapter();
return listItems;
}
#Override
protected void onPostExecute(BaseModel[] doc){
//list.setAdapter(doc);
if(doc != null) {
if(doc.length > 0 ) {
if(doc[0] instanceof Activity)
adapter = new OffersListAdapter(getApplicationContext(),doc);
else if (doc[0] instanceof Offer)
adapter = new OffersListAdapter(getApplicationContext(),doc);
else if (doc[0] instanceof Branch) {
adapter = new BranchListAdapter(getApplicationContext(),doc);
Log.i("Branch"," Added Branch");
}else if (doc[0] instanceof Consolation) {
adapter = new ListAdapter(getApplicationContext(),doc);
adapter.setDisplayImage(false);
}else if ( doc[0] instanceof Event) {
adapter = new EventListAdapter(getApplicationContext(),doc);
}
else
adapter = new ListAdapter(getApplicationContext(),doc);
}//end if doc != null
list.setAdapter(adapter);
}
handler.sendEmptyMessage(0);
}
I saw this post but I don't have a good result I'm working on this problem for 2 days
with my thanks in advance .
Note : this problem often appears the first time the application connects to the service after that if I pressed wait and the application continued then al other activities consuming the web service will work fine
I think you have low speed connection of 3G compared to Wi-Fi.So Use Asynctask to load data from server in seperate thread rather than main thread.It is good idea to show ProgressDialog while fetching the data.
And In some cases Apis will work in Wi-Fi and may not in 3G connection.So test Your url in Device browser also to make it confirm
You should use an AsyncTask for every task that could take some time to be executed. See http://developer.android.com/reference/android/os/AsyncTask.html
The problem i am seeing with your code is you are not doing network operation in Thread.
So if you perform asynchronous operation on Main thread,application will display above dialog if it don't receive response in 5 seconds.In your case 3g connection may be taking more than 5 seconds to return response.
Best bet is include above code in Thread!!
I have solved the problem , that I have a splash screen activity inside which I use C2DM and register the device in that activity using service
registrationIntent.putExtra("app", PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(), 0));
registrationIntent.putExtra("sender", "someemailaddress#gmail.com");
startService(registrationIntent);
I put this code inside an asyncTask and didn't block the UI
Thanks for every one who tried to help me
I tried to refer similar question on SO, but didn't got any help.
In my android app, I'm planning to implement Recent Quote the user has visited i.e. similar to recently visited pages on web.
Following are the steps I'm following:
1.) Whenever user opens any company view, fetch the company symbols from database
2.) Then store the current symbol along with dateTime in database.
3.) For all symbols fetched from database, Fetch their current value and %Change and display Company name, current value and %Change in a list.
The problem arises in the ASyncTask class as postExecute method doesn't allow it's return type to be any other than void.
Am I doing anything wrong?
Any help will be life saver !!!
String[] rsym,rcmp,rchg;
rdbM = new RecentDBManager(CompanyView.this);
try {
Calendar date1 = Calendar.getInstance();
SimpleDateFormat dateformatter = new SimpleDateFormat(
"dd/MM/yyyy HH:mm:ss");
String currentdate = dateformatter.format(date1.getTime());
rdbM.openDB();
//STEP 1
rsym = rdbM.getRecent_sym();
//STEP 2
rdbM.setData(currentsymbol, currentdate);
rdbM.closeDB();
} catch (Exception e) {
throw new Error(" *** ERROR in DB Access *** "+ e.toString());
}
//STEP 3
for(int i=0;i<rsym.length;i++)
{
DownloadRecentQuote quotetask = new DownloadRecentQuote();
recentquotetask
.execute(new String[] { "http://abc.com/stockquote.aspx?id="
+ rsym[i] });
//CURRENT VALUE and %CHANGE which should be returned from ASyncTask class
rcmp[i]=valuearr[0];
rchg[i]=valuearr[1];
}
list1 = new ArrayList<HashMap<String, String>>();
HashMap<String, String> addList1;
for (int i = 0; i < limit; i++)
{
addList1 = new HashMap<String, String>();
addList1.put(RecentSym_COLUMN, rsym[i]);
addList1.put(RecentCMP_COLUMN, rcmp[i]);
addList1.put(RecentChg_COLUMN, rchg[i]);
list1.add(addList1);
RecentAdapter adapter1 = new RecentAdapter(
CompanyView.this, CompanyView.this, list1);
listrecent.setAdapter(adapter1);
}
private class DownloadRecentQuote extends AsyncTask<String, Void, String> {
/* Fetching data for RecentQuote information */
#Override
protected String doInBackground(String... urls) {
String response = "";
for (String url : urls) {
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse execute = client.execute(httpGet);
InputStream content = execute.getEntity().getContent();
BufferedReader buffer = new BufferedReader(
new InputStreamReader(content));
String s = "";
while ((s = buffer.readLine()) != null) {
response += s;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return response;
}
#Override
protected void onPostExecute(String result) {
arr1 = result.split("#");
if (arr1[0].length() != 0) {
if (arr1[0].equals("1")) {
arr = arr1[1].split(";");
//RETURN 2 STRINGS
String valuearr[];
valuearr[0] = arr[3];
valuearr[1] = arr[6].concat("%");
//return valuearr;
}
}
}
postExecute() can't return a value because who or what would it return to? Your original method that invoked the AsyncTask is gone because your AsyncTask is running in the background. It's asynchronous meaning when AsyncTask.execute() returns it's still running in the background, and hence postExecute() can't return a value because there's nothing to return it to.
Instead your AsyncTask needs a reference back to your Activity or some other object so it can post your values back to it. In your code the lines after you call execute() can't be there because your task hasn't finished. Instead you should create a method called updateSymbol( currentPrice, percentChange), move all that code below execute() in there, and in your AsyncTask you should pass a reference to the Activity. Then call updateSymbol( currentPrice, percentChange ) from the onPostExecute() method.
But, be careful if you have a reference back to an Activity it can be destroyed while your doInBackground() is running, and when postExecute() runs it should just drop the results or not attempt to update the UI. For example, the user rotates their phone causing the Activity to be destroyed. I find it best to hold a reference to the AsyncTask in the activity so it can cancel() it if the Activity is destroyed. You can call AsyncTask.cancel() then check if your task was canceled like:
public void postExecute( String result ) {
if( !isCanceled() ) {
// do your updating here
activity.setSymbol( result );
}
}
It's really easy to create a base class for all Activities so you can easily keep track of AsyncTasks running:
public class BaseActivity extends Activity {
List<AsyncTask> runningTasks;
public void onStop() {
for( AsyncTask task : runningTasks ) {
task.cancel(true);
}
}
public AsyncTask start( AsyncTask task ) {
runningTasks.add( task );
return task;
}
public void done( AsyncTask task ) {
runningTasks.remove( task );
}
}
Some quick pointers. You don't need execute( new String[] { "blah" + blah } ). Varargs in Java allow you to do this. execute( "blah" + blah ). You also are catching exceptions and continuing without really handling them. It will be hard when something really happens because your app catches them, and just continues as if nothing happened. If you get an error you might want to provide some feedback to the user and stop trying to execute that process. Stop, show an error to the user, and let them do the next thing. Move the catch blocks to the bottom of the methods.
Essentially, AsyncTask.onPostExecute() is where you do whatever you want to do after AsyncTask's doInBackground() is executed and the execution result gets returned. This should be considered the best practice.
When AsyncTask().execute() is called from the UI thread (note that this method must be called from the UI thread), the Android framework creates a worker thread and starts running whatever you wrote in AsyncTask.doInBackground() on this worker thread. At this point (after calling new AsyncTask().execute()), the UI thread continues to execute code after new AsyncTask().execute(). So now during run time, you have two threads (UI and worker thread) both running simultaneously.
But where and when does the AsyncTask execution result get returned from the worker thread back to the UI thread?
The point where your worker thread (doInBackground()) finishes and returns to the UI thread is AysncTask.onPostExecute(). This method is guaranteed to be called by the framework on the UI thread as soon as AsyncTask finishes. In other words, we don't care where and when AsyncTask.onPostExecute() gets called at run time, we just need to guarantee it will be called ultimately at some stage in the future. This is the reason why this method does not return an execution result - instead, it requires that the execution result gets passed in as the only method parameter from doInBackground().
In addition, the Android API provides a way to return an AsyncTask execution result at coding time, AsyncTask.get():
MyAsyncTask myAsyncTask = new MyAsyncTask();
// This must be called from the UI thread:
myAsyncTask.execute();
// Calling this will block UI thread execution:
ExecutionResult result = myAsyncTask.get();
Bear in mind that AsyncTask.get() will block the calling thread's execution, and you will probably get an ANR exception if you call it on the UI thread. This is the payload of using AsyncTask.get(), by calling it on the UI thread, you are actually making your AsyncTask (worker thread) run synchronously with UI thread (by making UI thread wait). To sum up, this is doable but not recommended.
Just for future reference, because this post is a little old:
I have created an Activity class which has an onStart() method and a separate class for the AsyncTask. Based on my test, after the doInbackground() method the result will be sent to the activity first and after that onPostExecute() will run. This is because based off of logcat, I have my first response data (sent by server) first, then this response will show again from the activity and the last the message in onPostExecute() will show.
Code for the activity:
#Override
protected void onStart() {
super.onStart();
String str = "***";
if(isConnectedToInternet()){
myAsyncTask.execute();
try {
if(myAsyncTask.get())
str = myAsyncTask.getResponseMsg();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (CancellationException e) {
e.printStackTrace();
}
}
Log.i("Data returned by server2:", str);
}
AsyncTask code:
public class MyAsyncTask extends AsyncTask<Void, Void, Boolean> {
private URL url;
private HttpURLConnection conn;
private String strResponseMsg;
public MyAsyncTask(String url) throws MalformedURLException{
this.url = new URL(url);
}
#Override
protected void onPreExecute() {
Log.i("Inside AsyncTask", "myAsyncTask is abut to start...");
}
#Override
protected Boolean doInBackground(Void... params) {
boolean status = false;
try {
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(Manager.ConnTimeout);
conn.setReadTimeout(Manager.ReadTimeout);
int responseCode = conn.getResponseCode();
Log.i("Connection oppened", "Response code is:" + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
if (in != null) {
StringBuilder strBuilder = new StringBuilder();
// Read character by character
int ch = 0;
while ((ch = in.read()) != -1)
strBuilder.append((char) ch);
// Showing returned message
strResponseMsg = strBuilder.toString();
Log.i("Data returned by server:", strResponseMsg);
status = true;
}
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return status;
}
#Override
protected void onPostExecute(Boolean result) {
Log.i("Inside AsyncTask", "myAsyncTask finished its task. Returning data to caller...");
}
public String getResponseMsg(){
return strResponseMsg;
}
}
I'm using using AsyncTask to download data over internet and I have a little problem. I need to be able to start one AsyncTask a few times, that's why I'm creating a new instance everytime,but the thing that I notice is that it's working without any problem the first three or four times,but after that my AsyncTask is stuck on onPreExecute() and doing nothing after that. Am I doing something wrong ? (Actually I am using two AsyncTasks one after another just for testing purposes). Here is the sample code which I'm using :
this is how I start the AsyncTasks :
if (index == 1) {
//Login - first way
new FirstSync().execute(Synchronization.this);
} else if (index == 2) {
//SyncWithHash - second way
SyncWithHash syncHash = new SyncWithHash();
syncHash.execute(Synchronization.this);
} else if (index == 3) {
//Deactivate Collection - third way
deactivateColl = new DeactivateCollection();
deactivateColl.execute(Synchronization.this);
}
I did try with three different ways to start the asyncTask,but no change. Here is my AsyncTask :
// Sync With Hash
public class SyncWithHash extends AsyncTask <Context, Integer, Void> {
#Override
protected Void doInBackground(Context... arrContext) {
try {
String charset = "UTF-8";
hash = getAuthHash();
SharedPreferences lastUser = PreferenceManager.getDefaultSharedPreferences(Synchronization.this);
int userId = lastUser.getInt("lastUser", 1);
systemDbHelper = new SystemDatabaseHelper(Synchronization.this, null, 1);
systemDbHelper.initialize(Synchronization.this);
String sql = "SELECT dbTimestamp FROM users WHERE objectId=" + userId;
Cursor cursor = systemDbHelper.executeSQLQuery(sql);
if (cursor.getCount() < 0) {
cursor.close();
} else if (cursor.getCount() > 0) {
cursor.moveToFirst();
timeStamp = cursor.getString(cursor.getColumnIndex("dbTimestamp"));
Log.d("", "timeStamp : " + timeStamp);
}
String query = String.format("debug_data=%s&"
+ "client_auth_hash=%s&" + "timestamp=%s&"
+ "client_api_ver=%s&"
+ "set_locale=%s&" + "device_os_type=%s&"
+ "device_sync_type=%s&"
+ "device_identification_string=%s&"
+ "device_identificator=%s&" + "device_resolution=%s",
URLEncoder.encode("1", charset),
URLEncoder.encode(hash, charset),
URLEncoder.encode(timeStamp, charset),
URLEncoder.encode(clientApiVersion, charset),
URLEncoder.encode(locale, charset),
URLEncoder.encode(version, charset),
URLEncoder.encode("14", charset),
URLEncoder.encode(version, charset),
URLEncoder.encode(deviceId, charset),
URLEncoder.encode(resolution, charset));
SharedPreferences useSSLConnection = PreferenceManager
.getDefaultSharedPreferences(Synchronization.this);
boolean useSSl = useSSLConnection.getBoolean("UseSSl", true);
if (useSSl) {
UseHttpsConnection(url, charset, query);
} else {
UseHttpConnection(url, charset, query);
}
} catch (Exception e2) {
e2.printStackTrace();
}
return null;
}
#Override
protected void onProgressUpdate(Integer... progress) {
//cancelDialog.setProgress(progress[0]);
}
#Override
protected void onCancelled() {
Log.d("","ON CANCELLED");
}
#Override
protected void onPreExecute()
{
Log.d("","ON PRE EXECUTE");
// myProgress = 0;
}
#Override
protected void onPostExecute(Void v) {
Log.d("","ON POST EXECUTE");
}
}
So any ideas why it's happening and which is the best way to be able to use an AsyncTask a few times without any exceptions and bugs like the one that I get.
And another question : Is there anything in AsyncTask which can cause my connection to be Reset by peer , because I'm getting this error too (not every time).
Thanks a lot!
I think your doInBackground() is hanging. Make log statement when its entered and when its exited and check.
In the old days AsyncTask had a pool of threads, so if a doInBackground() hung, then it didnt affect the other AsyncTasks. That changed AFAIK with Android 2.2 or 2.3 to that a single thread took care of all AyncTasks, one at a time. Therefore, if your doInBackground() is hanging it might affect the next AsyncTasks being started and the will hang right after onPreExecute().
Edit: It was changed from a single thread, to multiple, and then back to a single thread:
http://developer.android.com/reference/android/os/AsyncTask.html#execute%28Params...%29 "When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. After HONEYCOMB, it is planned to change this back to a single thread to avoid common application errors caused by parallel execution."
If you really want an indefinite lot of stuff to 'hang' in parralel, then don't use AsyncTask. Use good old threads, which, when they need to update the GUI, fire off a Runnable to be runned on the GUI thread:
Button knap1, knap2, knap3;
...
Runnable r=new Runnable() {
public void run() {
// Do some stuff than hangs
try { Thread.sleep(10000); } catch (InterruptedException ex) {}
System.out.println("færdig!");
// Update GUI thread
Runnable r2=new Runnable() {
public void run() {
knap3.setText("færdig!");
}
};
runOnUiThread(r2);
}
};
new Thread(r).start();
(example from http://code.google.com/p/android-eksempler/source/browse/trunk/AndroidElementer/src/eks/asynkron/Asynkron1Thread.java?spec=svn109&r=109)
It might be happening because you are synchronizing on an object "Synchronization.this'.
Also noticed you are not closing the cursor which you opened.