Well, i have 3 swipe tabs and in each tab i have called a asynctask in the fragments onCreateView() method. Its showing result in normal case but the problem is when i rotate the screen. Basically it closes forcefully when a asynctask is going on and before completing the task-i rotate the screen.
So, i have tried to cancel the asynctask in onPause and call it again in onCreateView(). Below is the code inside a fragment:
public class AllFragment extends Fragment
{
SearchBusBeans searchBusObj;
ArrayList<SearchBusBeans> searchBusObj_arr;
SearchingBusesAdapter myAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{ .....
....
searchBusObj_arr = new ArrayList<SearchBusBeans>();
new GetSearchedBusesAsync().execute();
}
#Override
public void onPause() {
super.onPause();
GetSearchedBusesAsync asyncObj = new GetSearchedBusesAsync();
if(asyncObj.getStatus() == AsyncTask.Status.RUNNING){
asyncObj.cancel(true);
}
}
private class GetSearchedBusesAsync extends AsyncTask<Void, Void, Void>
{
#Override
protected void onPreExecute() { super.onPreExecute(); }
#Override
protected Void doInBackground(Void... params)
{
if(isCancelled())
this.cancel(true);
if(!isCancelled())
{...........
......do my codes.......
searchBusObj = new SearchBusBeans();
searchBusObj.setname(j_name);
searchBusObj_arr.add(searchBusObj);
}
return null;
}
#Override
protected void onPostExecute(Void result)
{
super.onPostExecute(result);
pBar.setVisibility(View.GONE);
myAdapter = new SearchingBusesAdapter(getActivity().getApplicationContext(), searchBusObj_arr);
gridView.setAdapter(myAdapter);
}
} //end of asynctask
} //end of fragment
Now as you can see that i have cancelled the asynctask in onPause. and inside doInBackground() i have done my code in condition-if(!isCancelled()). So, shouldn't the asynctask exit and not go to onPostExecute()?
Where am i going wrong? and how to do the cancellation and execution properly?
You can set your fragment to retain its state through a rotation. This will allow your AsyncTask to keep running.
public class AllFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
Override the onCreate method of the fragment and call setRetainInstance(true);. This keeps the fragment alive so that it doesn't get destroyed and then recreated during a configuration change.
There is another approach to stop the execution of an AsyncTask (on any event,not necessary on rotation):
1) Save your AsyncTask in a variable when you call .execute()
mTask = new YourAsyncTask().execute();
2) Then, when you need to stop the task call .cancel()
if(mTask != null) {
mTask.cancel(true);
}
This approach works for me perfectly and it is usable almost for any scenario. Hope it helps!
Related
Like I need to check whether a username already existed in database, and the program won't continue until response arrives.
it seems you need asynctask, this is an example how to use it..
public class YourFragment extends Fragment implements YourAsyncTask.YourInterface {
public YourFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_your, container, false);
//do your initial things
.
.
.
YourAsyncTask yourAsyncTask = new YourAsyncTask(this);
yourAsyncTask.execute();
return view;
}
#Override
public void onJobFinishListener(YourDataType yourData) {
//when this method is trigered by your asynctask
//it means that you are in ui thread and update your ui component
//TODO: update ui component with your data
}
}
and this is the asynctask;
public class YourAsyncTask extends AsyncTask {
private YourInterface yourInterfaceListener;
private YourDataType yourData; //this data should be calculated in doInBackground method and send via interface
public YourAsyncTask(YourInterface yourInterfaceListener) {
this.yourInterfaceListener = yourInterfaceListener;
}
#Override
protected Object doInBackground(Object[] objects) {
//do your all background tasks here
.
.
.
yourData = do something here to fill your data..
return null;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
yourInterfaceListener.onJobFinishListener(yourData);
}
public interface YourInterface{
void onJobFinishListener(YourDataType yourData);
}
}
Use AsyncTask to perform network operations
public class TalkToServer extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
/*
* do things before doInBackground() code runs
* such as preparing and showing a Dialog or ProgressBar
*/
}
#Override
protected void onProgressUpdate(Void... values) {
/*
* updating data
* such a Dialog or ProgressBar
*/
}
#Override
protected Void doInBackground(Void... params) {
//do your work here
return null;
}
#Override
protected void onPostExecute(Void result) {
/*
* do something with data here
* display it or send to mainactivity
* close any dialogs/ProgressBars/etc...
*/
}
}
And you can execute it using
TalkToServer myTask = new MyTask(); // can add params for a constructor if needed
myTask.execute(); // here is where you would pass data to doInBackground()
After the network call is done and response obtained onPostExecute() is called. If the AsyncTask is in inner class of your activity you can update UI inside onPostExecute(). Otherwise you can use an Interface to create a callback to activity.
You can find AsyncTask Docs here
You have to use async task. Then you can uae it your_async_class.execute().get() in your activiry this ..get method you want to it. Main thread will wait your async task. You have to change your_async_class extends AsyncTask
I've an AppCompatActivity that uses the NavigationDrawer pattern, managing some fragments. In one of these, that has no setRetainInstance(true), I show a DialogFragment with a ProgressDialog inside and an AsyncTask with this code:
SavingLoader savingLoader = SavingLoader.newInstance(savingLoaderMaxValue);
savingLoader.show(getChildFragmentManager(), SAVING_LOADER_TAG);
new MyAsyncTask().execute();
Where the SavingLoader class is this one:
public class SavingLoader extends DialogFragment {
private static final String MAX_VALUE_TAG = "MAX_VALUE_TAG";
private static final String PROGRESS_VALUE_TAG = "PROGRESS_VALUE_TAG";
public static SavingLoader newInstance(int max_value){
SavingLoader s = new SavingLoader();
Bundle args = new Bundle();
args.putInt(MAX_VALUE_TAG, max_value);
s.setArguments(args);
return s;
}
private ProgressDialog dialog;
public SavingLoader(){}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setCancelable(false);
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState){
dialog = new ProgressDialog(getActivity(), getTheme());
dialog.setTitle(getString(R.string.dialog_title_saving));
dialog.setMessage(getString(R.string.dialog_message_saving));
dialog.setIndeterminate(false);
int max = (savedInstanceState == null ?
getArguments().getInt(MAX_VALUE_TAG) : savedInstanceState.getInt(MAX_VALUE_TAG));
if (max >= 1){
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setProgress((savedInstanceState == null ?
0 : savedInstanceState.getInt(PROGRESS_VALUE_TAG)));
dialog.setMax(max);
} else dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
return dialog;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(MAX_VALUE_TAG, dialog.getMax());
outState.putInt(PROGRESS_VALUE_TAG, dialog.getProgress());
}
public int getProgress(){
return dialog.getProgress();
}
public int getMax(){
return dialog.getMax();
}
public void incrementProgressBy(int value){
if (dialog.getProgress() + value <= dialog.getMax())
dialog.incrementProgressBy(value);
}
}
In the onPostExecute() method I need to perform some UI update so here's my problem: if I start the dialog and the AsyncTask (like above) and I don't rotate my phone, all works as expected. Same thing if I rotate phone AFTER the onPostExecute() method. But if I rotate my phone WHILE the AsyncTask is still running, when it completes and reach the onPostExecute() method it gives me the IllegalStateException saying that the fragment hosting the AsyncTask and the Dialogfragment is no longer attached to the activity. So I tried to override both the onAttach() and the onDetach() methods (with a simple System.out.println) of my fragment, to see when the onPostExecute() gets called. The result is that when I rotate my phone, I always got this output:
onDetach
onAttach
... (if I rotate more my phone)
onPostExecute
So shouldn't the fragment be attached when the AsyncTask completes? Thank you all for your time and attention.
I've finally managed to solve this problem by stop using AsyncTask and using LoaderManager + AsyncTaskLoader following this article. In short, your fragment must implement the LoaderCallbacks interface and manage the AsyncTaskLoader. A skeleton fragment could be something like this:
public class MyFragment extends Fragment implements LoaderManager.LoaderCallbacks {
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
// Inflate here your view as you usually do and find your components
// For example imagine to have a button tha will fire the task
Button b = (Button) view.findViewById(R.id.my_button);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Use this to start task for the first time
getLoaderManager().initLoader(0, null, this);
// .. or this for restart the task, details in
// the provided article
// getLoaderManager().restartLoader(0, null, this);
}
});
// Get fragments load manager
LoaderManager lm = getLoaderManager();
if (lm.getLoader(0) != null) {
// Reconnect to an existing loader
lm.initLoader(0, null, this);
}
// Return your view here
return view;
}
// LoaderCallbacks methods to override
#Override
public Loader onCreateLoader(int id, Bundle args) {
// Create an instance of the loader passing the context
MyTaskLoader loader = new MyTaskLoader(getActivity());
loader.forceLoad();
return loader;
}
#Override
public void onLoadFinished(Loader loader, Object data) {
// Use this callback as you would use the AsyncTask "onPostExecute"
}
#Override
public void onLoaderReset(Loader loader) {}
// Now define the loader class
private static class MyTaskLoader extends AsyncTaskLoader {
public MyTaskLoader(Context context){
super(context);
}
#Override
public Object loadInBackground() {
// Do here your async work
}
}
}
We use viewpager fragment in it, we want to show every single page when viewpager show a fragment and load data , because it saves memory a lot, but we can not show a sign for loading when data loading, we replace a fragment when asynctask's onPreExecute calls and we replace again when calls onPostExecute, but there is some thing wrong, which is the best way to show loading sign when data loading?
public class TestFragment extends Fragment {
private FragmentManager manager = null;
private class TestTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
getFragmentManager().beginTransaction()
.replace(R.id.content_container, new LoadingFragment())
.commit();
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... arg0) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
getFragmentManager().beginTransaction()
.replace(R.id.content_container, new ContentFragment())
.commit();
super.onPostExecute(result);
}
}
public TestFragment(FragmentManager supportFragmentManager) {
this.manager = supportFragmentManager;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new TestTask().execute();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.empty_page, null);
}
}
Another easy way if you use the action bar in your app is calling supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); in the FragmentActivity right after super.onCreate(savedInstanceState); and from the child fragment ((ActionBarActivity)getActivity()).setSupportProgressBarIndeterminateVisibility(visibility); will show or hide a progress bar in the action bar of your hosting activity depending on your progress in the AsyncTask.
Note: I'm using support appcompat, but I'm pretty sure the methods are the same if you use ActionBarSherlock.
add an progressbar to the layout of the fragment but defining it invisible and centering it both vertically and horizontally. then in the onPreExecute set the visibility of all other views to Visibility.GONE and set the progressbar to VISIBLE. reverse this in the onPostExecute() method!
I have two activities. While switching to second activity by intent, it takes 3-4 seconds because it has lots of components with adapters fetching data from SQLite etc. Thus, I want to show a progress dialog while switching.
I have been digging topics for this purpose and tried many of them:
1-) Using AsyncTask on the second activity. It doesn't show the progress dialog as soon as I click on a component to switch to the second activity. It waits for 3-4 seconds and then progress dialog shows up for less then a second which is not user-friendly way.
2-) Using AsyncTask on the first activity. It shows as soon as I click on that component but the progress wheel doesn't spin. The progress dialog freezes.
3-) Using AsyncTask onStart() method on the second activity. This results as the first way.
The code below implements the second way above, using AsyncTask on the first activity.
public void toVisitRegister(Event event) { //Switching to the second activity
new startingThread().execute();
Intent toVisitRegister = new Intent(MainCalendar.this, VisitRegister.class);
startActivity(toVisitRegister);
finish();
}
And here is the AsyncTask
public class startingThread extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
startingProgress = new ProgressDialog(MainCalendar.this);
startingProgress.setTitle("Visit Register");
startingProgress.setMessage("Initializing...");
startingProgress.show();
}
#Override
protected String doInBackground(String... params) {
return null;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(MainCalendar.this.startingProgress != null) {
MainCalendar.this.startingProgress.dismiss();
}
}
}
I also tried to call startActivity in onPostExecute, but it didn't work. Therefore, I am waiting for your opinions and suggessions about this issue. Any help will be appreciated.
Thanks.
I also tried to call startActivity in onPostExecute,
Pass the Activity context to startingThread AsyncTask and put your start activity code in onPostExecute() of AsyncTask.
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(MainCalendar.this.startingProgress != null) {
MainCalendar.this.startingProgress.dismiss();
Intent toVisitRegister = new Intent(MainCalendar.this, VisitRegister.class);
mContext.startActivity(toVisitRegister);
mContext.finish();
}
}
Here mContext is the Context of your current MainCalendar Activity.
Looking at your implementation, the AsyncTask wont have time to work because you will be jumping to the next Activity right away. Try calling the next activity in PostExecute().
I also tried to call startActivity in onPostExecute, but it didn't work.
Did you did it like this:
public void toVisitRegister(Event event) { //Switching to the second activity
new startingThread().execute();
}
public class startingThread extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
startingProgress = new ProgressDialog(MainCalendar.this);
startingProgress.setTitle("Visit Register");
startingProgress.setMessage("Initializing...");
startingProgress.show();
}
#Override
protected String doInBackground(String... params) {
return null;
}
#Override
protected void onPostExecute(String result) {
if(MainCalendar.this.startingProgress != null) {
MainCalendar.this.startingProgress.dismiss();
}
Intent toVisitRegister = new Intent(MainCalendar.this, VisitRegister.class);
startActivity(toVisitRegister);
finish();
}
}
When an Activity terminates, e.g. after screen orientation changing, is that possible to change an AsyncTask activity context? Else it will create an error because when the activity terminates AsyncTask's activity context is gone too.
My homework done is the following:
public void onSaveInstanceState(Bundle savedInstanceState) <- doesn't solve
public Object onRetainNonConfigurationInstance() <- doesn't solve
android:configChanges="keyboardHidden|orientation"
<- solved but doesn't handle well relative layouts
What do you pass on your onRetainNonConfigurationInstance()? What I do is pass an object to it containing the AsyncTask, and then I try to retrieve the value in getLastNonConfigurationInstance().
EDIT: On second thought, it would depend on what you want to do after a configuration change. If you want to terminate the AsyncTask, and then call cancel() on it. If you want to continue its processing even after an orientation change, then you have to hold on to the task.
You can do that by saving the Activity in the AsyncTask like this:
private MyAsyncTask searchTask;
#Override
public void onCreate(Bundle savedInstance){
super.onCreate(savedInstance);
if (getLastNonConfigurationInstance()!=null) {
SavedObject savedObj = (SavedObject)getLastNonConfigurationInstance();
searchTask = savedObj.getAsyncTask();
searchTask.attach(this);
} else {
searchTask = new MyAsyncTask(this);
searchTask.execute();
}
}
#Override
public Object onRetainNonConfigurationInstance(){
searchTask.detach();
final SavedObject savedObj = new SavedObject();
savedObj.setAsyncTask(searchTask);
return savedObj;
}
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
MyActivity parentActivity = null;
MyAsyncTask (MyActivity activity) {
attach(activity);
}
void attach(MyActivity activity) {
this.parentActivity=activity;
}
void detach() {
parentActivity=null;
}
// Do your thread processing here
}
private class SavedObject {
private MyAsyncTask asyncTask;
public void setAsyncTask(MyAsyncTask asyncTask){
this.asyncTask = asyncTask;
}
public MyAsyncTask getAsyncTask() {
return asyncTask;
}
}
in the OnCancel method of your asynch task put finish();
public void onCancel(DialogInterface dialog) {
cancel(true);
dialog.dismiss();
finish();
}