I am setting up to use the Loader pattern and had issues using the cursor approach, so I have refactored my code because my tables do not use _id as the primary key because of the use of association tables and I setup my code to use the same basic structure as the LoaderCustomSupport.java example from the android developer site. All of the code works without errors and I can see that I have the proper data back and ready for the ListFragment to display but after the onLoadFinished call back completes the setData on the adapter the getView is never called. My getView looks like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
PhoneNumberListHolder holder;
if (row == null) {
row = mInflater.inflate(R.layout.phonenumber_row, parent, false);
holder=new PhoneNumberListHolder(row);
row.setTag(holder);
} else {
holder = (PhoneNumberListHolder)row.getTag();
}
holder.populateForm(this.phoneNumbers.get(position));
return row;
}
I am trying to use the holder pattern, but I am thinking that maybe it is part of my issue. Any ideas where I might be going wrong?
Here is the loader code (Like I said I followed the Google example for my first run changing what I thought I would need)
The Abstract Loader for my Class
/*
* Custom version of CommonsWare, LLC, AbstractCursorLoader
*
*/
package myApp.service.data;
import java.util.List;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
abstract public class AbstractPhoneNumberLoader extends AsyncTaskLoader<List<PhoneNumber>> {
abstract protected List<PhoneNumber> buildPhoneNumber();
List<PhoneNumber> lastPhoneNumber=null;
public AbstractPhoneNumberLoader(Context context) {
super(context);
}
#Override
public List<PhoneNumber> loadInBackground() {
List<PhoneNumber> data=buildPhoneNumber();
if (data!=null) {
// Make sure we fill the person
data.size();
}
return (data);
}
/**
* This will run on the UI thread, routing the results from the
* background to the consumer of the Person object
* (e.g., a PhoneNumberListAdapter).
*/
#Override
public void deliverResult(List<PhoneNumber> data) {
if (isReset()) {
// An async query attempted a call while the loader is stopped
if (data!=null) {
data.clear();
data=null;
//not sure the best option here since we cannot close the List object
}
return;
}
List<PhoneNumber> oldPhoneNumber=lastPhoneNumber;
lastPhoneNumber=data;
if (isStarted()) {
super.deliverResult(data);
}
if (oldPhoneNumber!=null && oldPhoneNumber!=data && oldPhoneNumber.isEmpty()) {
oldPhoneNumber.clear();
oldPhoneNumber=null;
}
}
/**
* Start an asynchronous load of the requested data.
* When the result is ready the callbacks will be called
* on the UI thread. If a previous load has completed
* and is still valid the result may be passed back to the
* caller immediately.
*
* Note: Must be called from the UI thread
*/
#Override
protected void onStartLoading() {
if (lastPhoneNumber!=null) {
deliverResult(lastPhoneNumber);
}
if (takeContentChanged() || lastPhoneNumber==null) {
forceLoad();
}
}
/**
* Must be called from the UI thread, triggered by
* a call to stopLoading().
*/
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task
cancelLoad();
}
/**
* Must be called from the UI thread, triggered by a
* call to cancel(). Here, we make sure our Person
* is null, if it still exists and is not already empty.
*/
#Override
public void onCanceled(List<PhoneNumber> data) {
if (data!=null && !data.isEmpty()) {
data.clear();
}
}
/**
* Must be called from the UI thread, triggered by a
* call to reset(). Here, we make sure our Person
* is empty, if it still exists and is not already empty.
*/
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (lastPhoneNumber!=null && !lastPhoneNumber.isEmpty()) {
lastPhoneNumber.clear();
}
lastPhoneNumber=null;
}
}
The Data Loader
/*
* Custom version of CommonsWare, LLC, SQLiteCursorLoader
*
*/
package myApp.service.data;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.List;
import myApp.service.data.ActorDbAdapter;
import android.content.Context;
public class PhoneNumberDataLoader extends AbstractPhoneNumberLoader {
ActorDbAdapter db=null;
protected final String actorId;
protected final String _PHONENUMBERID = "PhoneNumberId";
protected SimpleDateFormat dateFormat = new SimpleDateFormat("dd/mm/yyyy");
/**
* Constructor - takes the context to allow the database to be
* opened/created
*
* #param ctx the Context within which to work
*/
public PhoneNumberDataLoader(Context ctx, String actorId) {
super(ctx);
this.actorId = actorId;
getHelper(ctx);
}
// Get a database connection
private void getHelper(Context ctx) {
if (db==null) {
db=new ActorDbAdapter(ctx);
}
db.open();
}
// Loader Methods
/**
* Runs on a worker thread and performs the actual
* database query to retrieve the PhoneNumber List.
*/
#Override
protected List<PhoneNumber> buildPhoneNumber() {
return(db.readPhoneById(actorId));
}
/**
* Writes a semi-user-readable roster of contents to
* supplied output.
*/
#Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
writer.print(prefix);
writer.print("actorId=");
writer.println(actorId);
}
public void insert(PhoneNumber data, String actorId) {
new InsertTask(this).execute(db, data, actorId);
}
// Saved for Later, get Reads and Writes working first
// public void update(PhoneNumber data, String actorId, String whereClause, String[] whereArgs) {
// new UpdateTask(this).execute(db, data, actorId, whereClause, whereArgs);
// }
//
// public void delete(String actorId, String phoneNumberId, String whereClause, String[] whereArgs) {
// new DeleteTask(this).execute(db, actorId, phoneNumberId, whereClause, whereArgs);
// }
public void execSQL(String actorId) {
new ExecSQLTask(this).execute(db, actorId);
}
private class InsertTask extends ContentChangingTask<Object, Void, Void> {
InsertTask(PhoneNumberDataLoader loader) {
super(loader);
}
#Override
protected Void doInBackground(Object... params) {
ActorDbAdapter db=(ActorDbAdapter)params[0];
PhoneNumber data=(PhoneNumber)params[1];
int actorId=Integer.parseInt((String)params[2]);
db.createPhoneNumber(data, actorId);
return(null);
}
}
// Saved for Later, get Reads and Writes working first
// private class UpdateTask extends
// ContentChangingTask<Object, Void, Void> {
// UpdateTask(PhoneNumberDataLoader loader) {
// super(loader);
// }
//
// #Override
// protected Void doInBackground(Object... params) {
// ActorDbAdapter db=(ActorDbAdapter)params[0];
// String table=(String)params[1];
// int actorId=Integer.parseInt((String)params[2]);
// String where=(String)params[3];
// String[] whereParams=(String[])params[4];
//
// db.updatePhoneNumber(table, values, where, whereParams);
//
// return(null);
// }
// }
//
// private class DeleteTask extends
// ContentChangingTask<Object, Void, Void> {
// DeleteTask(PhoneNumberDataLoader loader) {
// super(loader);
// }
//
// #Override
// protected Void doInBackground(Object... params) {
// ActorDbAdapter db=(ActorDbAdapter)params[0];
// int actorId=Integer.parseInt((String)params[1]);
// int phoneNumberId=Integer.parseInt((String)params[2]);
// String where=(String)params[3];
// String[] whereParams=(String[])params[3];
//
// db.deletePhoneNumber(table, where, whereParams);
//
// return(null);
// }
// }
private class ExecSQLTask extends
ContentChangingTask<Object, Void, Void> {
ExecSQLTask(PhoneNumberDataLoader loader) {
super(loader);
}
#Override
protected Void doInBackground(Object... params) {
ActorDbAdapter db=(ActorDbAdapter)params[0];
String actorId=(String)params[1];
db.readPhoneById(actorId);
return(null);
}
}
}
Here is my full ListAdapter
package myApp.planner.utilities;
import java.util.ArrayList;
import java.util.List;
import myApp.planner.R;
import myApp.planner.codes.PhoneOrAddressTypeCode;
import myApp.service.data.PhoneNumber;
import myApp.service.data.PhoneNumberListData;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.TextView;
public class PhoneNumberListAdapter extends ArrayAdapter<PhoneNumber> {
private List<PhoneNumber> phoneNumbers;
private final LayoutInflater mInflater;
private Activity activity;
public PhoneNumberListAdapter(Activity a, int textViewResourceId) {
super(a, textViewResourceId);
activity = a;
mInflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setData(List<PhoneNumber> data) {
if (this.phoneNumbers==null) {
this.phoneNumbers = new ArrayList<PhoneNumber>();
}
this.phoneNumbers.clear();
if (data != null) {
for (PhoneNumber phoneNumber : data) {
this.phoneNumbers.add(phoneNumber);
}
}
}
public static class PhoneNumberListHolder {
private TextView actorid=null;
private TextView phonenumberid=null;
private TextView phonetype=null;
private TextView phonenumber=null;
private CheckBox isprimary=null;
PhoneNumberListHolder(View row) {
actorid=(TextView)row.findViewById(R.id.actorid);
phonenumberid=(TextView)row.findViewById(R.id.phonenumberid);
phonetype=(TextView)row.findViewById(R.id.txtphonetype);
phonenumber=(TextView)row.findViewById(R.id.txtphonenumber);
isprimary=(CheckBox)row.findViewById(R.id.isprimary);
}
//void populateForm(ArrayList<PhoneNumberListData> c, int position) {
void populateForm(PhoneNumber data) {
//PhoneNumberListData data = c.get(position);
// Attempt to add the Actor ID
if (actorid != null){
actorid.setText(data.getActor().get(0).getActorId()==0 ? "0": Integer.toString(data.getActor().get(0).getActorId()));
}
// Attempt to add the Phone Number Item ID
if (phonenumberid != null){
phonenumberid.setText(data.getPhoneNumberId()==0 ? "0": Integer.toString(data.getPhoneNumberId()));
}
// Attempt to add the Phone Number Type
if (phonetype != null){
phonetype.setText(data.getPhoneType()==null ? "": PhoneOrAddressTypeCode.valueOf(data.getPhoneType()).toString());
}
// Attempt to add the Phone Number
if (phonenumber != null){
phonenumber.setText(data.getPhoneNumber1()==null ? "": data.getPhoneNumber1());
}
// Attempt to add the is Primary Flag
if (isprimary != null){
isprimary.setChecked(data.getIsPrimary()==0 ? Boolean.FALSE: Boolean.TRUE);
}
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
PhoneNumberListHolder holder;
if (row == null) {
row = mInflater.inflate(R.layout.phonenumber_row, parent, false);
holder=new PhoneNumberListHolder(row);
row.setTag(holder);
} else {
holder = (PhoneNumberListHolder)row.getTag();
}
holder.populateForm(this.phoneNumbers.get(position));
return row;
}
}
The ListFragment:
package myApp.planner;
import java.util.ArrayList;
import java.util.List;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SearchViewCompat;
import android.support.v4.widget.SearchViewCompat.OnQueryTextListenerCompat;
import android.text.TextUtils;
import android.content.Intent;
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.database.Cursor;
import android.view.ContextMenu;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ListView;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import myApp.planner.R;
import myApp.planner.utilities.PhoneNumberListAdapter;
import myApp.service.data.PhoneNumber;
import myApp.service.data.PhoneNumberDataLoader;
public class ActorPhoneNumberListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<PhoneNumber>> {
public final static String ID_EXTRA="myapp.planner.actorid";
protected final static String TAG = "ActorPhoneNumberListFragment";
private static final int ADD_ID=Menu.FIRST + 1;
private static final int DELETE_ID=Menu.FIRST + 3;
private PhoneNumberListAdapter mAdapter=null;
private PhoneNumberDataLoader loader=null;
private String mCurFilter;
private String actorId = "0";
//private SharedPreferences prefs=null;
OnActorPhoneNumberListListener listener=null;
OnQueryTextListenerCompat mOnQueryTextListenerCompat;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
this.actorId = getActivity().getIntent().getExtras().getString(ID_EXTRA).toString();
}
#Override
public void onResume() {
super.onResume();
Bundle args=getArguments();
if (args!=null) {
loadPhoneNumbers(args.getString(ID_EXTRA));
}
// init Empty Test for no Phone numbers Found
// Add the menu options that we need to manage the list
setHasOptionsMenu(true);
// Hookup the dbAdapter and create a blank adapter
initList();
}
#Override
public void onPause() {
super.onPause();
}
#Override
public void onListItemClick(ListView list, View view, int position, long id) {
if (listener!=null) {
//We will actually want the PhoneNumber Id here for the popup edit screen
String mId = actorId;
listener.onActorPhoneNumberListSelected(mId);
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.phonenumber_opton, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId()==R.id.addNewPhone) {
//add();
return(true);
} else if (item.getItemId()==R.id.help) {
startActivity(new Intent(getActivity(), HelpPage.class));
return(true);
} else if (item.getItemId()==R.id.phoneRefresh) {
//startActivity(new Intent(getActivity(), ActorPhoneNumberListFragment.class));
//We may just need to refresh the loader
return(true);
} else
return(super.onOptionsItemSelected(item));
}
public void setOnActorPhoneNumberListListener(OnActorPhoneNumberListListener listener) {
this.listener=listener;
}
public void loadPhoneNumbers(String actorId) {
this.actorId=actorId;
}
private void initList() {
mAdapter=new PhoneNumberListAdapter(getActivity(), R.layout.phonenumber_row);
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
getActivity().getSupportLoaderManager().initLoader(0, null, this);
}
public interface OnActorPhoneNumberListListener {
void onActorPhoneNumberListSelected(String actorId);
}
#Override
public Loader<List<PhoneNumber>> onCreateLoader(int loaderId, Bundle args) {
loader= new PhoneNumberDataLoader(getActivity(), actorId);
return(loader);
}
#Override
public void onLoadFinished(Loader<List<PhoneNumber>> loader, List<PhoneNumber> data) {
// Now give the data to the adapter
mAdapter.setData(data);
mAdapter.notifyDataSetChanged();
//setListAdapter(mAdapter);
// Show the list
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
#Override
public void onLoaderReset(Loader<List<PhoneNumber>> arg0) {
// TODO Auto-generated method stub
mAdapter.setData(null);
}
}
Related
I'm having trouble starting a task after it has been cancelled the first time. It will run when entering the view, and starting the task, then cancel works when the fragment is first destroyed. But when re-entering the view, the AsyncTask will no longer run.
Is it possible some class state that needs to be cleaned up? Or do I need to remove the Fragment with the AsyncTask from the back stack?
Here is my code below:
package com.emijit.lighteningtalktimer;
import android.content.ContentUris;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.emijit.lighteningtalktimer.data.Timer;
import com.emijit.lighteningtalktimer.data.TimerContract.TimerEntry;
public class RunTimerFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String LOG_TAG = RunTimerFragment.class.getSimpleName();
private View rootView;
private Uri mUri;
private long mTimerId = 0;
private Timer mTimer;
private RunTimerTask mRunTimerTask;
private static final int DETAIL_LOADER = 0;
public static String URI = "URI";
public RunTimerFragment() {
}
static class ViewHolder {
TextView intervals;
TextView timerSeconds;
TextView timerMinutes;
TextView timerHours;
public ViewHolder(View view) {
intervals = (TextView) view.findViewById(R.id.run_timer_intervals);
timerSeconds = (TextView) view.findViewById(R.id.timer_seconds);
timerMinutes = (TextView) view.findViewById(R.id.timer_minutes);
timerHours = (TextView) view.findViewById(R.id.timer_hours);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_run_timer, container, false);
ViewHolder viewHolder = new ViewHolder(rootView);
rootView.setTag(viewHolder);
Bundle arguments = getArguments();
if (arguments != null) {
mUri = arguments.getParcelable(URI);
if (mUri != null) {
mTimerId = ContentUris.parseId(mUri);
Log.d(LOG_TAG, "mTimerId: " + mTimerId);
}
}
return rootView;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getLoaderManager().initLoader(DETAIL_LOADER, null, this);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (mTimerId != 0) {
return new CursorLoader(
getActivity(),
TimerEntry.CONTENT_URI,
null,
TimerEntry._ID + " = ?",
new String[] { Long.toString(mTimerId) },
null
);
}
return null;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (cursor != null && cursor.moveToFirst()) {
mTimer = new Timer(cursor);
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
}
public void startTimer() {
mRunTimerTask = new RunTimerTask();
mRunTimerTask.execute(mTimer);
}
#Override
public void onPause() {
super.onPause();
mRunTimerTask.cancel(true);
}
private class RunTimerTask extends AsyncTask<Timer, Integer, Long> {
private final String LOG_TAG = RunTimerTask.class.getSimpleName();
Timer mTimer;
int mCurrentSeconds = 0;
int mCurrentIntervals = 0;
#Override
protected Long doInBackground(Timer... params) {
Log.d(LOG_TAG, "doInBackground");
mTimer = params[0];
while (mTimer.getIntervals() > mCurrentIntervals) {
try {
Thread.sleep(1000);
mCurrentSeconds++;
publishProgress(mCurrentSeconds);
} catch (InterruptedException e) {
Log.d(LOG_TAG, e.toString());
}
}
return (long) mCurrentIntervals;
}
#Override
protected void onProgressUpdate(Integer... values) {
// do stuff
}
#Override
protected void onPostExecute(Long aLong) {
super.onPostExecute(aLong);
Log.d(LOG_TAG, "onPostExecute");
}
}
}
I think the issue might be in the catch block. Not sure about the mTimer.getIntervals() because it's from third party.
When you cancel the task. The InterruptedException will be caught in the task thread. Then your loop will still keep going because you didn't return or break the loop.
Since all AsyncTask will be queued up in one thread pool of size 1, even if you start another AsyncTask, it will still be blocked.
In your sample, I cannot see any call to startTimer(). Should you be overiding onResume() and calling startTimer() in there?
Been trying to load data from sqlite and display it on viewpager without much success.
I have a viewpager with two tabs which should hold data based on the tag_id passed as a parameter of newInstance. There is also an action bar navigation spinner with a list of counties that is used for filter data displayed based on the county_id.
Am able to fetch data from server and save it in the sqlite db but displaying it is the problem. Data is not dispalyed on the first page of the viewpager but it exists in the sqlite. Data for the second is the only one laoded.
Below is my implementation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.OnNavigationListener;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkError;
import com.android.volley.NoConnectionError;
import com.android.volley.ParseError;
import com.android.volley.Response;
import com.android.volley.ServerError;
import com.android.volley.TimeoutError;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.app.adapter.CustomCountySpinnerAdapter;
import com.app.adapter.TendersAdapter;
import com.app.database.DBFunctions;
import com.app.externallib.AppController;
import com.app.model.CountyModel;
import com.app.model.PostsModel;
import com.app.utils.AppConstants;
import com.app.utils.PostsListLoader;
import com.nhaarman.listviewanimations.appearance.simple.SwingBottomInAnimationAdapter;
import com.viewpagerindicator.TabPageIndicator;
public class PublicTendersFragment extends Fragment{
private static List<PubliTenders> public_tenders;
public PublicTendersFragment newInstance(String text) {
PublicTendersFragment mFragment = new PublicTendersFragment();
Bundle mBundle = new Bundle();
mBundle.putString(AppConstants.TEXT_FRAGMENT, text);
mFragment.setArguments(mBundle);
return mFragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
public_tenders = new ArrayList<PubliTenders>();
public_tenders.add(new PubliTenders(14, "County"));
public_tenders.add(new PubliTenders(15, "National"));
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final Context contextThemeWrapper = new ContextThemeWrapper(
getActivity(), R.style.StyledIndicators);
LayoutInflater localInflater = inflater
.cloneInContext(contextThemeWrapper);
View v = localInflater.inflate(R.layout.fragment_tenders, container,
false);
FragmentPagerAdapter adapter = new TendersVPAdapter(
getFragmentManager());
ViewPager pager = (ViewPager) v.findViewById(R.id.pager);
pager.setAdapter(adapter);
TabPageIndicator indicator = (TabPageIndicator) v
.findViewById(R.id.indicator);
indicator.setViewPager(pager);
return v;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
}
class TendersVPAdapter extends FragmentPagerAdapter{
public TendersVPAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Parcelable saveState() {
return null;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return Tenders.newInstance(public_tenders.get(position).tag_id);
case 1:
return Tenders.newInstance(public_tenders.get(position).tag_id);
default:
return null;
}
}
#Override
public CharSequence getPageTitle(int position) {
return public_tenders.get(position).tender_type.toUpperCase(Locale
.getDefault());
}
#Override
public int getCount() {
return public_tenders.size();
}
}
public class PubliTenders {
public int tag_id;
public String tender_type;
public PubliTenders(int tag_id, String tender_type) {
this.tag_id = tag_id;
this.tender_type = tender_type;
}
}
public static class Tenders extends ListFragment implements
OnNavigationListener, LoaderCallbacks<ArrayList<PostsModel>> {
boolean mDualPane;
int mCurCheckPosition = 0;
// private static View rootView;
private SwipeRefreshLayout swipeContainer;
private ListView lv;
private View rootView;
private DBFunctions mapper;
private CustomCountySpinnerAdapter spinadapter;
private TendersAdapter mTendersAdapter;
private static final String ARG_TAG_ID = "tag_id";
private int tag_id;
private int mycounty;
private static final int INITIAL_DELAY_MILLIS = 500;
private static final String DEBUG_TAG = "BlogsFragment";
private final String TAG_REQUEST = "BLOG_TAG";
private JsonArrayRequest jsonArrTendersRequest;
// private OnItemSelectedListener listener;
public static Tenders newInstance(int tag_id) {
Tenders fragment = new Tenders();
Bundle b = new Bundle();
b.putInt(ARG_TAG_ID, tag_id);
fragment.setArguments(b);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tag_id = getArguments().getInt(ARG_TAG_ID);
}
#Override
public void onStart() {
super.onStart();
getLoaderManager().initLoader(0, null, this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_headlines_blog,
container, false);
swipeContainer = (SwipeRefreshLayout) rootView
.findViewById(R.id.swipeProjectsContainer);
lv = (ListView) rootView.findViewById(android.R.id.list);
lv.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view,
int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
int topRowVerticalPosition = (lv == null || lv
.getChildCount() == 0) ? 0 : lv.getChildAt(0)
.getTop();
swipeContainer.setEnabled(topRowVerticalPosition >= 0);
}
});
swipeContainer.setOnRefreshListener(new OnRefreshListener() {
#Override
public void onRefresh() {
fetchPublicTenders(mycounty);
}
});
swipeContainer.setColorSchemeResources(R.color.blue_dark,
R.color.irdac_green, R.color.red_light,
R.color.holo_red_light);
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
mapper = new DBFunctions(getActivity());
mapper.open();
// initialize AB Spinner
populateSpinner();
fetchPublicTenders(mycounty);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
private void populateSpinner() {
try {
List<CountyModel> counties = new ArrayList<CountyModel>();
counties = mapper.getAllCounties();
ActionBar actBar = ((ActionBarActivity) getActivity())
.getSupportActionBar();
actBar.setDisplayShowTitleEnabled(true);
actBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
spinadapter = new CustomCountySpinnerAdapter(getActivity(),
android.R.layout.simple_spinner_dropdown_item, counties);
actBar.setListNavigationCallbacks(spinadapter, this);
} catch (NullPointerException exp) {
}
}
#Override
public Loader<ArrayList<PostsModel>> onCreateLoader(int arg0,
Bundle arg1) {
Log.v(DEBUG_TAG, "On Create Loader");
return new PostsListLoader(getActivity(), mycounty, tag_id);
}
#Override
public void onLoadFinished(Loader<ArrayList<PostsModel>> arg0,
ArrayList<PostsModel> data) {
// System.out.println("results " + data.size());
addToAdapter(data);
}
#Override
public void onLoaderReset(Loader<ArrayList<PostsModel>> arg0) {
lv.setAdapter(null);
}
#Override
public boolean onNavigationItemSelected(int pos, long arg1) {
CountyModel mo = spinadapter.getItem(pos);
this.mycounty = mo.getId();
refresh(mo.getId());
return false;
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
TextView txtTitle = (TextView) v.findViewById(R.id.tender_title);
TextView txtRefNo = (TextView) v.findViewById(R.id.ref_no);
TextView txtExpiryDate = (TextView) v
.findViewById(R.id.expiry_date);
TextView txtOrg = (TextView) v.findViewById(R.id.dept_or_org);
Intent intTenderFullDetails = new Intent(getActivity(),
TenderDetailsActivity.class);
intTenderFullDetails.putExtra(TenderDetailsActivity.TENDER_TITLE,
txtTitle.getText().toString().trim());
intTenderFullDetails.putExtra(TenderDetailsActivity.TENDER_REF_NO,
txtRefNo.getText().toString().trim());
intTenderFullDetails.putExtra(
TenderDetailsActivity.TENDER_EXPIRY_DATE, txtExpiryDate
.getText().toString().trim());
intTenderFullDetails.putExtra(TenderDetailsActivity.TENDER_ORG,
txtOrg.getText().toString().trim());
// intTenderFullDetails.putExtra(TenderDetailsActivity.TENDER_DESC,
// Lorem);
startActivity(intTenderFullDetails);
}
private void fetchPublicTenders(final int county_id) {
swipeContainer.setRefreshing(true);
Uri.Builder builder = Uri.parse(AppConstants.postsUrl).buildUpon();
builder.appendQueryParameter("tag_id", Integer.toString(tag_id));
System.out.println("fetchPublicTenders with tag_id " + tag_id
+ " and county_id " + county_id);
jsonArrTendersRequest = new JsonArrayRequest(builder.toString(),
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
if (response.length() > 0) {
for (int i = 0; i < response.length(); i++) {
try {
JSONObject tender_item = response
.getJSONObject(i);
mapper.createPost(
tender_item.getInt("id"),
tender_item.getInt("tag_id"),
tender_item.getInt("county_id"),
tender_item.getInt("sector_id"),
tender_item.getString("title"),
tender_item.getString("slug"),
tender_item.getString("content"),
tender_item
.getString("reference_no"),
tender_item
.getString("expiry_date"),
tender_item
.getString("organization"),
tender_item
.getString("image_url"),
tender_item
.getString("created_at"));
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
swipeContainer.setRefreshing(false);
refresh(county_id);
}
} else {
if (swipeContainer.isShown()) {
swipeContainer.setRefreshing(false);
}
try {
Toast.makeText(getActivity(),
"Sorry! No results found",
Toast.LENGTH_LONG).show();
} catch (NullPointerException npe) {
System.out.println(npe);
}
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (error instanceof NetworkError) {
try {
Toast.makeText(
getActivity(),
"Network Error. Cannot refresh list",
Toast.LENGTH_SHORT).show();
} catch (NullPointerException npe) {
System.err.println(npe);
}
if (swipeContainer.isShown()) {
swipeContainer.setRefreshing(false);
}
refresh(county_id);
} else if (error instanceof ServerError) {
try {
Toast.makeText(
getActivity(),
"Problem Connecting to Server. Try Again Later",
Toast.LENGTH_SHORT).show();
} catch (NullPointerException npe) {
System.err.println(npe);
}
if (swipeContainer.isShown()) {
swipeContainer.setRefreshing(false);
}
} else if (error instanceof AuthFailureError) {
} else if (error instanceof ParseError) {
} else if (error instanceof NoConnectionError) {
try {
Toast.makeText(getActivity(),
"No Connection", Toast.LENGTH_SHORT)
.show();
} catch (NullPointerException npe) {
System.err.println(npe);
}
} else if (error instanceof TimeoutError) {
try {
Toast.makeText(
getActivity()
.getApplicationContext(),
"Timeout Error. Try Again Later",
Toast.LENGTH_SHORT).show();
} catch (NullPointerException npe) {
System.err.println(npe);
}
}
if (swipeContainer.isShown()) {
swipeContainer.setRefreshing(false);
}
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Accept", "application/json");
return headers;
}
};
// Set a retry policy in case of SocketTimeout & ConnectionTimeout
// Exceptions. Volley does retry for you if you have specified the
// policy.
jsonArrTendersRequest.setRetryPolicy(new DefaultRetryPolicy(
(int) TimeUnit.SECONDS.toMillis(20),
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
jsonArrTendersRequest.setTag(TAG_REQUEST);
AppController.getInstance().addToRequestQueue(jsonArrTendersRequest);
}
public void refresh(int county_id) {
Bundle b = new Bundle();
b.putInt("myconty", county_id);
if (isAdded()) {
getLoaderManager().restartLoader(0, b, this);
}
}
private void addToAdapter(ArrayList<PostsModel> plist) {
mTendersAdapter = new TendersAdapter(rootView.getContext(), plist);
SwingBottomInAnimationAdapter swingBottomInAnimationAdapter = new SwingBottomInAnimationAdapter(
mTendersAdapter);
swingBottomInAnimationAdapter.setAbsListView(lv);
assert swingBottomInAnimationAdapter.getViewAnimator() != null;
swingBottomInAnimationAdapter.getViewAnimator()
.setInitialDelayMillis(INITIAL_DELAY_MILLIS);
setListAdapter(swingBottomInAnimationAdapter);
mTendersAdapter.notifyDataSetChanged();
}
}
}
And this is the PostsListLoader class
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
import com.app.database.DBFunctions;
import com.app.model.PostsModel;
public class PostsListLoader extends AsyncTaskLoader<ArrayList<PostsModel>> {
private DBFunctions mapper;
private ArrayList<PostsModel> myPostsModel;
private int county_id;
private int tag_id;
public PostsListLoader(Context context, int county_id, int tag_id) {
super(context);
mapper = new DBFunctions(getContext());
mapper.open();
this.county_id = county_id;
this.tag_id = tag_id;
}
#Override
public ArrayList<PostsModel> loadInBackground() {
String query_string = AppConstants.KEY_COUNTY_ID + " = " + county_id
+ " AND " + AppConstants.KEY_TAG_ID + " = " + tag_id;
myPostsModel = mapper.getPosts(query_string);
return myPostsModel;
}
#Override
public void deliverResult(ArrayList<PostsModel> data) {
if (isReset()) {
// An async query came in while the loader is stopped. We
// don't need the result.
if (data != null) {
onReleaseResources(data);
}
}
List<PostsModel> oldNews = data;
myPostsModel = data;
if (isStarted()) {
// If the Loader is currently started, we can immediately
// deliver its results.
super.deliverResult(data);
}
// At this point we can release the resources associated with
// 'oldNews' if needed; now that the new result is delivered we
// know that it is no longer in use.
if (oldNews != null) {
onReleaseResources(oldNews);
}
}
/**
* Handles a request to start the Loader.
*/
#Override
protected void onStartLoading() {
if (myPostsModel != null) {
// If we currently have a result available, deliver it
// immediately.
deliverResult(myPostsModel);
}
if (takeContentChanged() || myPostsModel == null) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad();
}
}
/**
* Handles a request to stop the Loader.
*/
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
/**
* Handles a request to cancel a load.
*/
#Override
public void onCanceled(ArrayList<PostsModel> news) {
super.onCanceled(news);
// At this point we can release the resources associated with 'news'
// if needed.
onReleaseResources(news);
}
/**
* Handles a request to completely reset the Loader.
*/
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
// At this point we can release the resources associated with 'apps'
// if needed.
if (myPostsModel != null) {
onReleaseResources(myPostsModel);
myPostsModel = null;
}
}
/**
* Helper function to take care of releasing resources associated with an
* actively loaded data set.
*/
protected void onReleaseResources(List<PostsModel> news) {
}
}
What could I be doing wrong?
Any help will be appreciated.
Thanks
I've created demo application which insert some data into sqlite database and update on listview [Fragment]. But i want to add one another data by click on button which data comes from EditText and update on listview with new data.
I write my demo code below. pls let me know how do i show all data in listview which after adding into database at the same time.
####################################### Step 1
package com.example.locationsample.database;
import java.util.List;
import android.database.sqlite.SQLiteDatabase;
public abstract class DataSource<T> {
public final SQLiteDatabase mDatabase;
public DataSource(final SQLiteDatabase database) {
mDatabase = database;
}
public abstract boolean insert(T entity);
public abstract boolean delete(T entity);
public abstract boolean update(T entity);
public abstract List read();
public abstract List read(String selection, String[] selectionArgs,
String groupBy, String having, String orderBy);
}
####################################### Step 2
package com.example.locationsample.model;
public class Test {
public Test() {
}
public Test(String name) {
this.name = name;
}
private int id;
private String name;
/**
* #return the id
*/
public int getId() {
return id;
}
/**
* #param id
* the id to set
*/
public void setId(final int id) {
this.id = id;
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name
* the name to set
*/
public void setName(final String name) {
this.name = name;
}
/*
* (non-Javadoc)
*
* #see java.lang.Object#toString()
*/
#Override
public String toString() {
return name;
// return "Test [name=" + name + "]";
}
}
####################################### Step 3
package com.example.locationsample.database;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.example.locationsample.model.Test;
public class TestDataSource extends DataSource<Test> {
public static final String TABLE_NAME = "test";
private static final String COLUMN_ID = "_id";
private static final String COLUMN_NAME = "name";
public static final String CREATE_COMMAND = "create table " + TABLE_NAME
+ " (" + COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_NAME + " text not null);";
public TestDataSource(final SQLiteDatabase database) {
super(database);
}
#Override
public boolean insert(final Test entity) {
if (entity == null) {
return false;
}
final long result = mDatabase.insert(TABLE_NAME, null,
generateContentValueFromObject(entity));
return result != -1;
}
#Override
public boolean delete(final Test entity) {
if (entity == null) {
return false;
}
final int result = mDatabase.delete(TABLE_NAME, COLUMN_ID + "=?",
new String[] { String.valueOf(entity.getId()) });
return result != 0;
}
#Override
public boolean update(final Test entity) {
if (entity == null) {
return false;
}
final int result = mDatabase.update(TABLE_NAME,
generateContentValueFromObject(entity), COLUMN_ID + "=?",
new String[] { String.valueOf(entity.getId()) });
return result != 0;
}
#Override
public List<Test> read() {
final Cursor cursor = mDatabase.query(TABLE_NAME, getAllColumns(),
null, null, null, null, null);
final List<Test> tests = new ArrayList<Test>();
if (cursor != null && cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
tests.add(generateObjectFromCursor(cursor));
cursor.moveToNext();
}
cursor.close();
}
return tests;
}
#Override
public List<Test> read(final String selection,
final String[] selectionArgs, final String groupBy,
final String having, final String orderBy) {
final Cursor cursor = mDatabase.query(TABLE_NAME, getAllColumns(),
selection, selectionArgs, groupBy, having, orderBy);
final List<Test> tests = new ArrayList<Test>();
if (cursor != null && cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
tests.add(generateObjectFromCursor(cursor));
cursor.moveToNext();
}
cursor.close();
}
return tests;
}
private String[] getAllColumns() {
return new String[] { COLUMN_ID, COLUMN_NAME };
}
private Test generateObjectFromCursor(final Cursor cursor) {
if (cursor == null) {
return null;
}
final Test test = new Test();
test.setId(cursor.getInt(cursor.getColumnIndex(COLUMN_ID)));
test.setName(cursor.getString(cursor.getColumnIndex(COLUMN_NAME)));
return test;
}
private ContentValues generateContentValueFromObject(final Test entity) {
if (entity == null) {
return null;
}
final ContentValues values = new ContentValues();
values.put(COLUMN_NAME, entity.getName());
return values;
}
}
####################################### Step 4
package com.example.locationsample.database;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DbHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "test.db";
private static final int DATABASE_VERSION = 1;
public DbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(TestDataSource.CREATE_COMMAND);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TestDataSource.TABLE_NAME);
onCreate(db);
}
}
####################################### Step 5
package com.example.locationsample.loader;
import android.content.Loader;
import android.os.AsyncTask;
import android.util.Log;
public abstract class ContentChangingTask<T1, T2, T3> extends
AsyncTask<T1, T2, T3> {
private static final String TAG = "ContentChangingTask";
private Loader<?> mLoader = null;
public ContentChangingTask(final Loader<?> loader) {
mLoader = loader;
}
#Override
protected void onPostExecute(final T3 result) {
// super.onPostExecute(result);
Log.i(TAG, "+++ onPostExecute() called! +++");
mLoader.onContentChanged();
}
}
####################################### Step 6
package com.example.locationsample.loader;
import java.util.List;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.util.Log;
public abstract class AbstractDataLoader<E extends List<?>> extends
AsyncTaskLoader<E> {
private static final String TAG = "AbstractDataLoader";
protected E mLastDataList = null;
protected abstract E buildList();
public AbstractDataLoader(final Context context) {
super(context);
}
/**
* Runs on a worker thread, loading in our data. Delegates the real work to
* concrete subclass' buildList() method.
*/
#Override
public E loadInBackground() {
Log.d(TAG, "+++ loadInBackground() called! +++");
return buildList();
}
/**
* Runs on the UI thread, routing the results from the background thread to
* whatever is using the dataList.
*/
#Override
public void deliverResult(final E dataList) {
Log.d(TAG, "+++ deliverResult() called! +++");
if (isReset()) {
// An async query came in while the loader is stopped
emptyDataList(dataList);
return;
}
final E oldDataList = mLastDataList;
mLastDataList = dataList;
if (isStarted()) {
super.deliverResult(dataList);
}
if (oldDataList != null && oldDataList != dataList
&& oldDataList.size() > 0) {
emptyDataList(oldDataList);
}
}
/**
* Starts an asynchronous load of the list data. When the result is ready
* the callbacks will be called on the UI thread. If a previous load has
* been completed and is still valid the result may be passed to the
* callbacks immediately.
*
* Must be called from the UI thread.
*/
#Override
protected void onStartLoading() {
//super.onStartLoading();
Log.d(TAG, "+++ onStartLoading() called! +++");
if (mLastDataList != null) {
deliverResult(mLastDataList);
}
if (takeContentChanged() || mLastDataList == null
|| mLastDataList.size() == 0) {
forceLoad();
}
}
/**
* Must be called from the UI thread, triggered by a call to stopLoading().
*/
#Override
protected void onStopLoading() {
Log.d(TAG, "+++ onStopLoading() called! +++");
// Attempt to cancel the current load task if possible.
cancelLoad();
}
/**
* Must be called from the UI thread, triggered by a call to cancel(). Here,
* we make sure our Cursor is closed, if it still exists and is not already
* closed.
*/
#Override
public void onCanceled(final E dataList) {
Log.d(TAG, "+++ onCanceled() called! +++");
if (dataList != null && dataList.size() > 0) {
emptyDataList(dataList);
}
}
/**
* Must be called from the UI thread, triggered by a call to reset(). Here,
* we make sure our Cursor is closed, if it still exists and is not already
* closed.
*/
#Override
protected void onReset() {
super.onReset();
Log.d(TAG, "+++ onReset() called! +++");
// Ensure the loader is stopped
onStopLoading();
if (mLastDataList != null && mLastDataList.size() > 0) {
emptyDataList(mLastDataList);
}
mLastDataList = null;
}
protected void emptyDataList(final E dataList) {
if (dataList != null && dataList.size() > 0) {
for (int i = 0; i < dataList.size(); i++) {
dataList.remove(i);
}
}
}
}
####################################### Step 7
package com.example.locationsample.loader;
import java.util.List;
import android.content.Context;
import android.util.Log;
import com.example.locationsample.database.DataSource;
import com.example.locationsample.model.Test;
public class SQLiteTestDataLoader extends AbstractDataLoader<List<Test>> {
private static final String TAG = "SQLiteTestDataLoader";
private final DataSource<Test> mDataSource;
private String mSelection;
private String[] mSelectionArgs;
private String mGroupBy;
private String mHaving;
private String mOrderBy;
public SQLiteTestDataLoader(final Context context,
final DataSource<Test> dataSource, final String selection,
final String[] selectionArgs, final String groupBy,
final String having, final String orderBy) {
super(context);
mDataSource = dataSource;
}
#SuppressWarnings("unchecked")
#Override
protected List<Test> buildList() {
final List<Test> testList = mDataSource.read(mSelection,
mSelectionArgs, mGroupBy, mHaving, mOrderBy);
return testList;
}
public void insert(final Test entity) {
new InsertTask(this).execute(entity);
}
public void update(final Test entity) {
new UpdateTask(this).execute(entity);
}
public void delete(final Test entity) {
new DeleteTask(this).execute(entity);
}
private class InsertTask extends ContentChangingTask<Test, Void, Void> {
InsertTask(final SQLiteTestDataLoader loader) {
super(loader);
}
#Override
protected Void doInBackground(final Test... params) {
Log.i(TAG, "+++ Inserting... +++");
mDataSource.insert(params[0]);
return (null);
}
}
private class UpdateTask extends ContentChangingTask<Test, Void, Void> {
UpdateTask(final SQLiteTestDataLoader loader) {
super(loader);
}
#Override
protected Void doInBackground(final Test... params) {
Log.i(TAG, "+++ Updating... +++");
mDataSource.update(params[0]);
return (null);
}
}
private class DeleteTask extends ContentChangingTask<Test, Void, Void> {
DeleteTask(final SQLiteTestDataLoader loader) {
super(loader);
}
#Override
protected Void doInBackground(final Test... params) {
Log.i(TAG, "+++ Deleting... +++");
mDataSource.delete(params[0]);
return (null);
}
}
}
####################################### Step 8
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<TextView
android:id="#+id/txt_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
<EditText
android:id="#+id/edtxt_add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="#+id/btn_add"
android:inputType="text"
android:labelFor="#+id/edtxt_add" />
<Button
android:id="#+id/btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="ADD" />
<fragment
android:id="#+id/titles"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/btn_add"
android:layout_below="#id/txt_data"
class="com.example.locationsample.MainFragment" />
</RelativeLayout>
####################################### Step 9
package com.example.locationsample;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import android.app.Activity;
import android.database.sqlite.SQLiteDatabase;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.TextView;
import com.example.locationsample.database.DbHelper;
import com.example.locationsample.database.TestDataSource;
import com.example.locationsample.loader.SQLiteTestDataLoader;
import com.example.locationsample.model.Test;
import com.example.locationsample.utilities.Utils;
public class MainActivity extends Activity implements
OnClickListener {
private static final String TAG = "MainActivity";
private TextView mTxtData;
private EditText mEdTxtAdd;
private LocationManager mLocationManager;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTxtData = (TextView) findViewById(R.id.txt_data);
mEdTxtAdd = (EditText) findViewById(R.id.edtxt_add);
findViewById(R.id.btn_add).setOnClickListener(this);
DbHelper helper = new DbHelper(MainActivity.this);
SQLiteDatabase database = helper.getWritableDatabase();
TestDataSource dataSource = new TestDataSource(database);
List<Test> testList = dataSource.read();
if (testList == null || testList.size() == 0) {
dataSource.insert(new Test("11111"));
dataSource.insert(new Test("22222"));
dataSource.insert(new Test("33333"));
}
database.close();
}
#Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_add:
if (mEdTxtAdd == null
|| TextUtils.isEmpty(mEdTxtAdd.getText().toString())) {
return;
}
DbHelper helper = new DbHelper(MainActivity.this);
SQLiteDatabase database = helper.getWritableDatabase();
TestDataSource dataSource = new TestDataSource(database);
SQLiteTestDataLoader loader = new SQLiteTestDataLoader(
MainActivity.this, dataSource, null, null, null, null, null);
loader.insert(new Test(mEdTxtAdd.getText().toString()));
break;
default:
break;
}
}
}
####################################### Step 10
package com.example.locationsample;
import java.util.List;
import android.app.ListFragment;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Loader;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.widget.ArrayAdapter;
import com.example.locationsample.database.DbHelper;
import com.example.locationsample.database.TestDataSource;
import com.example.locationsample.loader.SQLiteTestDataLoader;
import com.example.locationsample.model.Test;
public class MainFragment extends ListFragment implements
LoaderCallbacks<List<Test>> {
private ArrayAdapter<Test> mAdapter;
// The Loader's id (this id is specific to the ListFragment's LoaderManager)
private static final int LOADER_ID = 1;
private static final boolean DEBUG = true;
private static final String TAG = "MainFragment";
private SQLiteDatabase mDatabase;
private TestDataSource mDataSource;
private DbHelper mDbHelper;
#Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mDbHelper = new DbHelper(getActivity());
mDatabase = mDbHelper.getWritableDatabase();
mDataSource = new TestDataSource(mDatabase);
mAdapter = new ArrayAdapter<Test>(getActivity(),
android.R.layout.simple_list_item_1);
setEmptyText("No data, please add from menu.");
setListAdapter(mAdapter);
setListShown(false);
if (DEBUG) {
Log.i(TAG, "+++ Calling initLoader()! +++");
if (getLoaderManager().getLoader(LOADER_ID) == null) {
Log.i(TAG, "+++ Initializing the new Loader... +++");
} else {
Log.i(TAG,
"+++ Reconnecting with existing Loader (id '1')... +++");
}
}
// Initialize a Loader with id '1'. If the Loader with this id already
// exists, then the LoaderManager will reuse the existing Loader.
getLoaderManager().initLoader(LOADER_ID, null, this);
}
#Override
public Loader<List<Test>> onCreateLoader(final int id, final Bundle arg1) {
final SQLiteTestDataLoader loader = new SQLiteTestDataLoader(
getActivity(), mDataSource, null, null, null, null, null);
return loader;
}
#Override
public void onLoadFinished(final Loader<List<Test>> loader,
final List<Test> data) {
Log.d(TAG, "+++### onLoadFinished() called! ###+++");
if (DEBUG)
Log.i(TAG, "+++ onLoadFinished() called! +++");
mAdapter.clear();
for (final Test test : data) {
mAdapter.add(test);
}
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
#Override
public void onLoaderReset(final Loader<List<Test>> arg0) {
Log.i(TAG, "+++ onLoadReset() called! +++");
mAdapter.clear();
}
#Override
public void onDestroy() {
super.onDestroy();
mDbHelper.close();
mDatabase.close();
mDataSource = null;
mDbHelper = null;
mDatabase = null;
}
}
#
Whenever i write something in edittext and click on Add button, it stores data only to database, But it doesn't any update into listview which we are showing at there.
Pls, check where do i mistake???
Add this line in the main activity inside the onClick
getLoaderManager().restartLoader(0,null,this);
I've got this problem where when I change the orientation of my device, for some reason the on-screen representation of the ListView associated with my ListFragment is retained, and a new ListView is inflated beneath it. To give you some context, I am creating a very simple app whose purpose is to test some DAO objects I created. I would have preferred to post some screenshots depicting the behavior my app is experiencing, but unfortunately, my reputation is too low at the moment.
The best I could describe it would be that when I change the orientation of the device from portrait to landscape (and vice versa), it inflates a whole new ListView without deflating the previous one. I have been looking into this for a couple of days now, and quite honestly, I am stumped. Any help you guys could offer would be greatly appreciated.
Here is the code for the ListFragment:
package edu.uark.csce.mobile.healthyshopper;
import android.app.ListFragment;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.CursorLoader;
import android.content.Loader;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
/**
* A fragment representing a list of Items.
* <p />
* <p />
* Activities containing this fragment MUST implement the {#link Callbacks}
* interface.
*/
public class ViewFragment extends ListFragment implements
LoaderCallbacks<Cursor> {
// Class Fields:
private static final String TAG = "healthyshopper.ViewFragment";
private static final int MAIN_DB_LOADER = 0;
// In the final application, any query to the main database needs to return
// at least these fields:
private final String[] NECESSARY_COLUMNS = { DAOContentProvider.FOOD_DES,
DAOContentProvider.FOOD_CALORIES, DAOContentProvider.FOOD_FAT,
DAOContentProvider.FOOD_CARBS, DAOContentProvider.FOOD_PROTEIN };
private final String FROM[] = {DAOContentProvider.FOOD_GROUP, DAOContentProvider.FOOD_DES,
DAOContentProvider.FOOD_MANUFAC, DAOContentProvider.FOOD_PROTEIN,
DAOContentProvider.FOOD_FAT, DAOContentProvider.FOOD_CARBS,
DAOContentProvider.FOOD_CALORIES, DAOContentProvider.FOOD_SERV_SIZE};
private final int TO[] = {R.id.fd_group, R.id.shrt_desc,
R.id.manufac, R.id.protein,
R.id.fat, R.id.carb,
R.id.cal, R.id.amt};
private int threadMode;
private int queryType;
private int foodGroupId;
private CursorLoader loader;
private SimpleCursorAdapter listAdapter;
// Preference Option Constants:
private int UI_THREAD = 0;
private int BRANCHED_THREAD = 1;
private int ALL_ROWS = 0;
private int SPECIFIC_FD_GROUP = 1;
private int LIMITED_ROWS = 2;
private OnFragmentInteractionListener mListener;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public ViewFragment() {
}
/***************************************
* Life cycle callbacks:
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "in onCreate()");
setRetainInstance(true);
}
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
if(savedInstanceState == null){
listAdapter = new SimpleCursorAdapter(getActivity(), R.layout.list_node, null, FROM, TO, 0);
setListAdapter(listAdapter);
}
}
#Override
public void onStart(){
super.onStart();
}
#Override
public void onResume(){
super.onResume();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()
.getApplicationContext());
Log.d(TAG, preferences.getString("THREADING_MODE", "BLANK"));
//Load testing preferences:
threadMode = Integer.valueOf(preferences.getString("THREADING_MODE","0"));
queryType = Integer.valueOf(preferences.getString("QUERY_TYPE", "0"));
foodGroupId = Integer.valueOf(preferences.getString("FOOD_GROUP", "0")); // Default
if (threadMode == BRANCHED_THREAD) {
loader = (CursorLoader) getLoaderManager().initLoader(
MAIN_DB_LOADER, null, this);
} else {
String[] projection = null;
String selection = null;
if (queryType == SPECIFIC_FD_GROUP) {
Log.d(TAG, "Query Type: " + (getResources().getStringArray(R.array.query_type_select))[queryType]);
selection = DAOContentProvider.FOOD_GROUP + " = " + foodGroupId;
} else if (queryType == LIMITED_ROWS) {
projection = NECESSARY_COLUMNS;
}
/*This is for testing only. Ordinarily, you should NEVER perform Db operations on the UI thread.*/
listAdapter.swapCursor(getActivity().getContentResolver().query(
DAOContentProvider.CONTENT_URI, projection, selection, null, null));
}
}
#Override
public void onPause(){
super.onPause();
}
#Override
public void onStop(){
super.onStop();
}
#Override
public void onDestroyView(){
super.onDestroyView();
Log.e(TAG, "in onDestroyView()");
setListAdapter(null);
listAdapter.swapCursor(null);
listAdapter = null;
}
#Override
public void onDestroy(){
super.onDestroy();
}
#Override
public void onDetach(){
super.onDetach();
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
if (null != mListener) {
}
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated to
* the activity and potentially other fragments contained in that activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
public void onFragmentInteraction(String id);
}
/**
* LoaderManager Callbacks;
*/
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) {
case MAIN_DB_LOADER:
String[] projection = null;
String selection = null;
if (queryType == SPECIFIC_FD_GROUP) {
Log.d(TAG,
"Query Type: "
+ (getResources()
.getStringArray(R.array.query_type_select))[queryType]);
selection = DAOContentProvider.FOOD_GROUP + " = " + foodGroupId;
} else if (queryType == LIMITED_ROWS) {
projection = NECESSARY_COLUMNS;
}
return new CursorLoader(getActivity(),
DAOContentProvider.CONTENT_URI, projection, selection,
null, null);
default:
return null;
}
}
#Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
listAdapter.swapCursor(arg1);
listAdapter.notifyDataSetChanged();
}
#Override
public void onLoaderReset(Loader<Cursor> arg0) {
listAdapter.swapCursor(null);
listAdapter.notifyDataSetChanged();
}
}
And here is the code for its associated Activity:
package edu.uark.csce.mobile.healthyshopper;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class TestingActivity extends Activity implements ViewFragment.OnFragmentInteractionListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testing);
ViewFragment list = new ViewFragment();
getFragmentManager().beginTransaction().add(R.id.test_layout, list).commit();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.testing, menu);
return true;
}
#Override
public void onFragmentInteraction(String id) {
// TODO Auto-generated method stub
}
}
You're using setRetainInstance(true); in your fragment. This makes the fragment to be retained and reused when the activity is re-created.
You can read more detailed description in this question: Further understanding setRetainInstance(true)
I am used to building lists in android using adapters. If I need some long-to-get data, I use an asynctask, or a simple runnable, to update the data structure on which the adapter rely, and call notifyDataChanged on the adapter.
Although it is not straightforward, I finally find this is a simple model and it allows a good separation of logic presentation (in the asynctask, update a data structure) and the view (an adapter acting as a view factory, mostly).
Nevertheless, I read recently about loaders introduced in HoneyComb and included in the backward compatibility support-library, I tried them and find the introduce a lot of complexity. They are difficult to handle and add some kind of magic to this whole process through loader managers, add a lot of code and don't decrease the number of classes or collaborating items but I may be wrong and would like to hear some good points on loaders.
What are they advantages of loaders in terms of lines of code, clarity and effort ?
What are they advantages of loaders in terms of role separation during data loading, or more broadly, in terms of design ?
Are they the way to go, should I replace all my list data loading to implement them through loaders ?
Ok, this is a developers' forum, so here is an example. Please, make it better with loaders :
package com.sof.test.loader;
import java.util.ArrayList;
import java.util.List;
import android.app.ListActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.TextView;
/** The activity. */
public class LoaderTestActivity extends ListActivity {
private DataSourceOrDomainModel dataSourceOrDomainModel = new DataSourceOrDomainModel();
private List<Person> listPerson;
private PersonListAdapter personListAdapter;
private TextView emptyView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listPerson = new ArrayList<Person>();
personListAdapter = new PersonListAdapter( listPerson );
setListAdapter( personListAdapter );
setUpEmptyView();
new PersonLoaderThread().execute();
}
public void setUpEmptyView() {
emptyView = new TextView( this );
emptyView.setLayoutParams( new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT ) );
emptyView.setVisibility(View.GONE);
((ViewGroup)getListView().getParent()).addView(emptyView);
getListView().setEmptyView(emptyView);
}
/** Simulate a long task to get data. */
private class PersonLoaderThread extends AsyncTask<Void, Integer, List<Person>> {
#Override
protected List<Person> doInBackground(Void... params) {
return dataSourceOrDomainModel.getListPerson( new ProgressHandler());
}
#Override
protected void onProgressUpdate(Integer... values) {
emptyView.setText( "Loading data :" + String.valueOf( values[ 0 ] ) +" %" );
}
#Override
protected void onPostExecute(List<Person> result) {
listPerson.clear();
listPerson.addAll( result );
personListAdapter.notifyDataSetChanged();
}
private class ProgressHandler implements ProgressListener {
#Override
public void personLoaded(int count, int total) {
publishProgress( 100*count / total );
}
}
}
/** List item view factory : the adapter. */
private class PersonListAdapter extends ArrayAdapter<Person> {
public PersonListAdapter( List<Person> listPerson ) {
super(LoaderTestActivity.this, 0, listPerson );
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if( convertView == null ) {
convertView = new PersonView( getContext() );
}
PersonView personView = (PersonView) convertView;
personView.setPerson( (Person) getItem(position) );
return personView;
}
}
}
A small callback interface for progress
package com.sof.test.loader;
/** Callback handler during data load progress. */
public interface ProgressListener {
public void personLoaded(int count, int total );
}
A list item widget
package com.sof.test.loader;
import com.sof.test.loader.R;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.TextView;
/** List Item View, display a person */
public class PersonView extends LinearLayout {
private TextView personNameView;
private TextView personFirstNameView;
public PersonView(Context context) {
super(context);
LayoutInflater inflater= (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate( R.layout.person_view,this );
personNameView = (TextView) findViewById( R.id.person_name );
personFirstNameView = (TextView) findViewById( R.id.person_firstname );
}
public void setPerson( Person person ) {
personNameView.setText( person.getName() );
personFirstNameView.setText( person.getFirstName() );
}
}
It's xml : res/person_view.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/person_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/person_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true" />
<TextView
android:id="#+id/person_firstname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/person_name" />
</RelativeLayout>
The data source or model, providing data (slowly)
package com.sof.test.loader;
import java.util.ArrayList;
import java.util.List;
/** A source of data, can be a database, a WEB service or a model. */
public class DataSourceOrDomainModel {
private static final int PERSON_COUNT = 100;
public List<Person> getListPerson( ProgressListener listener ) {
List<Person> listPerson = new ArrayList<Person>();
for( int i=0; i < PERSON_COUNT ; i ++ ) {
listPerson.add( new Person( "person", "" + i ) );
//kids, never do that at home !
pause();
if( listener != null ) {
listener.personLoaded(i,PERSON_COUNT);
}//if
}
return listPerson;
}//met
private void pause() {
try {
Thread.sleep( 100 );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The POJO representing a person :
package com.sof.test.loader;
/** A simple POJO to be displayed in a list, can be manipualted as a domain object. */
public class Person {
private String name;
private String firstName;
public Person(String name, String firstName) {
this.name = name;
this.firstName = firstName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}//class
In case someone is looking for the loader version of my previous example : here it is :
package com.sof.test.loader;
import java.util.ArrayList;
import android.app.LoaderManager;
import java.util.List;
import android.app.ListActivity;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.Loader;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.TextView;
/** The activity. */
public class LoaderTestActivity2 extends ListActivity implements
LoaderManager.LoaderCallbacks<List<Person>> {
private DataSourceOrDomainModel dataSourceOrDomainModel = new DataSourceOrDomainModel();
private List<Person> listPerson;
private PersonListAdapter personListAdapter;
private TextView emptyView;
private Loader<List<Person>> personLoader;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listPerson = new ArrayList<Person>();
personListAdapter = new PersonListAdapter(listPerson);
setListAdapter(personListAdapter);
personLoader = new PersonLoader(this, dataSourceOrDomainModel, new ProgressHandler() );
setUpEmptyView();
getLoaderManager().initLoader(0, null, this);
personLoader.forceLoad();
}
public void setUpEmptyView() {
emptyView = new TextView(this);
emptyView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
emptyView.setVisibility(View.GONE);
((ViewGroup) getListView().getParent()).addView(emptyView);
getListView().setEmptyView(emptyView);
}
public void publishProgress(int progress) {
emptyView.setText("Loading data :" + String.valueOf(progress) + " %");
}
#Override
public Loader<List<Person>> onCreateLoader(int arg0, Bundle arg1) {
return personLoader;
}
#Override
public void onLoadFinished(Loader<List<Person>> personLoader, List<Person> result) {
listPerson.clear();
listPerson.addAll(result);
personListAdapter.notifyDataSetChanged();
}
#Override
public void onLoaderReset(Loader<List<Person>> arg0) {
listPerson.clear();
personListAdapter.notifyDataSetChanged();
}
/** List item view factory : the adapter. */
private class PersonListAdapter extends ArrayAdapter<Person> {
public PersonListAdapter(List<Person> listPerson) {
super(LoaderTestActivity2.this, 0, listPerson);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = new PersonView(getContext());
}
PersonView personView = (PersonView) convertView;
personView.setPerson((Person) getItem(position));
return personView;
}
}
private class ProgressHandler implements ProgressListener {
#Override
public void personLoaded(final int count, final int total) {
runOnUiThread( new Runnable() {
#Override
public void run() {
publishProgress(100 * count / total);
}
});
}
}
}
class PersonLoader extends AsyncTaskLoader<List<Person>> {
private DataSourceOrDomainModel dataSourceOrDomainModel;
private ProgressListener progressHandler;
public PersonLoader(Context context, DataSourceOrDomainModel dataSourceOrDomainModel, ProgressListener progressHandler ) {
super(context);
this.dataSourceOrDomainModel = dataSourceOrDomainModel;
this.progressHandler = progressHandler;
}
#Override
public List<Person> loadInBackground() {
return dataSourceOrDomainModel.getListPerson( progressHandler );
}
}
It would be more difficult to add support (support librairy) to this example as there is no equivalent of ListAcitivity in the support librairy. I would have either to create a ListFragment or create an FragmentActivity and give it a layout including a list.
One problem your code has which loaders aim to fix is what happens if your activity is restarted (say due to device rotation or config change) while your async task is still in progress? in your case your restarted activity will start a 2nd instance of the task and throw away the results from the first one. When the first one completes you can end up with crashes due to the fact your async task has a reference is what is now a finished activity.
And yes using loaders often makes for more/more complex code, particularly if you can't use one of the provided loaders.