Update Ui From An Asynctaskloader
Solution 1:
It's actually possible. You essentially need to subclass the AsyncTaskloader
and implement a publishMessage()
method, which will use a Handler
to deliver the progress message to any class that implements the ProgressListener
(or whatever you want to call it) interface.
Download this for an example: http://www.2shared.com/file/VW68yhZ1/SampleTaskProgressDialogFragme.html (message me if it goes offline) - this was based of http://habrahabr.ru/post/131560/
Solution 2:
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
:
publicclassMyActivityextendsActivity
{
publicvoidsomeFunction() { /* do some work over here */ }
publicvoidsomeOtherFunction() {
Runnable r = newRunnable() {
@Overridepublicvoidrun() {
while (true)
someFunction();
}
};
newThread(r).start(); // use it, for example here just make a thread to run it.
}
}
the compiler will actually generate something like this:
privatestaticclassAnonymousRunnable {
private MyActivity parent;
publicAnonymousRunnable(MyActivity parent) {
this.parent = parent;
}
@Override
publicvoidrun() {
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:
publicclassMyLoaderextendsAsyncTaskLoader<Something> {
privateObservablemObservable=newObservable();
synchronizedvoidaddObserver(Observer observer) {
mObservable.addObserver(observer);
}
synchronizedvoiddeleteObserver(Observer observer) {
mObservable.deleteObserver(observer);
}
@OverridepublicvoidloadInBackground(CancellationSignal signal)
{
for (inti=0;i < 100;++i)
mObservable.notifyObservers(newInteger(i));
}
}
And in your Activity
class
publicclassMyActivityextendsActivity {
privateObservermObserver=newObserver() {
@Overridepublicvoidupdate(Observable observable, Object data) {
finalIntegerprogress= (Integer) data;
mTextView.post(newRunnable() {
mTextView.setText(data.toString()); // update your progress....
});
}
}
@OverridepublicvoidonCreate(Bundle savedInstanceState) {
super.onCreated(savedInstanceState);
MyLoaderloader= (MyLoader) getLoaderManager().initLoader(0, null, this);
loader.addObserver(mObserver);
}
@OverridepublicvoidonDestroy() {
MyLoaderloader= (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...)
Solution 3:
Answering my own question, but from what I can tell, AsyncTaskLoader
isn't the best to use if you need to update the UI.
Solution 4:
In the class in which you implement LoaderManager.LoaderCallback
(presumably your Activity), there is an onLoadFinished()
method which you must override. This is what is returned when the AsyncTaskLoader
has finished loading.
Solution 5:
The best method is to use LiveData, 100% Working
Step 1: Add lifecycle dependency or use androidx artifacts as yes during project creation
implementation "androidx.lifecycle:lifecycle-livedata:2.1.0"
Step 2: Create the loader class as follow, in loader create in public method to set the livedata that can be observed from activity or fragment. see the setLiveCount method in my loader class.
package com.androidcodeshop.asynctaskloaderdemo;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.MutableLiveData;
import androidx.loader.content.AsyncTaskLoader;
import java.util.ArrayList;
publicclassContactLoaderextendsAsyncTaskLoader<ArrayList<String>> {
privateMutableLiveData<Integer> countLive = newMutableLiveData<>();
synchronized publicvoidsetLiveCount(MutableLiveData<Integer> observer) {
countLive = (observer);
}
publicContactLoader(@NonNullContext context) {
super(context);
}
@Nullable@OverridepublicArrayList<String> loadInBackground() {
returnloadNamesFromDB();
}
privateArrayList<String> loadNamesFromDB() {
ArrayList<String> names = newArrayList<>();
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
names.add("Name" + i);
countLive.postValue(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return names;
}
@OverrideprotectedvoidonStartLoading() {
super.onStartLoading();
forceLoad(); // forcing the loading operation everytime it starts loading
}
}
Step 3: Set the live data from activity and observe the change as follows
package com.androidcodeshop.asynctaskloaderdemo;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.MutableLiveData;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
publicclassMainActivityextendsAppCompatActivityimplementsLoaderManager.LoaderCallbacks<ArrayList> {
private ContactAdapter mAdapter;
private ArrayList<String> mNames;
private MutableLiveData<Integer> countLiveData;
privatestaticfinalStringTAG="MainActivity";
@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNames = newArrayList<>();
mAdapter = newContactAdapter(this, mNames);
RecyclerViewmRecyclerView= findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(newLinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter);
countLiveData = newMutableLiveData<>();
countLiveData.observe(this, newandroidx.lifecycle.Observer<Integer>() {
@OverridepublicvoidonChanged(Integer integer) {
Log.d(TAG, "onChanged: " + integer);
Toast.makeText(MainActivity.this, "" +
integer,Toast.LENGTH_SHORT).show();
}
});
// initialize the loader in onCreate of activity
getSupportLoaderManager().initLoader(0, null, this);
// it's deprecated the best way is to use viewmodel and livedata while loading data
}
@NonNull@Overridepublic Loader onCreateLoader(int id, @Nullable Bundle args) {
ContactLoaderloader=newContactLoader(this);
loader.setLiveCount(countLiveData);
return loader;
}
@OverridepublicvoidonLoadFinished(@NonNull Loader<ArrayList> load, ArrayList data) {
mNames.clear();
mNames.addAll(data);
mAdapter.notifyDataSetChanged();
}
@OverridepublicvoidonLoaderReset(@NonNull Loader loader) {
mNames.clear();
}
@OverrideprotectedvoidonDestroy() {
super.onDestroy();
}
}
Hope this will help you :) happy coding
Post a Comment for "Update Ui From An Asynctaskloader"