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.
Related
I have some fragments loaded in a ViewPager, where each "page" is loaded from a row in a cursor. Each fragment shows an image (JPEG) on the device. When the user dismisses the fragment (i.e swipe/page change, hits back/up, or just closes the app entirely) I want to invoke a method which opens the JPEG file for writing and does an update of its metadata. The actual work is eventually handled by the Apache Commons Imaging library.
I've implemented this by invoking my saveToFile() method from each fragment's life cycle onStop() handler. Does this mean the entire file operation ends up running on the UI thread? Should I definitely set up an AsyncTask for this?
Say the file write for some reason suddenly (for some jpeg) should take a long time, eg 2 minutes. What would then happen? Would the UI just wait (freeze) at this page/fragment before resuming? Or would the process (write to file) carry on "in the background" somehow? Or would the process just be killed, stopped short mid-process?
The way I have this wired up currently (onStop invoking saveToFile(), which calls up the imaging library and then updates the file) seems to work as it should. Even if I end the app, I still see my Toast text popping up, saying "Writing to file..." Seemingly, the process is never disturbed, and I can't say I'm experiencing any UI lag.
onStop() handler. Does this mean the entire file operation ends up
running on the UI thread? Should I definitely set up an AsyncTask for
this?
YES
An AsyncTask has several parts: a doInBackground method that does, in fact, run on a separate thread and the onPostExecute method that runs on the UI thread.
You can also use some sort of observer pattern such as EventBus to run async and post results to the UI.
Say the file write for some reason suddenly (for some jpeg) should
take a long time, eg 2 minutes. What would then happen? Would the UI
just wait (freeze)
The application will crash because Android will forcefully close it due to ANR (Application Not Responding).
Refer to the official documentation for details on this: https://developer.android.com/training/articles/perf-anr.html
Android applications normally run entirely on a single thread by
default the "UI thread" or "main thread"). This means anything your
application is doing in the UI thread that takes a long time to
complete can trigger the ANR dialog because your application is not
giving itself a chance to handle the input event or intent broadcasts.
Therefore, any method that runs in the UI thread should do as little
work as possible on that thread. In particular, activities should do
as little as possible to set up in key life-cycle methods such as
onCreate() and onResume(). Potentially long running operations such as
network or database operations, or computationally expensive
calculations such as resizing bitmaps should be done in a worker
thread (or in the case of databases operations, via an asynchronous
request).
The most effective way to create a worker thread for longer operations
is with the AsyncTask class.
Here is what I recommend though. Use the above mentioned, EventBus and create a BaseActivity which will automatically save the data for you onClose() by firing an event that runs Async. You then extend that base activity in all the places where you need autosave capabilities.
Here's what I mean with an example that uses EventBus.
public abstract class BaseActivity extends Activity{
#Override
protected void onResume(){
if(!EventBus.getDefault().isRegistered(this))
EventBus.getDefault().register(this);
super.onResume();
}
#Override
protected void onDestroy() {
if(EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
super.onDestroy();
}
#Override
protected void onStop() {
super.onStop();
//We fire event and pass the current parent class that inherited this base.
EventBus.getDefault().post(new EventBusProcessMySaveData(this.getClass()));
}
}
//Your model class to use with EventBus
public final class EventBusProcessMySaveData{
private final Class className;
public EventBusProcessMySaveData(final Class className){
this.className = className;
}
public Class getClassName(){
return this.className;
}
}
public class MyMainActivity extends BaseActivity{
//Do you standard setup here onCreate() and such...
//Handle Event for Saving Operation, async.
//This will fire everytime theres an onClose() IN ANY activity that
//extends BaseActivity, but will only process if the class names match.
#Subscribe(threadMode = ThreadMode.ASYNC)
public void methodNameDoesNotReallyMatterHere(final EventBusProcessMySaveData model){
//We make sure this is the intended receiving end by comparing current class name
//with received class name.
if(model.getClassName().equals(this.getClass())){
//Do whatever you need to do that's CPUintensive here.
}
}
}
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.
I m new in android, I'm not much aware about services.i have an activity class with a UI, i want to make this activity class runs in background, when i click the back button. how to make my activity runs in background like a service, plz help me..
You cannot really run an Activity on background! When an activity is not on foreground it gets to onStop and then the system could terminate it, to release resources, by onDestroy method! see Activity Lifecycle
In order to run on background you need to create a Service or IntentService
Checkout android javadoc about Services here and here or IntentService
and here is a third-party Android Service Tutorial
Edit: you may also need a communication between your service and your activity so you can get through that: Example: Communication between Activity and Service using Messaging
If you simply want your activity runs in back Try using
moveTaskToBack(true);
It seems like you want to run an activity in background when it quits. However, activity can't be run unless it's on foreground.
In order to achieve what you want, in onPause(), you should start a service to continue the work in activity. onPause() will be called when you click the back button. In onPause, just save the current state, and transfer the job to a service. The service will run in the background when your activity is not on foreground.
When you return to your activity later, do something in the onResume() to transfer the service 's job to your activity again.
You should read the developer guide on Threads: http://developer.android.com/guide/components/processes-and-threads.html
Specifically the function doInBackground()
Example from page:
public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
/** The system calls this to perform work in a worker thread and
* delivers it the parameters given to AsyncTask.execute() */
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
/** The system calls this to perform work in the UI thread and delivers
* the result from doInBackground() */
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}
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.
I have an activity that lets a user upload a photo to the web and instead of having them wait indefinitely for the image to upload I have it being done in an asynctask that handles the image upload. When the user presses the button to upload the image the I have the activity that uploads the image close and the app goes back to the earlier activity which has its own downloading process. What Im trying to figure out is what to do in the asynctask onPostExecute to get the download process in the other activity to run again. I tried just using this:
#Override
protected void onPostExecute(String result){
if(result!=null){
Main.DownloadManager.startDownloading();
}else if(result==null){
Toast.makeText(getBaseContext(),"Upload failed",timetoshow).show();
}
}
But I get no response in the main activity because I know Im not actually calling the running activity but just calling the method.....somewhere thats not instantiated or running....or at least thats what I think this does. So I guess my question is how can I pass the message to run the startDownloading() method from the asynctask of the closed activity in the currently running Main activity. Any help would be super much appreciated.
EDIT:
I was able to solve this by implementing one of the solutions bellow:
#Override
protected void onPostExecute(String result){
if(result!=null){
CustomContextClass.*Main*.DownloadManager.startDownloading();
}else if(result==null){
Toast.makeText(getBaseContext(),"Upload failed",timetoshow).show();
}
}
You may use a static method in DownloadManager
private static instance;
public void onCreate() {
instance = this;
}
public static void startDownloading() {
instance.downloadNow();
}
onProgressUpdate() of AnsycTask will tell you about the progress .so here you can decide hewhat data you want to send to next activity . in next activity deal with remaining not downloaded portion . thinks depends on server side implementation of data chunk .
your complete requirement will be required for more suggestions.
Having processes hanging around from a dead Activity is dangerous, as it has a high potential to cause leaks. Why not start the AsyncTask in the Activity you intend to show progress? That would make the most sense.
you can do something like below, if I'm not mistaken I think the AsyncTask has written DownloadManager class.
I hope you have the AsyncTask written as a InnerClass similar to this in that class.
private class InitTask extends AsyncTask<String, String, String> {....
Also you should have to have a getter to return InitTask object,
public InitTask getInitTask(){
return new InitTask();
}
So in the other class, you can call this like below,
new YourOtherClass().getInitTask().execute();