Search code examples
androidandroid-memory

When using nested AsycTask in activity will handler reference remain after onDestroy


I have sample code which very simply does some heavy work and sends a message to a handler to update the UIThread. My concern is with the handler reference i am passing to the constructor. If my activity gets destroyed while the asncTask is still running will the handler reference not be null ?

    public class SomeActivity extends Activity
{
    private static final int UPDATE_BUTTON_TEXT = 1;
    private static final SomeActivity me = null;

    private static Handler handler = new Handler() { 
        public void handleMessage(Message msg)  {
          if (me == null) return;

          switch (msg.what)  {
            case UPDATE_BUTTON_TEXT:
              Button btn = (Button) me.findViewById(R.id.someButton);
              btn.setText((String) msg.obj);
          }
        }
    };

    private View.OnClickListener onClickListener = new View.OnClickListener() {
       public void onClick(View view) {
            new SomeLongRunningTask().execute();
        }
    };

    private static class SomeLongRunningTask extends AsyncTask<Void, Void, Boolean> {

        private Handler handler;

        public SomeLongRunningTask(Handler handler)  {
            this.handler = handler;
        }

        @Override
        protected Boolean doInBackground(Void... voids) {

            try {
                Thread.sleep(30000); // replace with some background logic
            } catch (InterruptedException e) {}

            return true;
        }



          @Override
            protected void onPostExecute(Boolean aBoolean) {
//can the handler be null here if activity is destroyed ????
                Message msg = handler.obtainMessage(UPDATE_BUTTON_TEXT);
                msg.obj = "success"
                handler.sendMessage(msg);
            }
        }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        final Button someButton = (Button) findViewById(R.id.someButton);
        someButton.setOnClickListener(onClickListener);
    }

    @Override
    protected void onStart() {
        super.onStart();

        me = this;
    }

    @Override
    protected void onStop() {
        me = null;

        super.onStop();
    }
}

Solution

  • Yes, the reference of the handler is going to be retained in memory until it has a reference count > 0.

    I think you should use AsyncTask().onProgressUpdate for updating progress on UI, which does what you're trying to do.

    EDIT

    If you're updating ui in onPostExecute then you don't need to use onProgressUpdate(my apologies).

    Just use an interface as a callback function like below:

    private interface Callback {
        void updateUI(String value);
    }
    
    private static class SomeLongRunningTask extends AsyncTask<Void, String, Boolean> {
        private Callback mCallback;
    
        public SomeLongRunningTask(Callback callback)  {
            mCallback = callback;
        }
    
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            mCallback.updateUI("success");
        }
    }
    
    // somewhere else...
    Callback callback = new Callback() {
        @Override
        public void updateUI(String value) {
            Button btn = (Button) me.findViewById(R.id.someButton);
            btn.setText((String) msg.obj);
        }
    };
    
    new SomeLongRunningTask(callback).execute();
    

    Also it doesn't seem right to have a handler instance as a static variable. It will last until the class is unloaded.