benkdev
benkdev

Reputation: 673

Handler call to notifyDataSetChanged() not executing

I have an Handler registered in an Activity. handleMessage() calls notifyDataSetChanged on an Adapter. Things work while the Activity has initial focus. However, when I navigate out of the Activity and back in, notifyDataSetChanged() does not work.

FileAdapter is an ArrayAdapter. MergeAdapter is a custom class by CommonsWare. _mergeAdapter contains _fileAdapter.

Activity code:

 public void setUpDownloadHandler() {
// Define the Handler that receives messages from the thread and update the progress
_downloadHandler = new Handler() {
   public void handleMessage(Message message) {
       super.handleMessage(message);
       String fileId = (String) message.obj;
       int progress = message.arg1;
       FileInfo tempFile = null;
       for (FileInfo file: _files) {
   if (file.getFileId().equals(fileId)) {
                    file.setDownloadProgress(progress);
                    tempFile = file;
                }
            }
           if (tempFile != null) {
               _files.remove(tempFile);
               _files.add(tempFile);
           }
           _fileAdapter.notifyDataSetChanged();
           _mergeAdapter.notifyDataSetChanged();
       }
   };
}

Passing the handler:

RunnableTask task = new DownloadFileRunnableImpl(application, the_workspace_url, the_file_info, the_workspace_info.getTitle(), the_internal_storage_directory,
                _downloadHandler);

Background thread code:

if(temp > previous) {
    Message message = new Message();
    message.arg1 = _currentProgress.intValue();
    message.obj = _fileId;
    _progressHandler.sendMessage(message);
    previous = temp;
}

The other piece of information is that I'm passing the handler through a Binder and then into the runnable. I do this to run the background thread in a Service. I don't think this is the problem.

EDIT: It seems like the handler is not associated with the activity the second time it is navigated to (perhaps because onCreate creates a new handler). Is there a way to re-associate or retain the old handler?

Update The activity is being destroyed when it loses focus to another activity.

Upvotes: 2

Views: 1404

Answers (2)

flobacca
flobacca

Reputation: 978

Here is my answer, I relied heavily on http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentRetainInstance.html

Really just took their code and changed it so that I have to remake the fragment everytime I want to start the thread (work) again. And it communicates with the Activity through a handler.

public class Main extends Activity implements WorkProgressListener {

private static final String TAG = "tag";

private Handler handler;

private Button startWorkBtn;

private ProgressDialog progressDialog;

private boolean onSaveInstanceFlag = false;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.i(TAG,"Main onCreate " + Utils.getThreadId());

    setContentView(R.layout.main);

    handler = new ProgressHandler();

    startWorkBtn = (Button)this.findViewById(R.id.start_work_btn);
    startWorkBtn.setEnabled(false);
    startWorkBtn.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick (View v) {
        Log.i("tag","Main: startWorkBtn onClick ");
        startWorkBtn.setEnabled(false);
        FragmentManager fm = getFragmentManager();
        Fragment workF = (Fragment)fm.findFragmentByTag("work");
        if (null == workF) {
            workF = new WorkFragment();
            Log.i(TAG,"Main new WorkF" + Utils.getThreadId());

            startProgressDialog(true);
            startWorkBtn.setEnabled(false);
            fm.beginTransaction().add(workF, "work").commit();
            Log.i(TAG,"Main add(workF) " + Utils.getThreadId());

        }   
        else {
            // should never be able to get here.
        }               
    }           
});

    FragmentManager fm = getFragmentManager();

    Fragment loadingFragment = fm.findFragmentByTag("work");
    Log.i(TAG,"Main findFragment " + Utils.getThreadId());

    if (null == loadingFragment) {
        this.startWorkBtn.setEnabled(true);
    }  
    else {
        // could also decide to show progress dialog based on savedInstanceState
        this.startProgressDialog(true);
    }

} // end onCreate

@Override
public void onRestart() {
    Log.i(TAG,"Main onRestart " + Utils.getThreadId() );
    super.onRestart();
    this.onSaveInstanceFlag = false;
}


@Override
public void onResume () {
    Log.i(TAG,"Main onResume " + Utils.getThreadId());
    super.onResume();
    this.onSaveInstanceFlag = false;
}


@Override
public void onSaveInstanceState (Bundle savedInstanceState) {
    Log.i(TAG,"Main onSaveInstanceState "+ Utils.getThreadId());

    this.onSaveInstanceFlag = true;

    super.onSaveInstanceState(savedInstanceState);
    if (null != this.progressDialog) {
        savedInstanceState.putBoolean("progressDialog", true);
    }
    else {
        savedInstanceState.putBoolean("progressDialog", false);
    }
}

