Error with the simple code using Loaders in android - android

I was trying to refactor my code and move my code from Async Task to a loader. I came to know about the benefits of loaders through the Android Performance video series Loaders Android Performance
I know why Loaders are used and what classes it has and stuff (The theory). However I am unable to grasp the working concept and thus wrote this poor code. Thus I am also not able to debug it.
**EDIT: I was able to make it work but I still think I am calling it in a wrong manner.
new EarthquakeAsyncTaskLoader(this).forceLoad();
If anyone can help me out, on this.........**
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import java.io.IOException;
import java.util.ArrayList;
public class EarthQuakeActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<ArrayList<EarthQuakes>> {
ArrayList<EarthQuakes> earthquakes = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_earth_quake);
getSupportLoaderManager().initLoader(1, null, this);
}// End of onCreate
#Override
public Loader<ArrayList<EarthQuakes>> onCreateLoader(int id, Bundle args) {
**new EarthquakeAsyncTaskLoader(this).forceLoad();**
}
#Override
public void onLoadFinished(Loader<ArrayList<EarthQuakes>> loader, ArrayList<EarthQuakes> data) {
}
#Override
public void onLoaderReset(Loader<ArrayList<EarthQuakes>> loader) {
}
public class EarthquakeAsyncTaskLoader extends AsyncTaskLoader<ArrayList<EarthQuakes>> {
public EarthquakeAsyncTaskLoader(Context context) {
super(context);
}
#Override
protected void onStartLoading() {
// If the data is there, don't start again
if (earthquakes != null) {
deliverResult(earthquakes);
} else {
//Start the loader
forceLoad();
}
}
#Override
public ArrayList<EarthQuakes> loadInBackground() {
// Get the populated list from QueryUtils java class
try {
// Here in QueryUtils, I am making an HTTP network call
// Thus it has to be done in a helper background thread
earthquakes = QueryUtils.getArrayList();
} catch (IOException e) {
e.printStackTrace();
}
return earthquakes;
}
#Override
public void deliverResult(ArrayList<EarthQuakes> data) {
// Feed the adapter with data and display it
ListView earthquakesList = (ListView) findViewById(R.id.listV);
final EarthQuakeAdapter adapter = new EarthQuakeAdapter(getApplicationContext(), data);
earthquakesList.setAdapter(adapter);
earthquakesList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
EarthQuakes currentEarthquake = adapter.getItem(i);
Uri earthquakeUri = Uri.parse(currentEarthquake.getUrl());
// Create a new intent to view the earthquake URI
Intent websiteIntent = new Intent(Intent.ACTION_VIEW, earthquakeUri);
// Send the intent to launch a new activity
startActivity(websiteIntent);
}
});
}
}//End of Async Task Loader
//EarthQuakes is my class. I don't think you'll need this. But anyway:
public class EarthQuakes {
private double mMagnitude;
private String mLocationSmallText;
private String mLocationMainText;
private String mDateOfEarthquake;
private String mUrl;
// Default Constructor
public EarthQuakes(Double mag, String locationSmallText, String locationMainCityName, String dateE, String Url) {
this.mMagnitude = mag;
this.mLocationSmallText = locationSmallText;
this.mLocationMainText = locationMainCityName;
this.mDateOfEarthquake = dateE;
this.mUrl = Url;
}
// Public getters
public Double getMagnitude() {
return mMagnitude;
}
public String getLocationSmallTextEarthquake() {
return mLocationSmallText;
}
public String getLocationLargeTextEarthquake() {
return mLocationMainText;
}
public String getDateOfEarthquake() {
return mDateOfEarthquake;
}
public String getUrl() {
return mUrl;
}
}

