I have an Activity with a ViewPager and some Fragments in it.
The problem: It takes ages to start the activity because the ViewPager is created in the onResume methode. 2-3 seconds because of database requests.
I thought I can start a AsyncTask in onResume and do there the heavy work. But the time to start the Activity doesn't decrease.
If I place a Button in the View and do the work in onClick everything works fine. The Activity starts really fast and after the click the heavy work starts.
some code to think about:
The AsyncTask implementation
private class LoadTask extends AsyncTask<Void, Integer, Boolean>{
#Override
protected Boolean doInBackground(Void... params) {
PageAdapter mPageAdapter = new PageAdapter(
getSupportFragmentManager(), mFragments);
mViewPager.setAdapter(mPageAdapter);
return true;
}
}
the task is called in onResume
#Override
protected void onResume() {
super.onResume();
LoadTask task = new LoadTask();
task.execute();
}
This solution doesn't improve the starting time of the activity.
Is there a way to start the Activity and after inflating and displaying everything (progressbar...) doing the heavy task?
What you need to do is setup the empty view pager and link it to the adapter when you create your activity. Then, fill the content into whatever collection backs your adapter in the async task and notify the view that the dataset has changed. The view can't be displayed until it's created and you have moved the registering of the adapter to the async task so you won't notice a difference. The goal is to have your app show some view when you start it and simply fill in the data as it becomes available (as you probably know).
Related
The main activity has a "To ListView" button that launches a new activity with a list view. Each time this new activity loads, it calls an AsyncTask() method that retrieves some JSON remotely, parses it, and binds the data to the list view. Then, setContentView() is called (in onPostExecute()) to show the UI. How do I preserve the list view data, or at least the data array (for rebinding) on subsequent launches of that activity so that the ASyncTask() doesn't have to be called every time?
The ASyncTask() should get called only at the beginning (and not until when the application is forcefully terminated), subsequent calls should be done manually by the user perhaps with an onClick() event. I have tried setting a boolean for that purpose, but if so how to display the previous state of the list view when the boolean is false? I have also looked into onResume() and onBackPressed() but they don't seem to be much relevant.
Main.java:
toListView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent myIntent = new Intent(getApplicationContext(), ListView.class);
startActivity(myIntent);
}
});
ListView.java:
public class ListView extends ActionBarActivity {
private static boolean isFirstLaunch = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isFirstLaunch) {
// execute the ASyncTask();
isFirstLaunch = false;
}
// else display previous listview data or last activity state
}// onCreate()
For persisting data in your Android application, you have two obvious choices.
Using an SQLite database. This alternative is good if you have quite a lot of data to manage, sort and maintain. E.g if your JSON response contains a lot of data, persisting it to a database would be a good solution. Then you would simply query the database instead of executing the AsyncTask repeatedly. Vogella provides an excellent tutorial on this matter.
Using SharedPerefences. This alternative is better if you only have a couple of variables to relate to. Storing the data in SharedPreferences relieves you of the work needed to design and implement database support for you application. See the official documentation for a reference.
Scenario: I have a dialog fragment. Onclick of an image it launches an activity which does the process in an asynctask. When I get back the result the activity finishes and goes back to the dialog fragment.On getting back the image must be changed.
Problem:The image remains the same and only when you close the dialogfragment and reopen it does it change.
Sorry I havnt posted the code, Just wanted some suggestions on how to update the view from the activitys async task.
If I understood you correctly, try use invalidate() method for your ImageView in onPostExecute() of your AsyncTask.
If you start your AsyncTask in launched Activity, as I suppose, you can start custom Activity in Dialog Fragment and pass your ImageView pointer to constructor. Something like this.
public class AsyncTaskActivity extends Activity{
private ImageView yourImage;
public AsyncTaskActivity(ImageView yourImage){
this.yourImage = yourImage;
}
....
//your AsyncTask should be like this
new AsyncTask<Void, Void, Void>(){
//here your backround process
...
//here we invalidate ImageView
#Override
protected void onPostExecute(Void result) {
//update image Bitmap or something else
...
yourImage.invalidate();
}
}.execute();
}
With best regards.
I would suggest the following:
Start the activity (B) by using startActivityForResult from your dialog.
Override OnActivityResult in your original activity (A, the one that host the dialog).
call super.OnAcitivityResult in it, so that the dialog can receive the call back.
Your dialogfragment should override OnActivityResult and change the image based on the result.
Need your code to see what you are actually trying to do.
I'm trying to understand under what circumstances can getActivity() return null in a fragment AFTER onAttach. I typically start an async task in onCreate or onCreateView inside my fragments but I'm getting error reports indicating sometimes getActivity() is null when the async task finishes. Error reports are coming in via crashlytics but can't reproduce them.
The async tasks are "blocking" - I display a modal non-dismissable progress bar. Also rotation is prevented by calling setRequestedOrientation.
I'm using v4 support Fragment and FragmentActivity. Fragments are set to retain state.
What am I missing? Are there other config changes that may cause the fragment to be detached?
I tried temporarily enabling rotation and the dev option to destroy activity after leaving it but still can't reproduce this...
Here's some of the relevant code inside one of my fragments, in this case it would sometimes break with an NPE at activity.dismissSpinner:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
checkIfLoggedIn();
}
public void checkIfLoggedIn() {
LoginActivity activity = (LoginActivity)getActivity();
activity.showSpinner("Connecting, please wait...");
AsyncTask<String, Void, JsonResponse> asyncTask = new AsyncTask<String, Void, JsonResponse>() {
protected JsonResponse doInBackground(String... notused) {
return cmsServer().getCurrentUser(getActivity());
}
protected void onPostExecute(JsonResponse result) {
LoginActivity activity = (LoginActivity)getActivity();
activity.dismissSpinner();
//...more stuff here
}
};
asyncTask.execute();
}
Do you stop/cancel your AsyncTask if your app goes to background or is paused?
Consider the following scenario: your AsyncTask is executed, and when prompted with the progress bar, the user decides to do other stuff while she waits for the task to complete. She does so by pressing the home button. Alas, this might destroy the fragment and the activity. The running AsyncTask knows nothing about it, and when done, getActivity() method invocations (or local variables pointing to a non-existent Activity) may as well return null, causing your app to crash.
The Fragmentlife cycle is as follows
According to Fragment life-cycle onCreate() and onCreateView()are called before the Activity creation. so when we call getActivity() in these methods in returns null.
so instead of starting the async task in the onCreateView() start it in onStart() or onResume() so that getActivity() returns the exact Activity reference.
For more details click here
I've seen few questions nearly identical to mine, but I couldn't find a complete answer that satisfies all my doubts.. so here I am.. Suppose that you have an activity with an inner class that extends the AsyncTask class like this:
public class MyActivity extends Activity {
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
return DownloadImage(urls[0]);
}
protected void onPostExecute(Bitmap result) {
ImageView img = (ImageView) findViewById(R.id.img);
img.setImageBitmap(result);
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new DownloadImageTask().execute("http://mysite.com/image.png")
}
}
Suppose that the activity is paused or destroyed (maybe the two cases are different) while the DownloadImageTask is still running in background.. then, the DownloadImageTask's methods that run on the activity UI thread can be triggered and the DownloadImageTask may try to access Activity's methods (it is an inner class, so it can access the methods and instance variables of the outer class) with a paused or destroyed Activity, like the call to findViewByID in the example below.. what happens then? Does it silently fail? Does it produce any exception? Will the user be notified that something has gone wrong?
If we should take care that the launching thread (the Activity in this case) is still alive when running-on-UI methods are invoked, how can we accomplish that from within the AsyncTask?
I'm sorry if you find this as a duplicate question, but maybe this question is a bit more articulated and someone can answer with greater detail
Consider this Task (where R.id.test refers to a valid view in my activity's layout):
public class LongTaskTest extends AsyncTask<Void, Void, Void>{
private WeakReference<Activity> mActivity;
public LongTaskTest(Activity a){
mActivity = new WeakReference<Activity>(a);
}
#Override protected Void doInBackground(Void... params) {
LogUtil.d("LongTaskTest.doInBackground()");
SystemClock.sleep(5*60*1000);
LogUtil.d("mActivity.get()==null " + (mActivity.get()==null));
LogUtil.d("mActivity.get().findViewById(R.id.frame)==null " + (mActivity.get().findViewById(R.id.test)==null));
return null;
}
}
If I run this task from an Activity's onCreate like so:
public class Main extends Activity {
#Override
public void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.testlayout);
new LongTaskTest(this).execute();
finish();
}
}
No matter how long I sleep the background thread, my log always shows:
LongTaskTest.doInBackground()
mActivity.get()==null false
mActivity.get().findViewById(R.id.frame)==null false
Which is to say that the activity and its views appear to stay alive (even if I manually issue GCs via DDMS). If I had more time I'd look at a memory dump, but otherwise I don't really know why this is the case ... but in answer to your questions it appears that:
Does it silently fail? No
Does it produce any exception? No
Will the user be notified that something has gone wrong? No
The doInBackground() will keep on running even if your Activity gets destroyed(i,e your main thread gets destroyed) because the doInBackground() method runs on the worker's/background thread. There will be a 'problem' in running the onPostExecute() method as it runs on the main/UI thread and you may experience running into unrelated data but there will be no exception shown to the user. Thus, it is always better to cancel your AsyncTask when your activity gets destroyed as there is no reason to run AsyncTask when the Activity is no longer present. Use android Service if you continuously want to download something from the network even when your Component/Activity gets destroyed. Thanks.
I've been working with AsyncTasks in Android and I am dealing with an issue.
Take a simple example, an Activity with one AsyncTask. The task on the background does not do anything spectacular, it just sleeps for 8 seconds.
At the end of the AsyncTask in the onPostExecute() method I am just setting a button visibility status to View.VISIBLE, only to verify my results.
Now, this works great until the user decides to change his phones orientation while the AsyncTask is working (within the 8 second sleep window).
I understand the Android activity life cycle and I know the activity gets destroyed and recreated.
This is where the problem comes in. The AsyncTask is referring to a button and apparently holds a reference to the context that started the AsyncTask in the first place.
I would expect, that this old context (since the user caused an orientation change) to either become null and the AsyncTask to throw an NPE for the reference to the button it is trying to make visible.
Instead, no NPE is thrown, the AsyncTask thinks that the button reference is not null, sets it to visible. The result? Nothing is happening on the screen!
Update: I have tackled this by keeping a WeakReference to the activity and switching when a configuration change happens. This is cumbersome.
Here's the code:
public class Main extends Activity {
private Button mButton = null;
private Button mTestButton = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton = (Button) findViewById(R.id.btnStart);
mButton.setOnClickListener(new OnClickListener () {
#Override
public void onClick(View v) {
new taskDoSomething().execute(0l);
}
});
mTestButton = (Button) findViewById(R.id.btnTest);
}
private class TaskDoSomething extends AsyncTask<Long, Integer, Integer>
{
#Override
protected Integer doInBackground(Long... params) {
Log.i("LOGGER", "Starting...");
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 0;
}
#Override
protected void onPostExecute(Integer result) {
Log.i("LOGGER", "...Done");
mTestButton.setVisibility(View.VISIBLE);
}
}
}
Try executing it and while the AsyncTask is working change your phones orientation.
AsyncTask is not designed to be reused once an Activity has been torn down and restarted. The internal Handler object becomes stale, just like you stated. In the Shelves example by Romain Guy, he simple cancels any currently running AsyncTask's and then restarts new ones post-orientation change.
It is possible to hand off your Thread to the new Activity, but it adds a lot of plumbing. There is no generally agreed on way to do this, but you can read about my method here : http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html
If you only need a context and won't use it for ui stuff you can simply pass the ApplicationContext to your AsyncTask.You often need the context for system resources, for example.
Don't try to update the UI from an AsyncTask and try to avoid handling configuration changes yourself as it can get messy. In order to update the UI you could register a Broadcast receiver and send a Broadcast.
You should also have the AsyncTask as a separate public class from the activity as mentioned above, it makes testing a lot easier. Unfortunately Android programming often reinforces bad practices and the official examples are not helping.
This is the type of thing that leads me to always prevent my Activity from being destroyed/recreated on orientation change.
To do so add this to your <Activity> tag in your manifest file:
android:configChanges="orientation|keyboardHidden"
And override onConfigurationChanged in your Activity class:
#Override
public void onConfigurationChanged(final Configuration newConfig)
{
// Ignore orientation change to keep activity from restarting
super.onConfigurationChanged(newConfig);
}
To avoid this you can use the answer givin here: https://stackoverflow.com/a/2124731/327011
But if you need to destroy the activity (different layouts for portrait and landscape) you can make the AsyncTask a public class (Read here why it shouldn't be private Android: AsyncTask recommendations: private class or public class?) and then create a method setActivity to set the reference to the current activity whenever it is destroyed/created.
You can see an example here: Android AsyncTask in external class