I am working on an android app where i need to download some images and start a slide show.
For downloading the images i am using a AsynTask, as soon as the images are downloaded i am using an handler to start the slide show. But during orientation change i am not able to control the handler. Because the handler object is defined with AsyncTask class.
The below is the rough scenario of what i have done:
Class A extends Activity
Class B async = new ClassB(this);
async.execute();
Class B extends AsyncTask implements OnPageChangeListener, OnTouchListener
onPreExecute() -------Nothing in this method
doInBackground -------I am downloading the images here
onPostExecute ---------Handler starts here and runs for every 5 seconds for implementing Slide Show
Now the Problem:
During orientation change i have made sure that AsyncTask is called only once or else it downloads the images again, because of this the handler is not running during orientation change.
The first handler object which was started, it keeps running but the changes does not reflect on the screen.
I was thinking of implementing in a way that the images are downloaded by using the AsyncTask and once the downloading is done, ClassA should gain focus and handler should be implemented in ClassA(only when images are downloaded).
The Interface class naming is according to my case. Please adjust it according to your rquirments. ArtikelDetailsDataLoader my Async task. Which tell my activity that details are loaded.
in your Class B
public interface OndetailsLoadInterface {
public void onDetailsLoad(ArtikelDetailsInfo artikelDetails);
}
OndetailsLoadInterface detailsLoaderInterface;
public void setOnDetailsLoadListener(OndetailsLoadInterface detailsLoader) {
detailsLoaderInterface = detailsLoader;
}
and in onPostExecute
detailsLoaderInterface.onDetailsLoad(artikelDetails);
and in class A
ArtikelDetailsDataLoader mLoader = new ArtikelDetailsDataLoader(
items.get(position).getId(), mContext);
mLoader.setOnDetailsLoadListener(mLoaderLisnter);
and
OndetailsLoadInterface mLoaderLisnter = new OndetailsLoadInterface() {
#Override
public void onDetailsLoad(ArtikelDetailsInfo artikelDetails) {
//do slide show
}
};
Just make a method in your Activity called startSlideShow(position) that you call from your onPostExecute() method, with startSlideShow(0).
On orientation change save your current slide show position, and your downloaded images, and then in your onCreate(Bundle savedInstance) then start the slideshow from the previous position using startSlideShow(restoredPosition);.
If your problem is caused by the Activity restarting when orientation changes, have you considered setting android:configChanges="orientation" in your AndroidManifest.xml entry for the activity, which should prevent the restarts?
Then, if you need to perform any updates to the UI (e.g. switching to a different layout), you can handle onConfigurationChanged(Configuration) and implement it yourself.
Related
I am developing an Android App, which displays some images from an website in MainActivity. There are some other activities which are basically dealing with the offline contents like dealing with database and configuration.
Currently the App is designed in such a way like, when it navigates from MainActivity to any other activities, first it will destroy itself (calling finish()) and then navigate. Likewise if navigated to MainActivity from some other Activity, those activities will be destroyed before moving.
App works fine, initially. But if the user navigates between activities too frequently, then the image downloading task in the Main Page takes very long time.
For Example, if user navigates like
MainActivity --> Activity1 --> MainActivity
In the above case, there is no issue displaying the images in MainActivity initially. But in the next instance, it takes unusual time.
What I am planning to to implement a multi threading scenario, which will create a new thread every time the application trie to navigate to MainActivity and destroy the existing one.
Thread-0 : MainActivity --> Activity1 (to go Main) --> Kill Thread-0 and Create Thread-1
Thread-1 : MainActivity --> Activity1 --> Activity2 (to go to Main) --> Kill Thread-1 and create Thread-2
But I have no clue, how to implement the same. Any suggestion or reference link are most welcomed.
Try to use an AsyncTask like this:
public class MyTask extends AsyncTask<Void, Void, Void> {
private volatile boolean running = true;
#Override
protected void onCancelled() {
running = false;
}
#Override
protected Void doInBackground(Void... params) {
while (running) {
// download
}
return null;
}
}
Activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
task = new MyTask();
task.execute();
}
#Override
public void onDestroy() {
super.onDestroy();
if (task != null) task.cancel(true);
}
But for me the easiest way to download and display images is using Picasso.
Its that simple:
Picasso.with(context)
.load(url)
.resize(50, 50)
.centerCrop()
.into(imageView)
Probably the best way to implement this kind of behavior is with using the AsyncTask. You can start it like this
new YourAsyncTask().execute();
Cancel it like this:
mTask.cancel(true);
And you can use it's doInBackground(), onProgressUpdate() and onPostExecute() to make it perform like you wish (every method is pretty much self explanatory by the name itself).
Did you implement the cache memory logic when loading images.So that if an image is downloaded it would not go for the download again instead it(one activity or the other) will look in the cache.
Create BaseActivity class which extends Activity/AppCompatActivity.
extend BaseActivity class from your MainActivity.
Do all download tasks in BaseActivity using AsyncTask
should do the trick. :)
If you are loading images frequently creating/destroying a Thread would not be a good approach considering the overhead of creating and destroying. AsyncTask is an OK approach but risky for memory leaks. My suggestion would be to handle image loading tasks in a service like an IntentService or create a regular Service with a background thread.
Hi I'm implementing a custom asynctaskloader to load data in the background, and I need the loading process to run even if the user navigated out of the application. The problem is, once the user presses the menu button for example the loader onStopLoading() is called and then the loadInbackgroud() is called to restart the loading process.
Any ideas how can I prevent the loader to restart the loading process every time I navigate out of the program or turn of the screen given that during the loading process I acquire a partial wake lock.
P.S: The loader is attached to a fragment and the fragment RetainInstance is set to true.
Thanks in advance.
Have you considered using an IntentService instead of an AsyncTask?
An IntentService is a component which runs in the background and is not bound to the lifecycle of an activity, thus will not be affected when a fragment/activity is paused/restarted/destroyed. You can still publish progress/results/failures to activity or fragment-based listeners using a ResultReceiver.
A very basic code sample:
public class MyService extends IntentService
{
public MyService()
{
super("MyService");
}
#Override
protected void onHandleIntent(Intent intent)
{
// Perform your loading here ...
publishOutcome("Success");
}
private void publishOutcome(String outcome)
{
Bundle outcomeData = new Bundle();
outcomeData.putString(OUTCOME, outcome);
receiver.send(MY_OUTCOME, outcomeData );
}
}
For a more detailed discussion on AsyncTask vs IntentService have a look at this StackOverflow question.
Is it possible to load a new activity in the background before switching the view to that activity?
For example, I would like to have a slash screen activity that gets called and displays a splash screen. While this splash screen is displayed, the next activity is loaded, and when it is done loading (when it's onCreate() is finished) then the splash screen activity ends, and the new activity is displayed.
I know another option would be to display the splash screen in the new activity, and use async task to load all the data before removing the splash image... but I am stuck on that approach as well. The activity first has to load a fair amount of data, and then it has to dynamically add GUI elements based on that data. Once the GUI is fully loaded, I then want to remove the splash screen. The problem is that I cannot touch the UI thread from doInBackground(). How do I create my activity behind a splash screen, if I cannot update the UI from doInBackground? I know that onProgressUpdate() can access the UI thread, but I can't figure out how to implement it.
Any ideas? Thank you!
Since you don't have an example of your code, I am not sure what kind of data you are loading and how you are dynamically configuring the UI based on the data, but I'll try to answer as much as I can. As a result, the answer may sound a little generic.
First, define 2 layout xml files - one for the splash screen and one for your "main" activity.
So you'll end up with /res/layout/splash_screen.xml and /res/layout/main.xml
In your onCreate(), load the splash_screen layout:
setContentView(R.layout.splash_screen);
In your async task, you will load up whatever data you need to do, and you will save all that data in some sort of data structure. I'm gonna use a LinkedList of String for example's sake.
private class MyTask extends AsyncTask<Uri, Integer, List<String>> {
#Override
protected List<String> doInBackground(Uri... params) {
List<String> myList = new LinkedList<String>();
// load up the list with data you are trying to get
myList.add("foo");
myList.add("bar");
// whatever you return here will be passed in as a parameter to the onPostExecute()
return myList;
}
#Override
protected void onPostExecute(List<String> result) {
setContentView(R.layout.main2);
// set layout elements with data that from the result
TextView myTextView = (TextView) findViewById(R.id.some_label);
myTextView.setText(result.get(0));
// or just call some function you defined in your activity instead
}
}
So basically, have 2 different layout file and use the splash_screen layout, and use the async task the load the data and save it in some data structure you define, and use that data structure to load your UI elements in onPostExecute() after using setContentView() to change back to your main layout.
One special note:
With the above code, it will show the splash screen again and reload all the data again if you rotate the screen. If you want to avoid that, you can use the onSaveInstanceState() and save whatever data you want in the outBundle and read that data back in onCreate's savedInstanceState bundle and load the UI elements back up. This will require a separate thread (or you can just search about it) if you wanted to know more about handling rotation.
One of the solution to solve your problem I can think about is to use one activity for displaying the splash screen and your content. Since you can call setContentView() method at any time (not only in onCreate() method) just define all the views you want in separate XML files and pass the relevant id to setContentView() when it's time to switch.
You could also use one layout with your views and splash screens and hide / unhide attributes. When your data is loading setVisibility to your splash screen to Visible while your root view remain unvisible. When finish loading - do in the opposite way.
Hi i have an AsyncTask in my application called in OnCreate() that retrieve some data over the web and display an indeterminate progress bar while downloading.
The problem is when i start the app the screen remain blank until the AsyncTask is finished.
The code is something like that.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
loadData();
//Several UI Code
startAsyncTasks();
}
private void startAsyncTasks(){
new ConnectingTask().execute();
}
OnCreate() is executed before the Activity is being shown on the screen, so the whole data download process executes before showing the activity.
Solution could be to start AsyncTask in OnStart() method instead of in OnCreate() (also overriden). OnStart() is executed while activity is going to be shown on the screen (see activity lifecycle).
This is the case I have implemented in my app and it works. You should show a progress dialog in OnStart() method, and update and dismiss it in AsyncTask in proper moments.
This is how it looks in my app:
AsyncTask is started in activity OnStart() along with showing dialog box
Data is being downloaded in inner class extending AsyncTask
After data is downloaded the dialog box is dismissed and further data processing initialized (OnPostExecute())
The drawback could be that OnStart() is called on the first activity call, but also after restoring it (for example after minimizing the application). So AsyncTask can be executed few times in opposite to OnCreate(), that is called only once - when activity is created (not when it is restored).
There can also be some issues if You do the jUnit test of such activity with dialog in AsyncTask - let me know if You do - will post some solution
I have a semi-complicated problem and hoping that someone here will be able to help me.
On a click event I create a thread and start a long-running operation based on this method. After the long-running task is completed, it does a callback to another method, which does a post to the handler:
#Override
public void contentSearchModelChanged(Model_ContentSearch csm, ArrayList<Class_Reminder> newRemindersList) {
remindersList = newRemindersList;
mHandler.post(mUpdateDisplayRunnable);
}
Which calls a Runnable:
// post this to the Handler when the background thread completes
private final Runnable mUpdateDisplayRunnable = new Runnable() {
public void run() {
updateDisplay();
}
};
Finally, here is what my updateDisplay() method is doing:
private void updateDisplay() {
if (csModel.getState() != Model_ContentSearch.State.RUNNING) {
if(remindersList != null && remindersList.size() > 0){
r_adapter = new ReminderAdapater(Activity_ContentSearch.this, remindersList, thisListView);
thisListView.setAdapter(r_adapter);
r_adapter.notifyDataSetChanged();
}
}
}
This works beautifully when I do this normally. However, if I change the orientation while the long-running operation is running, it doesn't work. It does make the callback properly, and the remindersList does have items in it. But when it gets to this line:
r_adapter.notifyDataSetChanged();
Nothing happens. The odd thing is, if I do another submit and have it run the whole process again (without changing orientation), it actually updates the view twice, once for the previous submit and again for the next. So the view updates once with the results of the first submit, then again with the results of the second submit a second later. So the adapater DID get the data, it just isn't refreshing the view.
I know this has something to do with the orientation change, but I can't for the life of me figure out why. Can anyone help? Or, can anyone suggest an alternative method of handling threads with orientation changes?
Bara
The problem is that when you change orientations a new activity is spun up from the beginning (onCreate). Your long running process has a handle to the old (no longer visible) activity. You are properly updating the old activity but since it isn't on screen anymore, you don't see it.
This is not an easy problem to fix. There is a library out there that may help you though. It is called DroidFu. Here is a blog post that (much more accurately than I) describes the root cause of what you are seeing and how the DroidFu library combats it: http://brainflush.wordpress.com/2009/11/16/introducing-droid-fu-for-android-betteractivity-betterservice-and-betterasynctask/
Edit: (Adding code for tracking active activity)
In your application class add this:
private Activity _activeActivity;
public void setActiveActivity(Activity activity) {
_activeActivity = activity;
}
public Activity getActiveActivity() {
return _activeActivity;
}
In your Activities, add this:
#Override
public void onResume() {
super.onResume();
((MyApplicationClassName)getApplication()).setActiveActivity(this);
}
Now you can get the active activity by calling MyApplicationClassName.getActiveActivity();
This is not how DroidFu does it. DroidFu sets the active activity in onCreate but I don't feel that is very robust.
I had a similar problem, having a time consuming thread sendEmptyMessage to a handler, which in turn called notifyDataSetChanged on a ListAdapter. It worked fine until I changed orientation.
I solved it by declaring a second handler in the UI thread and make the first handler sendEmptyMessage to this handler, which in turn called notifyDataSetChanged on the ListAdapter. And having the ListAdapter declared as static.
I'm a newbie so I don't know if it is an ugly solution, but it worked for me...
From Jere.Jones description I would assume this works as: The long running process sendEmptyMessage to the handle from the old Activity, which in turn sendEmptyMessage to the handle in the new Activity.