@Override
public void onStop () {
    Log.i(TAG,"Main onStop " + Utils.getThreadId());
    super.onStop();
}

@Override
public void onDestroy () {
    Log.i(TAG,"Main onDestroy " + Utils.getThreadId());     
    super.onDestroy();
    this.closeProgressDialog();
    this.handler.removeCallbacksAndMessages(null);
}   


public class ProgressHandler extends Handler {

    @Override
    public void handleMessage (Message msg) {
        Log.i(TAG,"Main ProgressDialogHandler handleMessage");
        Bundle b = msg.getData();           
        boolean isDone = b.getBoolean("isDone");
        String tag = b.getString("tag");

        if (isDone && !onSaveInstanceFlag) {
            FragmentManager fm = getFragmentManager();
            Fragment loader = (Fragment)fm.findFragmentByTag(tag);

            fm.beginTransaction().remove(loader).commit();

            closeProgressDialog();
            Main.this.startWorkBtn.setEnabled(true);
        }
    }       
}


@Override
public void sendProgress(String tag, int progress, int max) {
    if ( progress == max) {
        Log.i(TAG,"Main sendProgress " + Utils.getThreadId());
        Message message = handler.obtainMessage();          
        Bundle b = new Bundle();
        b.putBoolean("isDone", true);
        b.putString("tag",tag);
        message.setData(b);
        this.handler.sendMessage(message);
    }       

}

private void startProgressDialog(boolean show) {
    this.progressDialog = new ProgressDialog(this);
    this.progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    this.progressDialog.setMessage("loading");
    this.progressDialog.setCancelable(false);
    this.progressDialog.show();
}

private void closeProgressDialog() {
    if (null != this.progressDialog) {
        progressDialog.cancel();
        this.progressDialog = null;
    }       
}   

} // end Main

public class WorkFragment extends Fragment {

private static final String TAG = "tag";

private boolean mReady = false;
private boolean mQuiting = false;

private boolean done = false;

public WorkFragment () {}

final Thread mThread = new Thread() {
    @Override
    public void run () {

    synchronized(this) {
            while (!mReady) {
                Log.i(TAG,"WorkF notReady"+ Utils.getThreadId());               

                if (mQuiting) {
                    return;
                }
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        } // end synchronized


        Log.i(TAG,"WorkF starting work "+ Utils.getThreadId());             

        try {
            Log.i(TAG,"WorkF about to sleep"+ Utils.getThreadId());             

            Thread.currentThread().sleep(10000l);

            Log.i(TAG,"WorkF almost finished "+ Utils.getThreadId());               

            done = true;

        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }

        synchronized(this) {
            while (!mReady) {
                Log.i(TAG,"Activity notReady"+ Utils.getThreadId());                

                if (mQuiting) {
                    return;
                }
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }

            ((WorkProgressListener)getActivity()).sendProgress(WorkFragment.this.getTag(), 100, 100);

        } // end synchronized 2
    }

};


@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
     Log.i(TAG,"WorkF, onAttach: "+ Utils.getThreadId());   

}

 @Override
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     Log.i(TAG,"WorkF, onCreate: "+ Utils.getThreadId());   

     setRetainInstance(true);

     mThread.start();
 }

 @Override
 public void onActivityCreated(Bundle savedInstanceState) {
     super.onActivityCreated(savedInstanceState);
     Log.i(TAG,"WorkF, onActivityCreated: "+ Utils.getThreadId());  

     if (done) {
         ((WorkProgressListener)getActivity()).sendProgress(WorkFragment.this.getTag(), 100, 100);
     }

     synchronized (mThread) {
         mReady = true;
         mThread.notify();
     }
 }

 @Override
 public void onStart()
 {
     super.onStart();
     Log.i(TAG,"WorkF, onStart: "+ Utils.getThreadId() );   

 }

 @Override
 public void onDestroy() {
     synchronized (mThread) {
         mReady = false;
         mQuiting = true;
         mThread.notify();
     }

     super.onDestroy();
 }

 @Override
 public void onDetach() {
     synchronized (mThread) {
         mReady = false;
         mThread.notify();
     }

     super.onDetach();
 }

 public void restart() {
     synchronized (mThread) {
         mThread.notify();
     }
 }
}// end WorkFragment

public interface WorkProgressListener {

public void sendProgress (String tag, int progress, int max);

}

Upvotes: 0

flobacca
flobacca

Reputation: 978

I would try putting a log message in your activity's onDestroy method to see if it is getting destroyed, when you navigate away from your activity. So your task may have the handler from the old activity.

Upvotes: 1

Related Questions