Currently building an Android app and checking it on Genymotion running 4.1.1. I'm using AsyncTask to call the Bing Translate API to translate from text:
class TranslateFacebookText extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... message) {
String translatedText = "";
try {
translatedText = Translate.execute(message[0], Language.AUTO_DETECT, Language.ENGLISH);
} catch (Exception e) {
....
}
return translatedText;
}
#Override
protected void onPostExecute(String translatedText) {
message = translatedText;
confirmTTSData();
}
}
public void onClick(View src) {
TranslateFacebookText translateTask = new TranslateFacebookText();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
translateTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);
}
else {
translateTask.execute(message);
}
}
I'm using this method to start the task after reading this question: Android SDK AsyncTask doInBackground not running (subclass)
I'm doing so, since after about 2-5 minutes from the programs start, the AsyncTask refuses to run. That is, doInBackground does not get called, nor does onPostExecute. the onClick DOES get called, creates the new AsyncTask and runs the execution code, but the doInBackground does not get called.
This is completely random. I'm not doing anything else - just waiting there for a couple of minutes, and afterwards clicking the button again to see this happen. This is also true with a service which runs every specified time using a Handler and postDelayed. Here's an example:
public class MyService extends Service {
private Handler periodicEventHandler;
private final int PERIODIC_EVENT_TIMEOUT = 600000;
#Override
public void onCreate() {
periodicEventHandler = new Handler();
periodicEventHandler.postDelayed(doPeriodicTask, PERIODIC_EVENT_TIMEOUT);
}
private Runnable doPeriodicTask = new Runnable()
{
public void run()
{
TranslateFacebookText translateTask = new TranslateFacebookText();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
translateTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);
}
else {
translateTask.execute(message);
}
periodicEventHandler.postDelayed(doPeriodicTask, PERIODIC_EVENT_TIMEOUT);
}
};
class TranslateFacebookText extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... message) {
String translatedText = "";
try {
translatedText = Translate.execute(message[0], Language.AUTO_DETECT, Language.ENGLISH);
} catch (Exception e) {
....
}
return translatedText;
}
#Override
protected void onPostExecute(String translatedText) {
message = translatedText;
confirmTTSData();
}
}
}
The doPeriodicTask runs fine, again creating the AsyncTask and calling the execution code, but doInBackground never gets called. If I change PERIODIC_EVENT_TIMEOUT to 8000, for example, doInBackground would get called fine.
Ideas?
Related
I followed this post to set up an Http Async Request: HttpRequest
So, now, I call: new DownloadTask().execute("http://www.google.com/");
to make this request.
How can I manage different calls? For example:
new DownloadTask().execute("http://www.google.com/");
new DownloadTask().execute("http://www.facebook.com/");
new DownloadTask().execute("http://www.twitter.com/");
And have different results?
Pass one more argument to the AsyncTask. Make some constants corresponding to your tasks.
new DownloadTask().execute("http://www.google.com/", DownloadTask.ID_ASYNC1);
new DownloadTask().execute("http://www.facebook.com/", DownloadTask.ID_ASYNC2);
new DownloadTask().execute("http://www.twitter.com/", DownloadTask.ID_ASYNC3);
Inside AsyncTask, use this id to identify which is the request being called.
private class DownloadTask extends AsyncTask<String, Void, String> {
//Variable for storing the req id
private int id;
//Constants corresponding to your tasks
public static int ID_ASYNC1 = 0;
static static int ID_ASYNC1 = 0;
static static int ID_ASYNC1 = 0;
#Override
protected String doInBackground(String... params) {
id = params[1]);
//your code
}
#Override
protected void onPostExecute(String result) {
if(id == ID_ASYNC1){
//Do your task #1
} else if(id == ID_ASYNC2){
//Do your task #2
}
}
}
You have to use looper for smooth downloading for multiple file it will download them one by one. In this way your application run smoothly huge huge amount of downloads.
How to use Looper
public class MainActivity extends Activity implements DownloadThreadListener,
OnClickListener {
private DownloadThread downloadThread;
private Handler handler;
private ProgressBar progressBar;
private TextView statusText;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create and launch the download thread
downloadThread = new DownloadThread(this);
downloadThread.start();
// Create the Handler. It will implicitly bind to the Looper
// that is internally created for this thread (since it is the UI
// thread)
handler = new Handler();
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
statusText = (TextView) findViewById(R.id.status_text);
Button scheduleButton = (Button) findViewById(R.id.schedule_button);
scheduleButton.setOnClickListener(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
// request the thread to stop
downloadThread.requestStop();
}
// note! this might be called from another thread
#Override
public void handleDownloadThreadUpdate() {
// we want to modify the progress bar so we need to do it from the UI
// thread
// how can we make sure the code runs in the UI thread? use the handler!
handler.post(new Runnable() {
#Override
public void run() {
int total = downloadThread.getTotalQueued();
int completed = downloadThread.getTotalCompleted();
progressBar.setMax(total);
progressBar.setProgress(0); // need to do it due to a
// ProgressBar bug
progressBar.setProgress(completed);
statusText.setText(String.format("Downloaded %d/%d", completed,
total));
// vibrate for fun
if (completed == total) {
((Vibrator) getSystemService(VIBRATOR_SERVICE))
.vibrate(100);
}
}
});
}
#Override
public void onClick(View source) {
if (source.getId() == R.id.schedule_button) {
int totalTasks = new Random().nextInt(3) + 1;
for (int i = 0; i < totalTasks; ++i) {
downloadThread.enqueueDownload(new DownloadTask());
}
}
}
}
DownloadThread.Class
public final class DownloadThread extends Thread {
private static final String TAG = DownloadThread.class.getSimpleName();
private Handler handler;
private int totalQueued;
private int totalCompleted;
private DownloadThreadListener listener;
public DownloadThread(DownloadThreadListener listener) {
this.listener = listener;
}
#Override
public void run() {
try {
// preparing a looper on current thread
// the current thread is being detected implicitly
Looper.prepare();
Log.i(TAG, "DownloadThread entering the loop");
// now, the handler will automatically bind to the
// Looper that is attached to the current thread
// You don't need to specify the Looper explicitly
handler = new Handler();
// After the following line the thread will start
// running the message loop and will not normally
// exit the loop unless a problem happens or you
// quit() the looper (see below)
Looper.loop();
Log.i(TAG, "DownloadThread exiting gracefully");
} catch (Throwable t) {
Log.e(TAG, "DownloadThread halted due to an error", t);
}
}
// This method is allowed to be called from any thread
public synchronized void requestStop() {
// using the handler, post a Runnable that will quit()
// the Looper attached to our DownloadThread
// obviously, all previously queued tasks will be executed
// before the loop gets the quit Runnable
handler.post(new Runnable() {
#Override
public void run() {
// This is guaranteed to run on the DownloadThread
// so we can use myLooper() to get its looper
Log.i(TAG, "DownloadThread loop quitting by request");
Looper.myLooper().quit();
}
});
}
public synchronized void enqueueDownload(final DownloadTask task) {
// Wrap DownloadTask into another Runnable to track the statistics
handler.post(new Runnable() {
#Override
public void run() {
try {
task.run();
} finally {
// register task completion
synchronized (DownloadThread.this) {
totalCompleted++;
}
// tell the listener something has happened
signalUpdate();
}
}
});
totalQueued++;
// tell the listeners the queue is now longer
signalUpdate();
}
public synchronized int getTotalQueued() {
return totalQueued;
}
public synchronized int getTotalCompleted() {
return totalCompleted;
}
// Please note! This method will normally be called from the download
// thread.
// Thus, it is up for the listener to deal with that (in case it is a UI
// component,
// it has to execute the signal handling code in the UI thread using Handler
// - see
// DownloadQueueActivity for example).
private void signalUpdate() {
if (listener != null) {
listener.handleDownloadThreadUpdate();
}
}
}
DownloadTask.Class
public class DownloadTask implements Runnable {
private static final String TAG = DownloadTask.class.getSimpleName();
private static final Random random = new Random();
private int lengthSec;
public DownloadTask() {
lengthSec = random.nextInt(3) + 1;
}
#Override
public void run() {
try {
Thread.sleep(lengthSec * 1000);
// it's a good idea to always catch Throwable
// in isolated "codelets" like Runnable or Thread
// otherwise the exception might be sunk by some
// agent that actually runs your Runnable - you
// never know what it might be.
} catch (Throwable t) {
Log.e(TAG, "Error in DownloadTask", t);
}
}
}
DownloadThreadListener.class (interface)
public interface DownloadThreadListener {
void handleDownloadThreadUpdate();
}
Using this you can add huge amount of downloads it will add them queue.
Complete tutorial
I'm working on an app that retrieves data from network, stores them to the device and then reads them.
Problem is, I get my data in a Async Task.. And my app doesn't let the task finish before trying to show the data to the user..
I've tried task.get() but without result (it just stops there).
Here is my task:
public GetOptionsTask(XMLPortalGetOptions request) {
super(request);
}
protected void onCancelled(){
// TODO afficher message pas d'options sur le disque
}
#Override
public void handleError(Transaction transaction) {
// TODO afficher message pas d'options sur le disque
}
#Override
public void handleSuccess(Transaction transaction) {
saveOptions(transaction.getResponse());
request = null;
Log.d(OptionsManager.class.getName(), this.getStatus().toString());
}
This task is an instance of my custom Async Task:
protected BaseXMLTransaction request;
public abstract void handleError(Transaction transaction);
public abstract void handleSuccess(Transaction transaction);
public TransactionTask(BaseXMLTransaction request){
this.request = request;
}
#Override
protected Void doInBackground(Void... params) {
try {
Log.i(TransactionTask.class.getName(), "Doing in background");
SocketHandler.sendTransaction(this, request.getRequest());
} catch (SocketHandlerNotConfiguredException e) {
Log.e(TransactionTask.class.getName(), "SocketHandler's parameters were not set.");
}
return null;
}
#Override
public void transactionResult(Transaction transaction) {
switch (transaction.getCode()) {
case ERROR:
Log.e(TransactionTask.class.getName(), "ERROR !!!");
handleError(transaction);
break;
case NO_CLIENT:
Log.e(TransactionTask.class.getName(), "No Client Error");
handleError(transaction);
break;
case NO_SERVER:
Log.e(TransactionTask.class.getName(), "No Server Error");
handleError(transaction);
break;
case OLD_VERSION:
Log.e(TransactionTask.class.getName(), "Old Version");
handleError(transaction);
break;
case TIMEOUT:
Log.e(TransactionTask.class.getName(), "Transaction Timeout");
handleError(transaction);
break;
case SUCCESS:
Log.i(TransactionTask.class.getName(), "Transaction Success");
handleSuccess(transaction);
}
}
I seriously don't know what to do... Execute goes to fast and get doesn't do anything since I'm not returning anything I guess.
onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.
#Override
protected void onPostExecute(String result) {
}
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
and call it like this:
new DownloadFilesTask().execute(url1, url2, url3);
I use an interface as a delegate to do this. Here is an example:
In my main activity I have a onClick listener to trigger my async call and a listener to process once the call is complete.
private void enableLocationButton(){
locationButton = (Button) findViewById(R.id.locationButton);
locationButton.setEnabled(true);
locationButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, selectLocationActivity.class);
intent.putExtra("serverURL",server.getWebServerAddressField());
startActivityForResult(intent, 200);
}
});
}
#Override
protected void onActivityResult(int requestCode,int resultCode, Intent data){
if(resultCode == RESULT_OK) {
switch (requestCode){
case 100:
processServerResponse((PmsWebServer) data.getBundleExtra("server").get("server"));
break;
case 200:
processLocationResponse((PmsDataSource)data.getBundleExtra("location").get("location"));
default:processError();
}
}else{
processError();
}
}
Somewhere in the selectLocationActivity I have a call to the Async call and something to process the response, please note that this class implements an interface that is used in the Async call.
public class selectLocationActivity extends ListActivity implements SoapServiceInterface{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_location_select);
chosenServer = this.removeURLHeader(getIntent().getStringExtra("serverURL"));
this.retrieveLocationOptionsByServer(chosenServer);
}
private void retrieveLocationOptionsByServer(String server) {
Map<String,Object> parms = new HashMap<String,Object>();
parms.put(WEB_SERVER_NAME,server);
SoapServiceObject service = new SoapServiceObject(Services.SERVICE_DETAILS,parms);
callTheService(service);
}
private void callTheService(SoapServiceObject service){
SoapServiceHelper helper = new SoapServiceHelper();
helper.delegate = thisActivity;
helper.execute(service);
}
#Override
public void serviceCallComplete(SoapObject response){
this.createClickableListOnScreen(response);
}
//...more code...//
}
serviceCallComplete is kicked off by the asyncTask. Below is the code for that task
public class SoapServiceHelper extends AsyncTask<SoapServiceObject, Void, SoapObject>{
public SoapServiceInterface delegate = null;
private Integer RETRY_COUNT = 0;
private final Integer MAX_RETRY_COUNT = 2;
protected SoapObject doInBackground(SoapServiceObject... args){
SoapServiceObject service = args[0];
try{
service.callTheService();
}catch(Exception e){
System.out.println("An error occurred calling the service\n" + e.getMessage());
}
return service.getResponse();
//return callDateTimeService();
}
protected void onPostExecute(SoapObject result){
delegate.serviceCallComplete((SoapObject)(result.getProperty(0)));
}
}
And finally here is the interface
public interface SoapServiceInterface {
public void serviceCallComplete(SoapObject response);
}
I know I'm displaying something to the screen directly from my result, just sub that part with a save and read ;)
One thing with that task was that it was saving stuff into a singleton. I managed to call the methods using the information from the network saved in the singleton at the onResume(). When the threads end, it goes to the onResume and everything works fine!
I know this is a duplicate question but please hold on. I have read some similar questions and answer but none of them seems working for me.
What to do:
I have to do a search which will send a request to a web service and receive a response.
As i can't consume network on UI thread, I used AsyncTask.
What i tried:
I tried using task.execute() this returns immediately without even showing progressdialog box and i receive response as null (set in onPostExecute)
if i use task.execute.get() then it freezes screen and again no dialog box shows up (but i receive response correctly).
Below is my code with task.execute. Kindly correct me.
public class LookIn extends AppCompatActivity implements View.OnClickListener{
private Button btn=null;
private TextView txtPinCode=null;
private Service service=null;
private final static int timeout=20;
private String jsonResponse;
//private ProgressBar helperSearchProgressBar;
private String pincode="";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_look_in);
btn=(Button)findViewById(R.id.button);
btn.setOnClickListener(this);
txtPinCode=(TextView) findViewById(R.id.txtPinCode);
this.service=(Service) ParamFactory.getParam(ConstantLabels.SELECTED_SERVICE_ID);
// this.helperSearchProgressBar=(ProgressBar)findViewById(R.id.helperSearchProgressBar);
}
#Override
public void onClick(View v) {
String pincode= txtPinCode.getText().toString();
if(pincode==null || pincode.isEmpty() || pincode.length()!=6)
{
this.txtPinCode.setError("Please enter a 6 degit pin code from 700000 to 700200");
return;
}
ParamFactory.setParam(ConstantLabels.PINCODE_ID,pincode);
this.pincode=pincode;
loadHelper();
Intent intent= new Intent(LookIn.this,SearchResult.class);
startActivity(intent);
}
public void setJsonResponse(String jsonResponse)
{
this.jsonResponse=jsonResponse;
}
private void loadHelper()
{
Log.v("Callme", "Running thread:" + Thread.currentThread().getId());
ArrayAdapter<User> adapter=null;
String params=this.pincode+","+this.service.getId();
List<User> result=null;
try {
new CallmeGetHelperAsyncTask().execute(params); //my task.execute()
result= RestUtil.getUserList(jsonResponse);
adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, result);
ParamFactory.setParam("getHelperForService", adapter);
}
catch(JSONException x)
{
Log.e("Callme", Log.getStackTraceString(x));
}
}
class CallmeGetHelperAsyncTask extends AsyncTask<String,Void,String > {
// private Context context=null;
private ProgressDialog dialog=null;
private String jsonResponse;
private LookIn activity;
public CallmeGetHelperAsyncTask(){}
public CallmeGetHelperAsyncTask(LookIn activity)
{
this.activity=activity;
}
#Override
protected void onPreExecute() {
this.dialog= new ProgressDialog(LookIn.this);
this.dialog.setMessage("Loading...");
this.dialog.show();
Log.v("Callme","Dialog Shown");
}
#Override
protected void onPostExecute(String s) {
if(s!=null)
{
this.activity.setJsonResponse(s);
}
else
{
Log.v("Callme","kill me");
}
if(this.dialog.isShowing())
{
Log.v("Callme","Closing Dialog");
this.dialog.dismiss();
}
}
#Override
protected String doInBackground(String... params) {
Log.v("Callme","From Background:"+Thread.currentThread().getId());
String pincode=params.clone()[0].split(",")[0];
String serviceId=params.clone()[0].split(",")[1];
String url=String.format(URL.GET_HELPER,serviceId,pincode);
jsonResponse= null;
try {
jsonResponse = RestUtil.makeRestRequest(url);
} catch (IOException e) {
e.printStackTrace();
}
return jsonResponse;
}
}
}
Note: I haven't tried using while loop to waiting for the asynctask, because i think that will also end up freezing my screen. Please correct me if i am wrong
I haven't tried using while loop to waiting for the asynctask
No need to use loop for waiting AsyncTask Result.
Because onPostExecute method execute after doInBackground so instead of using jsonResponse just after call of execute method, do it inside setJsonResponse method, because this method called from onPostExecute which always run on Main UI Thread:
public void setJsonResponse(String jsonResponse)
{
this.jsonResponse=jsonResponse;
//Create adapter object here
result= RestUtil.getUserList(jsonResponse);
adapter = new ArrayAdapter(...);
ParamFactory.setParam("getHelperForService", adapter);
}
I've created a component which downloads an Image (in the Android side) and I want to send back size details about that image (to the JS side) of my app.
Now, I can safely say that I can call my component from JS and it will respond back with data but as soon as I add in the Async element to download the image from an URL, read it and respond I get a NullpointerException as soon as I call my callback.invoke("response text");
My problematic code is:
public void loadImage(final String url, final Callback onLoadCallback) {
...
new AsyncTask<String, Void, Void>() {
#Override
protected Void doInBackground(String... url) {
try {
theImage = Glide.with(getReactApplicationContext()).load(url[0]).asBitmap().into(-1, -1).get();
}
catch ...
return null;
}
#Override
protected void onPostExecute(Void dummy) {
if (null != theImage) {
onLoadCallback.invoke("Success"); //<== THIS LINE HERE
}
}
}.execute(url);
...
}
Now, I get that it's because I'm trying to return on a sep thread back to the main thread via the callback but I'm not sure how in the heck I'm supposed to get the info I want back to the JS side?! This is my first attempt at a component in RN so be kind! :)
Extra Info - My React module:
var MY_Image = require('NativeModules').MYImage;
var myimage = {
loadImage(url, onLoad) {
MY_Image.loadImage(url, onLoad)
},
};
module.exports = myimage;
Then in my React app view:
...
componentDidMount: function() {
myImage.loadImage('[URL to Image]',onLoad=> {
console.log('Success: '+onLoad);
});
}
Thanks for the input everyone. I've managed to sort this. I needed a class-wide variable to hold the callback in and a callback handler. Here's my code:
public class MyClass extends ReactContextBaseJavaModule {
private Bitmap mTheImage;
private Callback mCallback;
private WritableMap mResults;
public MyClass(ReactApplicationContext reactContext) {
super(reactContext);
this.mContext = reactContext;
}
private void consumeCallback(String type, WritableMap obj) {
if(mCallback!=null) {
obj.putString("type", type);
mCallback.invoke(obj);
mCallback = null;
}
}
#ReactMethod
public void doMyStuff(final String input, final Callback callback) {
if(mCallback==null) {
mResults = Arguments.createMap();
}
mCallback = callback;
new AsyncTask<String, Void, Void>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(String... params) {
try {
String myValue = params[0];
mResults.putString("myValue", myValue);
mTheImage = [method to get the image]
}
catch(Exception e) {
}
return null;
}
protected void onPostExecute(Void dummy) {
if(null!=mTheImage && null!=mCallback) {
mResults.putInt("width", mImage.getWidth());
mResults.putInt("height",mImage.getHeight());
consumeCallback("success", mResults);
}
else {
consumeCallback("error", mResults);
}
}
}.execute(url);
}
}
I am following the documentation given by IBM (https://developer.ibm.com/mobilefirstplatform/documentation/getting-started-7-0/hello-world/creating-first-native-android-mobilefirst-application/)
After calling request.send(new MyInvokeListener()); there is no sucess or failure call back. Receiving an error message "Android Prototype stopped working."
Adapter is working fine when i right click on the adapter --> Run As --> Call Mobile First Adapter
Below is my android native code.
public class TaskFeed extends AsyncTask<Void, Void, String> {
ProgressDialog Dialog = new ProgressDialog(TaskActivity.this);
#Override
protected void onPreExecute() {
Dialog.setMessage("Establishing connection...");
Dialog.show();
}
#Override
protected String doInBackground(Void... params) {
try {
final WLClient client = WLClient.createInstance(TaskActivity.this);
client.connect(new MyConnectListener());
URI adapterPath = new URI("/adapters/TaskAdapter/getAllTasks");
WLResourceRequest request = new WLResourceRequest(adapterPath,WLResourceRequest.GET);
request.send(new MyInvokeListener());
} catch (Exception e) {
e.printStackTrace();
}
// Dialog.setMessage("Loading Tasks..");
return "test";
}
#Override
protected void onPostExecute(String r) {
Dialog.dismiss();
ArrayList<ListViewModel> result = AssignAndGetCurrentTaskResults();
tvListCount.setText(GetActionBarString());
adapter = new ArrayDataAdapter(taContext, R.layout.task_row_item, result);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
My InvokeListner Class
public class MyInvokeListener implements WLResponseListener {
public void onSuccess(WLResponse response) {
try {
allTaskResults= ParseData(response.getResponseJSON().getJSONArray("array"));
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
public void onFailure(WLFailResponse response) {
}
}
Taking out the code which creates and call to mobile first adapter from async task solved my problem.
There is a window leakage in android doing so.