Update UI from an AsyncTaskLoader

后端 未结 5 1857
庸人自扰
庸人自扰 2021-02-10 01:21

I\'ve converted my AsyncTask to an AsyncTaskLoader (mostly to deal with configuration changes). I have a TextView I am using as a progres

5条回答
  •  名媛妹妹
    2021-02-10 01:49

    Emm... you shouldn't be doing this.

    because how an anonymous class access parent class Method or Field is by storing an invisible reference to the parent class.

    for example you have a Activity:

    public class MyActivity
        extends Activity
    {
        public void someFunction() { /* do some work over here */ }
    
        public void someOtherFunction() {
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    while (true)
                        someFunction();
                }
            };
            new Thread(r).start(); // use it, for example here just make a thread to run it.
        }
    }
    

    the compiler will actually generate something like this:

    private static class AnonymousRunnable {
        private MyActivity parent;
        public AnonymousRunnable(MyActivity parent) {
            this.parent = parent;
        }
    
        @Override
        public void run() {
            while (true)
                parent.someFunction();
        }
    }
    

    So, when your parent Activity destroys (due to configuration change, for example), and your anonymous class still exists, the whole activity cannot be gc-ed. (because someone still hold a reference.)

    THAT BECOMES A MEMORY LEAK AND MAKE YOUR APP GO LIMBO!!!

    If it was me, I would implement the "onProgressUpdate()" for loaders like this:

    public class MyLoader extends AsyncTaskLoader {
        private Observable mObservable = new Observable();
        synchronized void addObserver(Observer observer) {
            mObservable.addObserver(observer);
        }
        synchronized void deleteObserver(Observer observer) {
            mObservable.deleteObserver(observer);
        }
    
        @Override
        public void loadInBackground(CancellationSignal signal)
        {
            for (int i = 0;i < 100;++i)
                mObservable.notifyObservers(new Integer(i));
        }
    }
    

    And in your Activity class

    public class MyActivity extends Activity {
        private Observer mObserver = new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                final Integer progress = (Integer) data;
                mTextView.post(new Runnable() {
                    mTextView.setText(data.toString()); // update your progress....
                });
            }
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreated(savedInstanceState);
    
            MyLoader loader = (MyLoader) getLoaderManager().initLoader(0, null, this);
            loader.addObserver(mObserver);
        }
    
        @Override
        public void onDestroy() {
            MyLoader loader = (MyLoader) getLoaderManager().getLoader(0);
            if (loader != null)
                loader.deleteObserver(mObserver);
            super.onDestroy();
        }
    }
    

    remember to deleteObserver() during onDestroy() is important, this way the loader don't hold a reference to your activity forever. (the loader will probably be held alive during your Application lifecycle...)

提交回复
热议问题