My problem is this;
I have a AsyncTask that works fine, and on doInBackground() it calls a new class that sync my data to a web service using REST service, i don't have everything on a unique class because i need the same content sync for different activitys and it's easier this way.
What i need is, on the sync procedure, i can get the number of "contacts" and everytime it downloads a contact, removes 1 from the "contacts" lenght, so, i nedd to show on the progress dialog the length of contact and refresh everytime it downloads a new "contact"
hre's my code for the AsyncTask:
public class syncContentTask extends AsyncTask<String, String, Boolean> {
private ProgressDialog mprogress;
private Context context;
//token for JSON header to authenticate
String authToken;
public syncContentTask(Context cxt, String token) {
this.context = cxt;
mprogress = new ProgressDialog(context);
authToken = token;
}
protected void onPreExecute() {
mprogress = ProgressDialog.show(context, "Sync", "Sync in progress...");
}
#Override
protected Boolean doInBackground(String... params) {
syncData syncData = new syncData();
syncData.syncData(context, authToken);
publishProgress(progress);
return true;
}
protected void onProgressUpdate(String... progress) {
//mprogress.setProgress(Integer.parseInt(progress[0]));
}
protected void onPostExecute(Boolean result) {
if (result) {
mprogress.dismiss();
}
}
}
In the Sync Data class i have functions that handles the HttpRequest and database stuff...
can anyone help??
You need to create a listener for your data progress and have it update the progress bar. Right now it looks like this line:
syncData.syncData(context, authToken);
blocks and no updates are provided to your progress indicator until it is done. So, you need something like:
MyListener listener = new MyListener(context);
SyncData syncData = new syncData(listener);
And in your listener have callback methods like myListener.downloadStarted() , myListener.updateProgressBar(int progress) and myListener.downloadCompleted() that your syncData class calls as the download progresses.
For example:
public abstract class SDScanAdapter implements SDScanListener {
public void startScan() {
}
public void updateScanProgress(int scanItemsTotal, int scanItemsCompleted) {
}
public void scanComplete() {
}
}
Then create a listener class:
public class ScanListener extends SDScanAdapter {
#Override
public void scanComplete(String contactName, String action) {
runOnUiThread(scanComplete);
}
#Override
public void startScan() {
runOnUiThread(startScan);
}
#Override
public void updateScanProgress(int scanItemsTotal,
int scanItemsCompleted) {
if (scanCountTotal != scanItemsTotal) {
scanCountTotal = scanItemsTotal;
progressBar.setMax(scanCountTotal);
}
if (scanCountUpdate != scanItemsCompleted) {
scanCountUpdate = scanItemsCompleted;
runOnUiThread(updateScanProgress);
}
}
}
And then for this example you need Runnables (startScan, scanComplete and updateScanProgress) that perform UI tasks, like updating the progress bar. In your case, you may also want to load some of the results, etc.
Then in your AsyncTask you do:
ScanListener listener = new ScanListener();
SyncData syncData = new syncData(listener);
Assuming the SDScanListener class and AsyncTask are all in your Activity. Also, your SyncData calss will need to have a SDScanListener variable that is set when it instantiates. Then, while it does its job, calls are made to the listener methods like:
scanListener.startScan();
And while it progresses, it calls the other methods (and corresponding parameters are passed in).
Related
I'am trying to implement an AsyncTask in Android that will load all my data from the database. Therefore I used the onPreExecute method to start a ProgressDialog
public class DataLoader extends AsyncTask<Void, Void, Void> {
private LoginActivity activity;
private ProgressDialog nDialog;
public DataLoader (LoginActivity act){
this.activity = act;
nDialog = new ProgressDialog(activity);
}
protected void onPreExecute() {
System.out.print("Start AsyncTask");
nDialog.setMessage("Loading data..");
nDialog.setTitle("Starting the application");
nDialog.setIndeterminate(false);
nDialog.setCancelable(true);
nDialog.show();
}
#Override
protected Void doInBackground(Void ... params) {
System.out.println("Starting doInBackground");
loadDashboardData();
return null;
}
protected void onPostExecute() {
nDialog.dismiss();
Intent i = new Intent();
i.setClass(activity.getApplicationContext(), DashboardActivity.class);
activity.startActivity(i);
}
The I use the doInBackground method to load call a function to load the data. This method is called from an visible activity. The task is called with:
public class LoginActivity extends Activity {
public void onClick(View v) {
DataLoader dl = new DataLoader(this);
dl.execute();
}
}
And the code for the doInBackground is:
protected Void doInBackground(Void ... params) {
System.out.println("Starting doInBackground");
loadDashboardData();
return null;
}
Now the problem is that my doInBackground method will not finish. I tried to implement the loadDashboardData() call in the onPreExecute method. This will not show my dialog box but it will load the data correctly. In this case the UI Thread is not responding and will response after all the data has been loaded.
What can hinder the doInBackground method to execute correctly and load the data properly? The called method works (because I can call it and get the correct data). Also I'am not seeing the println in my run console.
In the frontend I can see the progressbar spinning, but in the backend I can see that no data is loaded.
Your problem is that you are overriding the wrong method name : )
It should be
#Override
protected void onPostExecute(Void result) {
// your code
}
as in your case the variable which doInBackground return is Void.
You can check the documentation about AsyncTask .
hi i have AsyncTask in my app but i am unable to change its setMessage
for example :-
private class ProgressTask1 extends AsyncTask<String, Void, Boolean> {
private ProgressDialog dialog;
public ProgressTask1(MainActivity mainActivity) {
context = mainActivity;
dialog = new ProgressDialog(context);
}
private Context context;
protected void onPreExecute() {
this.dialog.setMessage("Checking system...");
this.dialog.setCancelable(false);
this.dialog.setTitle("Please Wait...");
this.dialog.setIcon(R.drawable.ic_launcher);
this.dialog.show();
}
now i want that setmessage to change i tried adding it in doinbackground
protected Boolean doInBackground(final String... args) {
dothis1();
this.dialog.setMessage("one done...");
dothis2();
this.dialog.setMessage("two done...");
but this is making app force close and do not rate it low because i tried my best and searched forum but could able to fix this so asked for hand at this nice community :)
anybody can help ? :)
ERROR
05-13 23:36:34.899: E/AndroidRuntime(2454): Caused by:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the
original thread that created a view hierarchy can touch its views.
ohmm, you should not update UI from background thread. To update UI, you can do by two ways:
1) Using publishProgress (Progress... values) and onProgressUpdate(...). To do that, you must change your AsynTask class:
private class ProgressTask1 extends AsyncTask<String, String, Boolean> {
//.......... //your init
#Override
protected Boolean doInBackground(String... params) {
//your background handle
//publish to change UI
String toShow = "your_string_here";
publishProgress(toShow);
//return ...; //your return value
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
//detect message and show it.
//this.dialog.setMessage(values[0]);
}
}
2) Using onPostExecute(....):
private class ProgressTask1 extends AsyncTask<String, Void, Boolean> {
//.......... //your init
#Override
protected Boolean doInBackground(String... params) {
//your background handle
//return ...;//your return value
}
#Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
String toShow = "your_string_here";
//this.dialog.setMessage(toShow);
}
}
You cannot update the UI from the background.
See AsyncTask.onProgressUpdate to update the UI based on your background progress. :D
I have an app connected to a Java Servlet backend by means of an AsyncPost task. The task returns a String to the client representing a json object serialized with Gson.
It works almost fine, the problem is that I'm unable to access the Servlet response message from the class instantiating the call to the ServletPostAsyncTask: ListViewPrenota.class.
The project is structured as follows:
Both within the Servlet and the Client I created the two classes, Tour.class and Tours.class to store my data:
Tour class:
public class Tour {
// some simple int/string/list fields
}
Tours class:
public class Tours {
private List<Tour> tours;
// ...
}
On Client side, in a ServletPostAsyncTask.class, I receive the aforementioned Gson object within doInBackGround(). Within onPostExecute() I deserialize it, this way:
class ServletPostAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
private Context context;
Tours tours;
#Override
protected String doInBackground(Pair<Context, String>... params) {
//connect to Servlet and get the serialized Gson object
}
#Override
protected void onPostExecute(String jsonResponse) {
tours = (new Gson().fromJson(jsonResponse, Tours.class));
}
}
Now, from ListViewPrenota.class in Client I am calling the ServletPostAsyncTask:
ServletPostAsyncTask s = new ServletPostAsyncTask();
s.execute(new Pair<Context, String>(ListViewPrenota.this, "tours"));
Tours ttours = s.tours;
Tour tour = ttours.getTours().get(0);
Problem: I receive a java.lang.NullPointerException pointing to Tour tour = ttours.getTours().get(0);
What is the reasong preventing me to access the newly received Tours object from other classes than ServletPostAsyncTask?
Thank you very much
the problem is you are thinking that code runs serially, if you want to use stuff returned from the AsycTask you need to use it in onPostExecute or have a callback that sends the data after it is done
doInBackground(){
//do heavy work
}
onPostExecute(Data data){
//handle data
//send data via interface to activity or class that needs the data
//or just put everything that needs the data in here
}
Ok, it works. Here's what I was able to come up with:
Callback interface:
interface CallBack {
void callBackMethod(Tours tours);//do job
}
Caller class:
class ServletPostAsyncTask extends AsyncTask<Pair<Context, String>, Tours, String>{
private Context context;
Tours tours;
public ListViewPrenota listViewPrenota;
public ServletPostAsyncTask(ListViewPrenota listView){
this.listViewPrenota = listView;
}
#Override
protected String doInBackground(Pair<Context, String>... params) {
//communicate with Servlet and get a HttpResponse
}
#Override
protected void onPostExecute(String jsonResponse) {
tours = (new Gson().fromJson(jsonResponse, Tours.class));
//the callback starts a thread updating the UI in ListViewPrenota
listViewPrenota.callBackMethod(tours);
Toast.makeText(
context,
"Connected. \nTours size: "+ tours.getTours().size(),
Toast.LENGTH_LONG).show();
}
}
The callback interface's implementation within ListViewPrenota:
public class ListViewPrenota extends FragmentActivity implements CallBack{
private ProgressDialog m_ProgressDialog = null;
private Runnable viewOrders;
private TourAdapter m_adapter;
ListView listView;
private ArrayList<Tour> m_tours =null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view_prenota);
listView = (ListView) findViewById(R.id.list);
m_tours = new ArrayList<Tour>();
m_adapter = new TourAdapter(this, R.layout.list_row, m_tours);
listView.setAdapter(m_adapter);
getActionBar().setDisplayHomeAsUpEnabled(true); //pulsante drawer
getActionBar().setHomeButtonEnabled(true); //pulsante dietro
ServletPostAsyncTask spat = new ServletPostAsyncTask(ListViewPrenota.this);
String status = spat.getStatus().toString();
spat.execute(new Pair<Context, String>(ListViewPrenota.this,"tours"));
}
public void callBackMethod(final Tours tours){
System.out.println("I've been called back");
viewOrders = new Runnable(){
#Override
public void run() {
getOrders(tours);
}
};
Thread thread = new Thread(null, viewOrders, "MagentoBackground");
thread.start();
m_ProgressDialog = ProgressDialog.show(
ListViewPrenota.this,
"Please wait...",
"Retrieving data ...",
true);
}
public void getOrders(Tours tours){
try{
m_tours = new ArrayList<>();
m_tours.addAll(tours.getTours());
Thread.sleep(2000);
Log.i("ARRAY", "" + m_tours.size());
} catch (Exception e) {
Log.e("BACKGROUND_PROC", e.getMessage());
}
//add tours to the adapter
runOnUiThread(returnRes);
}
private Runnable returnRes = new Runnable() {
#Override
public void run() {
if(m_tours != null && m_tours.size() > 0){
m_adapter.notifyDataSetChanged();
for(int i=0;i<m_tours.size();i++)
m_adapter.add(m_tours.get(i));
}
m_ProgressDialog.dismiss();
m_adapter.notifyDataSetChanged();
}
};
If there's a better way to do it I accept further suggestions.
In the mean time, thank you very much
I have an activity with multiple AsyncTask's, but when i press back button, the Activity is reloaded and the AsyncTask's are executed again. what should i do to Back to the previous activity and not reload the activity and asynctask ? please help.
public class LugarActivity extends SherlockActivity {
CargarDatos cargarDatos;
CargarComentarios cargarComentarios;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lugar);
cargarDatos = new CargarDatos();
cargarCometarios = new CargarComentarios();
loadData();
}
public void loadData(){
cargarDatos.execute();
}
public void loadOtherData(){
cargarComentarios.execute();
}
public class CargarDatos extends AsyncTask<Integer, Integer, String>{
#Override
protected String doInBackground(Integer... params) {
// here download data
}
#Override
protected void onPostExecute(String html) {
loadOtherData();
}
}
public class CargarComentarios extends AsyncTask<Integer, Integer, String>{
#Override
protected String doInBackground(Integer... params) {
// here download data
}
}
}
FIXED!
i fixed the problem with Singleton class:
public class DataManager {
private static DataManager instance = null;
protected static boolean isShowingTheView = false;
protected DataManager() { }
public static synchronized DataManager getInstance() {
if (instance == null) {
instance = new DataManager();
}
return instance;
}
}
in the activity i add this code:
DataManager dataManager = new DataManager();
if(!dataManager.isShowingTheView){
loadData();
dataManager.isShowingTheView = true;
}else{
finish();
}
and finally i override the onDestroy() method
#Override
public void onDestroy(){
dataManager.isShowingTheView = false;
super.onDestroy();
}
Remove loadData() from onCreate and call somewhere else.
Use Fragments
http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
A fragment can stay in memory during a configuration change and therefore you can run your asynctask inside itself. You can then query the fragment for any state information you require from your tasks and update your Activity accordingly.
If your Activity is destroyed before the other activity starts, using the back button will call onCreate again, instead of onRestart or onResume.
See here for details.
As Kuffs already mentions, using Fragments is the way to go.
Uglier solution, you could also set a shared preference holding a boolean once your AsyncTask is launched (or on its onPostExecute) so that it won't launch again after checking for that preference on your Activity's onCreate.
I would like to enable a few buttons from my main activity once the stuffs from doInBackground() is finished! Can someone please let me know how to do that?
I can't use findViewByID() for making he button visible from the AsyncTask class as it's not an activity class! :/
Do Like this...
Define a method which enables the Buttons.
Then on PostExecute() on AsyncTask, call that method
there is one callback onPostExecution(...) { } of AsynTask class use this method to UI stuff,for enable,disable button just write this way in onPostExcustion(...)
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
}
});
also make sure this method only available in activity class
thank you
Follow this way:
[1] Create your AsyncTask :
public class performBackgroundTask extends AsyncTask<Void, Void, Void> {
ProgressDialog Dialog = new ProgressDialog(HotUsers.this);
protected void onPreExecute() {
Dialog.setMessage("Loading Hot Users...");
Dialog.show();
}
protected void onPostExecute(Void unused) {
if(Dialog.isShowing())
Dialog.dismiss();
set_details_on_screen();
}
#Override
protected Void doInBackground(Void... params) {
get_details_from_server(); // get data like userid,username,userdesc etc...
return null;
}
}
[2] That will call function to proceed for UI changes.
public void set_details_on_screen()
{
if(userid > 0 )
handler_default.sendEmptyMessage(0);
else
handler_default.sendEmptyMessage(1);
}
[3] At last your UI changes will be reflected on screen with this Handler.
private Handler handler_default = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 0: {
textuserid = (TextView) findViewById(R.id.userid);
textusername = (TextView) findViewById(R.id.username);
textuserdesc = (TextView) findViewById(R.id.userdesc);
textuserid.setText(userid);
textusername.setText(username);
textuserdesc.setText(userdesc);
break;
}
case 1: {
Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
break;
}
}
}
};
Thanks.
your class which extends AsyncTask you can push your context into it, when calling the execute().
private class RegisterUser extends AsyncTask<String,String,String> {
private ListActivity activity;
public RegisterUser(ListActivity activity) {
this.activity = activity;
}
protected void onPostExecute(JSONObject json) {
activity.editText = (EditText)activity.findViewById(R.id.editText1);
//or
activity.enableButton();
}
}
and call the execute from the Activity like this:
new RegisterUser(this).execute(new String[] {"param"});
or you can define the AsyncTask class inside your Activity class - where you can reach everything.
more info Lars Vogel - Android Threads, Handlers and AsyncTask