This alternative will also work:
getSupportLoaderManager().initLoader(1, null, this).forceload();
However, this is just a way around an issue with loaders that is mentioned here.
This issue happens if you are using AsyncTaskLoader that is not a CursorLoader.
You need to implement onStartLoading() and handle calling forceLoad() there. Highly recommend going through the issue page.
If you are using multiple loaders throughout your app and don't want to implement onStartLoading() every time. Here's a custom loader class you can include in your app and inherit from this instead of the usual AsyncTaskLoader:
WrappedAsyncTaskLoader.java(Original Author: Alexander Blom)
public abstract class WrappedAsyncTaskLoader<D> extends AsyncTaskLoader<D> {
private D mData;
/**
* Constructor of <code>WrappedAsyncTaskLoader</code>
*
* #param context The {#link Context} to use.
*/
public WrappedAsyncTaskLoader(Context context) {
super(context);
}
/**
* {#inheritDoc}
*/
#Override
public void deliverResult(D data) {
if (!isReset()) {
this.mData = data;
super.deliverResult(data);
} else {
// An asynchronous query came in while the loader is stopped
}
}
/**
* {#inheritDoc}
*/
#Override
protected void onStartLoading() {
super.onStartLoading();
if (this.mData != null) {
deliverResult(this.mData);
} else if (takeContentChanged() || this.mData == null) {
forceLoad();
}
}
/**
* {#inheritDoc}
*/
#Override
protected void onStopLoading() {
super.onStopLoading();
// Attempt to cancel the current load task if possible
cancelLoad();
}
/**
* {#inheritDoc}
*/
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
this.mData = null;
}
}

Related

How to use Retrofit and Loader class in the fragment

