Pableras84
Pableras84

Reputation: 1195

It is possible to stop a thread when the user press back key?

I have a method that downloads a image from internet and stores it on a bitmap. While it is downloading the image, it shows a loading dialog. The method works fine, but i want to add the functionality into my app to stop the thread (so stop the download) and dismiss the dialog if the user press the back key on the phone.

public static void getRemoteImage(final String url, final Handler handler) {
        Thread thread = new Thread(){ 
            public void run() {
                try {
                    Looper.prepare();
                    handler.sendEmptyMessage(Util.SHOW_LOADING_DIALOG);
                    final URL aURL = new URL(url);
                    final URLConnection conn = aURL.openConnection();
                    conn.connect();
                    final BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());
                    image = BitmapFactory.decodeStream(bis);
                    bis.close();
                    handler.sendEmptyMessage(Util.HIDE_LOADING_DIALOG); 
                    Looper.loop();  
                }catch (Exception e) {e.printStackTrace();}
            }
        };
        thread.start();
    }

How can i add the functionality to stop the thread when the user press the back key on my activity? I can't find the way on google

EDIT: this is my attempt to use Ovidiu answer, but it doesn't works :(

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event)  {
         if (keyCode == KeyEvent.KEYCODE_BACK) {
              dialogHandler.sendEmptyMessage(Util.HIDE_DIALOG);
              task.cancel(true);
              return true;            
          }
       return super.onKeyDown(keyCode, event);
    }

    private class DownloadTask extends AsyncTask{       
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //INITIALIZATIONS
        }

        @Override
        protected Object doInBackground(Object... params) {
            try {
                dialogHandler.sendEmptyMessage(Util.SHOW_DIALOG);
                final URL aURL = new URL(url);
                final URLConnection conn = aURL.openConnection();
                conn.connect();
                final BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());
                image = BitmapFactory.decodeStream(bis);
                bis.close();
                dialogHandler.sendEmptyMessage(Util.HIDE_DIALOG);   
            }catch (Exception e) {e.printStackTrace();}
            return null;
        }

        @Override
        protected void onCancelled(){
            super.onCancelled();
            System.out.println("Task cancelled");
        }
    }

Upvotes: 3

Views: 928

Answers (3)

Ovidiu Latcu
Ovidiu Latcu

Reputation: 72331

  1. You should be using AsyncTask

  2. To stop it you should be using Activity lifecycle methods such as onStop() or onDestroy() to cancel() the AsyncTask.

  3. To not reinvent the wheel you should probably use some libraries which do exact that (load remote images) like Ignition.

Upvotes: 6

Vincent Mimoun-Prat
Vincent Mimoun-Prat

Reputation: 28561

A general advise about managing an AsyncTask within an activity.

public class MyActivity extends Activity {

  // Restore the task we kept alive 
  public void onCreate(Bundle savedInstanceState) {
    myTask = (MyTask) getLastNonConfigurationInstance();
    if (myTask!=null) myTask.connect(this);
  }

  // Keep the task alive on screen rotation for example 
  public Object onRetainNonConfigurationInstance() {
    myTask.disconnect();
    return myTask;
  }

  // Kill the task when we stop. If you wanna keep the task alive, you don't kill it here. 
  protected void onStop() {
    killTask();
    super.onStop();
  }

  // Disconnect the task from this activity and cancel it if needed
  private void killTask() {
    if (myTask!=null) {
      myTask.cancel(true);
      myTask.disconnect();
      myTask = null;
    }
  }

  // Kill task if running, create new one, link it to us and run it
  private void launchTask() {
    killTask();
    myTask = new MyTask();
    myTask.connect(this).execute();
  }

  void handleTaskCancelled() {
    Toast.makeText(this, "Task cancelled", Toast.LENGTH_SHORT).show();
  }

  void handleTaskCompleted(String result) {
    Toast.makeText(this, "Task done: " + result, Toast.LENGTH_SHORT).show();
  }

  private MyTask myTask;
}

// Either a standalone class, or a static inner class of your activity

public class MyTask extends AsyncTask<Void, Void, String> {

  @Override
  protected void onPostExecute(String result){
    if (activityRef.get()!=null) {
      activityRef.get().handleTaskCompleted(result);
    }
  }

  @Override
  protected void onCancelled(){
    if (activityRef.get()!=null) {
      activityRef.get().handleTaskCancelled();
    }
  }

  public MyTask connect(MyActivity a) {
    activityRef = new WeakReference<MyActivity>(a);
  }

  public void disconnect() {
    activityRef.clear();
  }

  // Never keep a strong reference to an activity, you might leak memory
  WeakReference<MyActivity> activityRef = new WeakReference<MyActivity>(null);
}

Upvotes: 1

Vincent Mimoun-Prat
Vincent Mimoun-Prat

Reputation: 28561

Since most of the long work is done by a single call BitmapFactory.decodeStream(bis);, you won't be able to really stop the thread instantaneously anyway...

Some possible way of doing it though would be (doing as you do with a thread, but this would be better in an AsyncTask, which is essentially putting the run() code into the doInBackground() function):

public class ImageThread extends Thread {
  Bitmap image = null;

  public void run() {
    // 1. download the stream to a byte[]
    while data available in input stream
      read a few bytes and store them
      if (isInterrupted()) cleanUpAndExit();
    endwhile

    // 2. Create bitmap from data
    image = BitmapFactory.decodeByteArray(...)
  }  
}

BitmapFactory.decodeByteArray

Oh. And the missing bit ... You then need to override the activity's onBackPressed, or the activity's onStop method to insert the code to cancel the thread: imageThread.interrupt();

Upvotes: 0

Related Questions