I used Retrofit to receive the json from my RESTful, it is fine. And I tried to implement the Loader class to maker the data loading logic more clear instead of putting it in a onCreateView method to load it, which is not quite a clear logic for loading data. However, I found a bit confused if I tried to use AsyncTaskLoader( which one supposed to receive the data from asynchronous process) for my retrofit. And I stuck in this point. Retrofit is already an asynchronous process and I wonder should I used the asynchronous call or synchronous call in the retrofit inside the AsyncLoader class.
package generic.fragment;
import android.databinding.ViewDataBinding;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import java.util.ArrayList;
import java.util.List;
import generic.adapter.BaseListAdapter;
public abstract class SwipedLoaderListFragment<Bean, Adapter extends BaseListAdapter<Bean, ? extends ViewDataBinding>> extends SwipedListFragment<Bean, Adapter> implements LoaderManager.LoaderCallbacks<List<Bean>> {
public SwipedLoaderListFragment(FragConfig pFragConfig) {
super(pFragConfig);
}
#Override
public List<Bean> loadData(String query) {
List<Bean> list = new ArrayList<>();
return list;
}
#Override
public void refreshing() {
getLoaderManager().restartLoader(0, null, this);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getLoaderManager().initLoader(0, null, this).forceLoad();
}
#Override
public void onLoadFinished(Loader<List<Bean>> loader, List<Bean> data) {
mAdapter.clear();
mAdapter.addAll(data);
}
#Override
public void onLoaderReset(Loader<List<Bean>> loader) {
mAdapter.clear();
}
}
And this is the fragment I used.
public class LocListFragment extends SwipedLoaderListFragment<String, SimpleStringAdapter> {
public LocListFragment() {
super(new FragConfigBuilder(R.layout.swiped_list).setEnableSwipe(false).setFilterable(true).setEnableDivider(true).build());
}
#Override
public void query(String query) {
super.query(query);
mAdapter.filter(query);
}
#Override
public void queryWhenTextChanged(String query) {
super.queryWhenTextChanged(query);
mAdapter.filter(query);
}
#Override
public SimpleStringAdapter initListAdapter() {
return new SimpleStringAdapter(getActivity(), loadData("")) {
#Override
public ListItemStringBinding bind(ListItemStringBinding pBinding, String pS, int pPosition) {
pBinding.setText(pS);
return pBinding;
}
};
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent lIntent = new Intent();
lIntent.putExtra(SCConstants.PARAM_LOC, mAdapter.getItem(position));
getActivity().setResult(Activity.RESULT_OK, lIntent);
getActivity().finish();
}
#Override
public String getHintStr() {
return "Input Location";
}
#Override
public String getSearchTitle() {
return "Location Search";
}
#Override
public Loader<List<String>> onCreateLoader(int id, Bundle args) {
return new AsyncTaskLoader<List<String>>(getActivity()) {
#Override
public List<String> loadInBackground() {
//here will be the retrofit call
return null;
};
}
}
Since the Loader's loadInBackground method is already asynchronous, it would probably be easier to use a synchronous retrofit call (i.e., use execute rather than enqueue).
For your loader to work, you will also need to override onStartLoading. An implementation might look something like this:
public class MyLoader<List<String>> extends AsyncTaskLoader {
List<String> mResult;
#Override
public List<String> loadInBackground() {
mResult = myHttpApi.execute()...
return mResult;
}
#Override
protected void onStartLoading() {
if (mResult != null) {
deliverResult(mResult);
}
if (mResult == null || takeContentChanged()) {
forceLoad();
}
}
}

RunonUI Thread blocks a AsynTask in android from executing to completion

I have an issue with runOnuiThread and AsyncTask getting called together.
My AsynchTask gets data to populate a listView through runOnUIThread call.
This Asych Task can get data even when UI is not in focus . It starts from a UI screen and runs until application is logged out.
Now data coming from this Task can populate only a particular listview.
Now if i invoke another Asynch Task from another view using call executeOnExecutor call for AsynchTask, the Asynch Task does not run to compeltion. It locks up.
If I comment out code for the never ending AsychTask called Receiver.. then all UI's listview get populated and no Asych Task locks.
This Receiver waits on a REST API call for response to return but since I am running through executeonExecutor call, it should be parallel processing.
I need to have the receiver running all the time as that is an integral of my application.
What strategy can I use here to fix this issue.
Here are my code snippets.
public class Receiver {
private final static String QUEUE_NAME = "hello";
private String m_ErrorMessage;
private IRunOnUIThreadCallback iRunOnUIThreadCallback;
private Send m_Received;
private int m_TimeoutDuration;//how long the reading of new message waits in milli seconds
public void SetCallback(IRunOnUIThreadCallback runOnUIThreadCallback)
{
iRunOnUIThreadCallback = runOnUIThreadCallback;
}
public void SetTimeoutDuration(int timeout)
{
m_TimeoutDuration = timeout;
}
public void StartReceiver(Send receiverInfo)
{
String receivedInfo = null;
try {
new ReceiveInfo ().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, receiverInfo);
}
catch(Exception exp)
{
m_ErrorMessage = exp.getMessage();
}
}
private class ReceiveInfo extends AsyncTask<Send, Void, Send>
{
//initiate vars
public receive() {
super();
//my params here
}
protected Message doInBackground(Send... receiverInfo)
{
Send recv=null;
try {
PreferenceSingleton single = PreferenceSingleton.getInstance();
final User user = single.getUser();
final SvcApi svc = LoginAuthSvc.init();
Send send=(Send)receiverInfo[0];
send.setUserId(user.getUsername());
//dxbrem
while (true) {
recv=svc.receive(send);
String str= recv.get();
if ((str == null || (str.trim().length() == 0))) {
continue;
}
//DJ uncomment
iRunOnUIThreadCallback.RunAfterIsReceived(recv);
//messages.add(message);
System.out.println(" [x] Received '" + recv + "'");
}
}catch(Exception exp)
{
m_ErrorMessage = exp.getMessage();
}
return recv;
}
}
public String getErrorMessage() {
return m_ErrorMessage;
}
}
public interface IRunOnUIThreadCallback {
public void RunAfterIsReceived(ByteSent m);
public void RunAfterIsReceived(Send m);
}
The class that handles this.. has the following code and
public class MainFragment extends Fragment implements MFragment.OnFragmentInteractionListener, IRunOnUIThreadCallback {
private Receiver mReceiver;
public void SetUICallbackOnMessageReceiver()
{
mReceiver.SetCallback(this);
}
private void callRunUIThread(final SentInfo m) {
getActivity().runOnUiThread(new Runnable() {
public void run() {
if (m!= null) {
mGridArray.add(message);
if (mListAdapter != null) {
mListAdapter.notifyDataSetChanged();
mListView.setSelection(mListAdapter.getCount());
mListView.smoothScrollToPosition(mListAdapter.getCount());
}
}
}
}); // end of runOnUiThread
}
#Override
public void RunAfterIsReceived(ByteSent m) {
}
#Override
public void RunAfterIsReceived(Sent m) {
SentInfo m= new SentInfo(false, recv.getInfo());
callRunUIThread(msg);
}
mListAdapter is the ListAdapater
mListView is the ListView
Here is the AsynchTask code
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
public class CallableTask<T> extends AsyncTask<Void,Double,T> {
private static final String TAG = CallableTask.class.getName();
public static <V> void invoke(Callable<V> call,Activity activity, TaskCallback<V> callback){
new CallableTask<V>(activity,call, callback).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR );
}
private Callable<T> callable_;
private AsyncTask<Void, Void, String> asyncTask_;
private Context context;
private Activity activity;
private Fragment fragmentActivity;
private android.support.v4.app.Fragment dynamicFragment;
private TaskCallback<T> callback_;
private Exception error_;
public CallableTask(Fragment actvy,Callable<T> callable, TaskCallback<T> callback) {
callable_ = callable;
callback_ = callback;
fragmentActivity=actvy;
}
public CallableTask(Activity actvy,Callable<T> callable, TaskCallback<T> callback) {
callable_ = callable;
callback_ = callback;
activity=actvy;
}
#Override
protected T doInBackground(Void... ts) {
T result = null;
try{
result = callable_.call();
} catch (Exception e){
Log.e(TAG, "Error invoking callable in AsyncTask callable: " + callable_, e);
error_ = e;
}
return result;
}
#Override
protected void onPostExecute(T r) {
if(error_ != null){
callback_.error(error_);
}
else {
callback_.success(r,activity);
}
}
public static <V> void invoke(Callable<V> call, Fragment _frg, TaskCallback<V> callback) {
new CallableTask<V>(_frg,call, callback).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR );
}
// public CallableTask(android.support.v4.app.Fragment chatActivity,Callable<T> callable, TaskCallback<T> callback) {
// callable_ = callable;
// callback_ = callback;
// dynamicFragment=chatActivity;
// }
public CallableTask(android.support.v4.app.Fragment actvy,Callable<T> callable, TaskCallback<T> callback) {
callable_ = callable;
callback_ = callback;
dynamicFragment=actvy;
}
public static <V> void invoke(Callable<V> call, android.support.v4.app.Fragment _frg, TaskCallback<V> callback) {
new CallableTask<V>(_frg,call, callback).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR );
}
}
This gets called here... only when clicking on a button Send.
CallableTask.invoke(new Callable<Sent>() {
#Override
public Sent call() throws Exception {
}, this, new TaskCallback<Sent>() {
#Override
public void success(Sent result, Context context) {
mGridArray.add(result);
if (mListAdapter != null) {
mListAdapter.notifyDataSetChanged();
mListView.setSelection(mListAdapter.getCount());
mListView.smoothScrollToPosition(mListAdapter.getCount());
}
#Override
public void error(Exception e) {
}
});
Thanks
Dhiren
I finally resolved this by running a Asynch.cancel call on the thread from the activity fragment that started this thread. when I move away from activity. If I did not , it blocked any other tasks from running,

Android AIDL and Service and Activity parceable seems to be passing default class values

I have a strange issue going on, I have created an Android service, and a widget that consumes it. I have some complex objects that are sent between the two via AIDL. I have no problems registering my callbacks, and the service is populating the data correctly and sending it to the callbacks, but when I put a breakpoint on the writeToParcel method, and activate my debugger on the widget, I notice that the method is serializing the default class values, and not the ones the service populated it with. Does anyone have any suggestions for figuring out what is going on.
Here is my object:
Node.java:
package my.unique.package.name.service;
import java.util.ArrayList;
import java.util.HashMap;
import android.os.Parcel;
import android.os.Parcelable;
public class Node implements Parcelable {
protected int mNodeId;
protected double mBatteryLevel = -1;
protected double mBatteryTemp;
protected String mRadioName;
protected String mIPAddress;
protected String mFirmwareVersion;
protected String mModel;
protected String mSerialNumber;
protected String mUptime;
protected double mTemp;
protected double mInputVoltage;
protected double mBatteryClockVoltage;
protected String mSource;
protected double mLatitude;
protected double mLongitude;
protected double mAltitude;
protected String mFixType;
protected int mSatellites;
protected String mNTPServer;
protected ArrayList<Neighbor> mNeighbors;
protected HashMap<String, String> mManagedNodes;
protected HashMap<String, String> mOtherNodes;
public Node()
{
}
/**
* Node Constructor for Parcelable Interface
*
* #param in
* Parcel in
*/
public Node(Parcel in) {
String[] data = new String[19];
in.readStringArray(data);
mNodeId = Integer.parseInt(data[0]);
mBatteryLevel = Double.parseDouble(data[1]);
mBatteryTemp = Double.parseDouble(data[2]);
mRadioName = data[3];
mIPAddress = data[4];
mFirmwareVersion = data[5];
mModel = data[6];
mSerialNumber = data[7];
mUptime = data[8];
mTemp = Double.parseDouble(data[9]);
mInputVoltage = Double.parseDouble(data[10]);
mBatteryClockVoltage = Double.parseDouble(data[11]);
mSource = data[12];
mLatitude= Double.valueOf(data[13]);
mLongitude = Double.valueOf(data[14]);
mAltitude = Double.valueOf(data[15]);
mFixType = data[16];
mSatellites = Integer.valueOf(data[17]);
mNTPServer = data[18];
}
//Removed public get/set methods...
public static final Parcelable.Creator<Node> CREATOR = new Parcelable.Creator<Node>() {
public Node createFromParcel(Parcel in) {
return new Node(in);
}
public Node[] newArray(int size) {
return new Node[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringArray(new String[] { String.valueOf(mNodeId),
String.valueOf(mBatteryLevel), String.valueOf(mBatteryTemp),
mRadioName, mIPAddress, mFirmwareVersion, mModel,
mSerialNumber, mUptime, String.valueOf(mTemp),
String.valueOf(mInputVoltage),
String.valueOf(mBatteryClockVoltage),
mSource, String.valueOf(mLatitude), String.valueOf(mLongitude),
String.valueOf(mAltitude), mFixType,
String.valueOf(mSatellites), mNTPServer});
}
}
Node.aidl:
package my.unique.package.namespace.service;
parcelable Node;
This is how the service notifies callbacks
private void notifyCallbacksOfUpdate(Node updated) {
final int N = mNodeUpdatedCallbacks.beginBroadcast();
for (int i = 0; i < N; i++) {
try {
mNodeUpdatedCallbacks.getBroadcastItem(i).NodeUpdated(updated);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mNodeUpdatedCallbacks.finishBroadcast();
}
EDIT: Additional Code from consumer
MyApplication.java
public void bindConnection()
{
if (mServiceConnection == null) {
// create connection to service
mServiceConnection = new MyServiceConnection(this);
Intent i = new Intent("my.unique.package.IService");
boolean bound = bindService(i, mConnection,
Context.BIND_AUTO_CREATE);
Log.v("Service Bound: ", String.valueOf(bound));
}
}
Connection Class
import my.unique.package.service.IService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
public class MyServiceConnection implements ServiceConnection {
private IService service;
private Context mContext;
public MyServiceConnection(Context context)
{
mContext = context;
}
public void onServiceConnected(ComponentName name, IBinder boundService) {
service = IService.Stub.asInterface(boundService);
if(service != null)
{
mContext.sendBroadcast(new Intent(MyApplication.BOUND_TO_SERVICE));
}
}
public void onServiceDisconnected(ComponentName name) {
service = null;
}
protected IService getService()
{
return service;
}
}
In here is where I get notified of updates to the node, service sends correct info, but here is where it receives the default values. The node object class is in the same jar as the service and widget, just different packages, does the AIDL need to be copied to all of the packages, or can it reference it cross packages.
private INodeUpdatedCallback nodeUpdated = new INodeUpdatedCallback.Stub() {
#Override
public void NodeUpdated(Node nodeInfo) throws RemoteException {
Log.v("Node Updated: ", nodeInfo.getRadioIP());
if (nodeInfo.getIP().equalsIgnoreCase(mIp)
&& requestedLogin) {
requestedLogin = false;
ViewSwitcher vs = (ViewSwitcher) findViewById(R.id.vsSettings);
if (vs.getDisplayedChild() != VIEW_SETTINGS) {
vs.setInAnimation(AnimationUtils.loadAnimation(
WaveRelaySettingsActivity.this, R.anim.fade_in));
vs.setOutAnimation(AnimationUtils.loadAnimation(
WaveRelaySettingsActivity.this, R.anim.fade_out));
vs.setDisplayedChild(VIEW_SETTINGS);
}
}
}
};
Aidl for stub of callback
package my.unique.namespace.service;
import my.unique.namespace.service.Node;
oneway interface INodeUpdatedCallback {
void NodeUpdated(out Node nodeInfo);
}
I believe you need to change out to in in your AIDL.
package my.unique.namespace.service;
import my.unique.namespace.service.Node;
oneway interface INodeUpdatedCallback {
void NodeUpdated(in Node nodeInfo);
}

method clash from android LoaderManager

Suppose I have an android app with a Feed class, which is called by some Fragment implemented as follows and throwing : java.lang.ClassCastException: com.newsfeeder.ui.MainFragment cannot be cast to android.app.LoaderManager$LoaderCallbacks
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
public class MainFragment extends Fragment implements LoaderManager.LoaderCallbacks<List<Feed>>
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View v=inflater.inflate(R.layout.main_container, null);
listView = (ListView) v.findViewById(R.id.list);
getLoaderManager().initLoader(0, null, (android.app.LoaderManager.LoaderCallbacks<Object>) this);
return v;
}
public Loader<List<Feed>> onCreateLoader(int id, Bundle args) {
final List<Feed> initialItems = items;
return new ThrowableLoader<List<Feed>>(getActivity(), items) {
#Override
public List<Feed> loadData() throws Exception {
try {
if(getActivity() != null) {
return serviceProvider.getFeeds(); //some method fetching some `feeds`
} else {
return Collections.emptyList();
}
} catch (OperationCanceledException e) {
Activity activity = getActivity();
if (activity != null)
activity.finish();
return initialItems;
}
}
};
}
protected List<Feed> items = Collections.emptyList();
Btw ThrowableLoader class is implemented as follow
import android.content.Context;
public abstract class ThrowableLoader<D> extends AsyncLoader<D> {
private final D data;
private Exception exception;
/**
* Create loader for context and seeded with initial data
*
* #param context
* #param data
*/
public ThrowableLoader(Context context, D data) {
super(context);
this.data = data;
}
#Override
public D loadInBackground() {
exception = null;
try {
return loadData();
} catch (Exception e) {
Ln.d(e, "Exception loading data");
exception = e;
return data;
}
}
/**
* #return exception
*/
public Exception getException() {
return exception;
}
/**
* Clear the stored exception and return it
*
* #return exception
*/
public Exception clearException() {
final Exception throwable = exception;
exception = null;
return throwable;
}
/**
* Load data
*
* #return data
* #throws Exception
*/
public abstract D loadData() throws Exception;
}
and here's the `AsyncLoader`
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
public abstract class AsyncLoader<D> extends AsyncTaskLoader<D> {
private D data;
/**
* Create async loader
*
* #param context
*/
public AsyncLoader(Context context) {
super(context);
}
#Override
public void deliverResult(D data) {
if (isReset())
// An async query came in while the loader is stopped
return;
this.data = data;
super.deliverResult(data);
}
#Override
protected void onStartLoading() {
if (data != null)
deliverResult(data);
if (takeContentChanged() || data == null)
forceLoad();
}
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
data = null;
}
}
The problem is you are mixing android.support.v4.app.LoaderManager with android.app.LoaderManager.
I guess you want to use the support library and hence you're using the android.support.v4.app.Fragment class (those imports are missing), therefore you should not do the ((android.app.LoaderManager.LoaderCallbacks<Object>)this) cast but just implement the android.support.v4.app.LoaderManager.LoaderCallbacks<List<Feed>> interface in your android.support.v4.app.Fragment and pass it without cast.

AsyncTaskLoader basic example. (Android)

I am using a Loader in my application and based on the result I get from the query I perform on COntacts using this Loader I perform some calculations and store them back in a Sqlite DB. I want this operation to be Asynchronous, however I am confused between using an Async task, as I have lot of different data types to return or should I use a simple handler or an AsyncTaskLoader, I want it to be simple as I am new to Loaders. I tried to search around for examples of AsyncTaskLoader but it seems rocket science, a basic and simple functional example of any of the three in the context of my scenario would be a lot helpful.
If you wish to use AsyncTaskLoader, here's a nice sample for you.
EDIT: I've decided to make a simpler solution (based on this repo):
public abstract class AsyncTaskLoaderEx<T> extends AsyncTaskLoader<T> {
private static final AtomicInteger sCurrentUniqueId = new AtomicInteger(0);
private T mData;
public boolean hasResult = false;
public static int getNewUniqueLoaderId() {
return sCurrentUniqueId.getAndIncrement();
}
public AsyncTaskLoaderEx(final Context context) {
super(context);
onContentChanged();
}
#Override
protected void onStartLoading() {
if (takeContentChanged())
forceLoad();
//this part should be removed from support library 27.1.0 :
//else if (hasResult)
// deliverResult(mData);
}
#Override
public void deliverResult(final T data) {
mData = data;
hasResult = true;
super.deliverResult(data);
}
#Override
protected void onReset() {
super.onReset();
onStopLoading();
if (hasResult) {
onReleaseResources(mData);
mData = null;
hasResult = false;
}
}
protected void onReleaseResources(T data) {
//nothing to do.
}
public T getResult() {
return mData;
}
}
Usage:
in your activity:
getSupportLoaderManager().initLoader(TASK_ID, TASK_BUNDLE, new LoaderManager.LoaderCallbacks<Bitmap>() {
#Override
public Loader<Bitmap> onCreateLoader(final int id, final Bundle args) {
return new ImageLoadingTask(MainActivity.this);
}
#Override
public void onLoadFinished(final Loader<Bitmap> loader, final Bitmap result) {
if (result == null)
return;
//TODO use result
}
#Override
public void onLoaderReset(final Loader<Bitmap> loader) {
}
});
inner static class , or a normal class:
private static class ImageLoadingTask extends AsyncTaskLoaderEx<Bitmap> {
public ImageLoadingTask (Context context) {
super(context);
}
#Override
public Bitmap loadInBackground() {
//TODO load and return bitmap
}
}
Update: starting from support library 27.1.0, things changed a bit (link here) :
In version 27.1.0, onStartLoading() is called every time the Activity
is started. Since you call deliverResult() in onStartLoading(), you
trigger onLoadFinished(). This is Working as Intended.
You should remove your call to deliverResult() from onStartLoading()
as it is not needed (Loaders already deliver results computed in
loadInBackground() without any additional work needed on your part).
I've updated the code above for this change.
EDIT:
Updated, kotlin version can be found here.
Since Honeycomb and the v4 Compatibility Library it is possible to use AsyncTaskLoader. From what I understand, the AsyncTaskLoader can survive through config changes like screen flips. But using AsyncTask you can mess up with configuration changes.
Key information: AsyncTaskLoader is subclass of Loader. This class performs the same function as the AsyncTask, but a bit better, it can also be useful in handling configuration changes (screen orientation).
A very good example and explanation is given here.
http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html
Google has a pretty good example directly in the API Docs.
Android Design Patterns provides some more detail and the reasoning behind Loaders.
This tutorial will definetly help You. http://www.javacodegeeks.com/2013/08/android-custom-loader-to-load-data-directly-from-sqlite-database.html
Here's step by step tutorial to implement AsyncTaskLoader. or check out this same article on Medium
Implement LoaderManager.LoaderCallbacks<String> on MainActivity and create a static int to uniquely identify your loader and create a String key to pass string url to your loader
public class MainActivity extends AppCompatActivity
implements LoaderManager.LoaderCallbacks<String>{
public static final int OPERATION_SEARCH_LOADER = 22;
public static final String OPERATION_QUERY_URL_EXTRA = "query";
//...}
Override onCreateLoader,onLoadFinishedand onLoaderReset functions inside MainActivity
#Override
public Loader<String> onCreateLoader(int id, final Bundle args) {
//Here we will initiate AsyncTaskLoader
return null;
}
#Override
public void onLoadFinished(Loader<String> loader, String operationResult) {
//Think of this as AsyncTask onPostExecute method, the result from onCreateLoader will be available in operationResult variable and here you can update UI with the data fetched.
Log.d("MAINACTIVITY","result : "+ operationResult);
}
#Override
public void onLoaderReset(Loader<String> loader) {
//Don't bother about it, Android Studio will override it for you
}
inside onCreateLoader() return a new AsyncTaskLoader<String> as an anonymous inner class with this as the constructor's parameter and override loadInBackground & onStartLoading inside anonymous
inner class
#Override
public Loader<String> onCreateLoader(int id, final Bundle args) {
return new AsyncTaskLoader<String>(this) {
#Override
public String loadInBackground() {
//Think of this as AsyncTask doInBackground() method, here you will actually initiate Network call
return null;
}
#Override
protected void onStartLoading() {
//Think of this as AsyncTask onPreExecute() method,start your progress bar,and at the end call forceLoad();
forceLoad();
}
};
}
Inside loadInBackground make a network call using HTTPUrlConnection or OKHttp or anything that you use.
#Override
public String loadInBackground() {
String url = args.getString(OPERATION_QUERY_URL_EXTRA);//This is a url in string form
if (url!=null&&"".equals(url)) {
return null;//if url is null, return
}
String operationResult="";
try {
operationResult = NetworkUtils.getResponseFromHttpUrl(url);//This just create a HTTPUrlConnection and return result in strings
} catch (IOException e) {
e.printStackTrace();
}
return operationResult;
}
Inside onCreate initialize the loader with OPERATION_SEARCH_LOADER as the ID, null for the bundle, and this for the context
getSupportLoaderManager().initLoader(OPERATION_SEARCH_LOADER, null, this);
Now call this method, whenever and wherever you want to trigger the loader
private void makeOperationSearchQuery(String url) {
// Create a bundle called queryBundle
Bundle queryBundle = new Bundle();
// Use putString with OPERATION_QUERY_URL_EXTRA as the key and the String value of the URL as the value
queryBundle.putString(OPERATION_QUERY_URL_EXTRA,url);
// Call getSupportLoaderManager and store it in a LoaderManager variable
LoaderManager loaderManager = getSupportLoaderManager();
// Get our Loader by calling getLoader and passing the ID we specified
Loader<String> loader = loaderManager.getLoader(OPERATION_SEARCH_LOADER);
// If the Loader was null, initialize it. Else, restart it.
if(loader==null){
loaderManager.initLoader(OPERATION_SEARCH_LOADER, queryBundle, this);
}else{
loaderManager.restartLoader(OPERATION_SEARCH_LOADER, queryBundle, this);
}
}
Walla, you are done, just to remind you NetworkUtils.getResponseFromHttpUrl(url); is my custom function which take string convert it into URL which in turn used to create HTTPUrlConnection
I like this brief example AsyncTask and AsyncTaskLoader.
class FooLoader extends AsyncTaskLoader {
public FooLoader(Context context, Bundle args) {
super(context);
// do some initializations here
}
public String loadInBackground() {
String result = "";
// ...
// do long running tasks here
// ...
return result;
}
}
class FooLoaderClient implements LoaderManager.LoaderCallbacks {
Activity context;
// to be used for support library:
// FragmentActivity context2;
public Loader onCreateLoader(int id, Bundle args) {
// init loader depending on id
return new FooLoader(context, args);
}
public void onLoadFinished(Loader loader, String data) {
// ...
// update UI here
//
}
public void onLoaderReset(Loader loader) {
// ...
}
public void useLoader() {
Bundle args = new Bundle();
// ...
// fill in args
// ...
Loader loader =
context.getLoaderManager().initLoader(0, args, this);
// with support library:
// Loader loader =
// context2.getSupportLoaderManager().initLoader(0, args, this);
// call forceLoad() to start processing
loader.forceLoad();
}
}
Simplifying hard, maybe
private void loadContent() {
getLoaderManager().initLoader(1000, new Bundle(),
new LoaderManager.LoaderCallbacks<List<String>>() {
#Override
public Loader<List<String>> onCreateLoader(int id, Bundle args) {
return new AsyncTaskLoader<List<String>>(MainActivity.this.getApplicationContext()) {
#Override
public List<String> loadInBackground() {
Log.i("B", "Load background data ");
ArrayList<String> data = new ArrayList<>();
for (int i = 0; i < 5000; i++) {
data.add("Data." + i + " " + System.currentTimeMillis());
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return data;
}
};
}
#Override
public void onLoadFinished(Loader<List<String>> loader, List<String> data) {
Log.i("B", "Here are your data loaded" + data);
if (!loader.isAbandoned()) {
mAdapter.setData(data); // Read also about RecyclerView
}
}
#Override
public void onLoaderReset(Loader<List<String>> loader) {
Log.i("B", "Loader reset");
}
}).forceLoad();
}
#Override
protected void onDestroy() {
// Abandon the loader so that it should not attempt to modify already dead GUI component
getLoaderManager().getLoader(1000).abandon();
super.onDestroy();
}
Make this part of your Activity. The sample simulates delay, but makes new entries easy to recognize because they will have the different time stamp suffix. Of course you also need RecyclerView to display the data, the answer to this question seems very good.
The loader in this example is the inner class that keeps the reference to the parent activity. It must be external static class without such reference in production.
I prefer using Bolts-Android. it is very easy.
https://github.com/BoltsFramework/Bolts-Android
Task.callInBackground(new Callable<Void>() {
public Void call() {
// Do a bunch of stuff.
}
}).continueWith(...);

Categories

Resources