scientiffic
scientiffic

Reputation: 9415

Stop an AsyncTask on tab switch

I have an Android application that has 3 tabs; they're created using an Activity (TabsFragmentActivity), and the interfaces for each tab are created using Fragments (TabProfile, TabSearch, and TabLocations fragments).

In one of my fragments (TabLocations), I start an AsyncTask. I want to be able to cancel the AsyncTask once the user switches to a different tab. Is there a way for me to do this through the Activity (TabsFragmentActivity) that populates the tabs?

I think what I need to do is check on the status of the AsyncTask from the TabsFragmentActivity activity, but I'm not sure how to do this since the AsyncTask is initiated in one of the fragments and not the activity itself. The issue I have now is that at the async task, it tries to populate some listviews, and the listviews aren't on the screen if the user switches tabs.

TabFragmentActivity.java

package com.example.q;

import java.util.HashMap;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.View;
import android.widget.TabHost;
import android.widget.TabHost.TabContentFactory;

import com.parse.Parse;
import com.parse.ParseACL;
import com.parse.ParseUser;

public class TabsFragmentActivity extends FragmentActivity implements TabHost.OnTabChangeListener {
    private static final String TAG = "TabsFragmentActivity";

    private TabHost mTabHost;
    private HashMap mapTabInfo = new HashMap();
    private TabInfo mLastTab = null;

    private class TabInfo {
         private String tag;
         private Class clss;
         private Bundle args;
         private Fragment fragment;
         TabInfo(String tag, Class clazz, Bundle args) {
             this.tag = tag;
             this.clss = clazz;
             this.args = args;
         }

    }

    class TabFactory implements TabContentFactory {

        private final Context mContext;

        /**
         * @param context
         */
        public TabFactory(Context context) {
            mContext = context;
        }

        /** (non-Javadoc)
         * @see android.widget.TabHost.TabContentFactory#createTabContent(java.lang.String)
         */
        public View createTabContent(String tag) {
            View v = new View(mContext);
            v.setMinimumWidth(0);
            v.setMinimumHeight(0);
            return v;
        }

    }
    /** (non-Javadoc)
     * @see android.support.v4.app.FragmentActivity#onCreate(android.os.Bundle)
     */
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Step 1: Inflate layout
        setContentView(R.layout.tabs_layout);
        // Step 2: Setup TabHost
        initialiseTabHost(savedInstanceState);
        mTabHost.setCurrentTab(1);
        if (savedInstanceState != null) {
            //mTabHost.setCurrentTab(1);
            mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab")); //set the tab as per the saved state
        }
        // initialize Parse
        Log.d(TAG, "initializing Parse");
        Parse.initialize(this, "rReToGbnCSdQEi7Q33UrCO4U395Rk7RcKPcNncOn", "haJKaN0zbNKhgBT1t9jGUY0ih4rYQfPMA9p31BSw"); 

        ParseUser.enableAutomaticUser();
        ParseACL defaultACL = new ParseACL();

        // If you would like all objects to be private by default, remove this line.
        defaultACL.setPublicReadAccess(true);

        ParseACL.setDefaultACL(defaultACL, true);    
    }

    /** (non-Javadoc)
     * @see android.support.v4.app.FragmentActivity#onSaveInstanceState(android.os.Bundle)
     */
    protected void onSaveInstanceState(Bundle outState) {
        outState.putString("tab", mTabHost.getCurrentTabTag()); //save the tab selected
        super.onSaveInstanceState(outState);
    }

    /**
     * Step 2: Setup TabHost
     */
    private void initialiseTabHost(Bundle args) {
        mTabHost = (TabHost)findViewById(android.R.id.tabhost);
        mTabHost.setup();
        TabInfo tabInfo = null;
        TabsFragmentActivity.addTab(this, this.mTabHost, this.mTabHost.newTabSpec("Tab1").
                setIndicator("", getResources().getDrawable(R.drawable.profile)), (tabInfo = new TabInfo("Tab1", TabProfile.class, args)));
        this.mapTabInfo.put(tabInfo.tag, tabInfo);
        TabsFragmentActivity.addTab(this, this.mTabHost, this.mTabHost.newTabSpec("Tab2").
                setIndicator("", getResources().getDrawable(R.drawable.locations) ), (tabInfo = new TabInfo("Tab2", TabLocations.class, args)));
        this.mapTabInfo.put(tabInfo.tag, tabInfo);
        TabsFragmentActivity.addTab(this, this.mTabHost, this.mTabHost.newTabSpec("Tab3").
                setIndicator("", getResources().getDrawable(R.drawable.search)), (tabInfo = new TabInfo("Tab3", TabSearch.class, args)));
        this.mapTabInfo.put(tabInfo.tag, tabInfo);

        // Default to the second tab
        this.onTabChanged("Tab2");

        mTabHost.setOnTabChangedListener(this);
    }

    /**
     * @param activity
     * @param tabHost
     * @param tabSpec
     * @param clss
     * @param args
     */
    private static void addTab(TabsFragmentActivity activity, TabHost tabHost, TabHost.TabSpec tabSpec, TabInfo tabInfo) {
        // Attach a Tab view factory to the spec
        tabSpec.setContent(activity.new TabFactory(activity));
        String tag = tabSpec.getTag();

        // Check to see if we already have a fragment for this tab, probably
        // from a previously saved state.  If so, deactivate it, because our
        // initial state is that a tab isn't shown.
        tabInfo.fragment = activity.getSupportFragmentManager().findFragmentByTag(tag);
        if (tabInfo.fragment != null && !tabInfo.fragment.isDetached()) {
            FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
            ft.detach(tabInfo.fragment);
            ft.commit();
            activity.getSupportFragmentManager().executePendingTransactions();
        }

        tabHost.addTab(tabSpec);
    }

    /** (non-Javadoc)
     * @see android.widget.TabHost.OnTabChangeListener#onTabChanged(java.lang.String)
     */
    public void onTabChanged(String tag) {
        Log.d(TAG, "in onTabChanged");
        TabInfo newTab = (TabInfo) this.mapTabInfo.get(tag);
        if (mLastTab != newTab) {
            Log.d(TAG, "switching to new tab");
            FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
            if (mLastTab != null) {
                Log.d(TAG, "last tab not null");
                if (mLastTab.fragment != null) {
                    Log.d(TAG, "last tab fragment not null");
                    ft.detach(mLastTab.fragment);
                }
            }
            if (newTab != null) {
                if (newTab.fragment == null) {
                    newTab.fragment = Fragment.instantiate(this,
                            newTab.clss.getName(), newTab.args);
                    ft.add(R.id.realtabcontent, newTab.fragment, newTab.tag);
                } else {
                    ft.attach(newTab.fragment);
                }
            }

            mLastTab = newTab;
            ft.commit();
            this.getSupportFragmentManager().executePendingTransactions();
        }
    }


}

TabLocations.java (There is a lot in this file, so I've edited most out for clarity):

public class TabLocations extends Fragment {

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (container == null) {
            return null;
        }
        return (RelativeLayout)inflater.inflate(R.layout.tab_locations_layout, container, false);
    }


    @Override
    public void onActivityCreated(Bundle savedInstanceState) {

        // Run AsyncTask for getting Google Places
        FetchGooglePlaces places = new FetchGooglePlaces(getActivity());
        places.execute();

 }       


}

FetchGooglePlaces.java - my AsyncTask

package com.example.q;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

import android.app.Activity;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;

import com.parse.FindCallback;
import com.parse.GetCallback;
import com.parse.ParseException;
import com.parse.ParseObject;
import com.parse.ParseQuery;


public class FetchGooglePlaces extends AsyncTask<String, PlacesList, String>{

    private static final String TAG = "FetchGooglePlaces";

     // Google Places
     GooglePlaces googlePlaces;

     // Places List
     public PlacesList nearPlaces;

     // GPS Location
     GPSTracker gps;

     // Progress layouts for qlocations and alocations
     LinearLayout qProgress;
     LinearLayout aProgress;

     // KEY Strings
     public static String KEY_REFERENCE = "reference"; // id of the place
     public static String KEY_NAME = "name"; // name of the place

     // ListView of all q locations detected
     ListView qLV;

     // ListView of all all non-q locations detected
     ListView allLV;

     // Refresh Button
     Button refresh;

     // TabLocations activity
     public Activity activity;

     PlacesDBHelper placesDBHelper;
     SQLiteDatabase db;

     // ArrayList to store the names of qLocations
     ArrayList<String[]> qLocations;

     //ArrayList for storing QLocations
     ArrayList<QLocation> qLocationData = new ArrayList<QLocation>();

     //Temporary ArrayList for storing QLocations
     ArrayList<String[]> qLocationDataString = new ArrayList<String[]>();

     //ArrayList for storing allLocations
     ArrayList<QLocation> allLocationData = new ArrayList<QLocation>();

     //Temporary ArrayList for storing allLocations
     ArrayList<String[]> allLocationDataString = new ArrayList<String[]>();

     boolean done = false;

     boolean checking = true;

     int counter = 0;

     public FetchGooglePlaces(Activity a){
         activity = a;
     }

    /**
     * Before starting background thread Show Progress Dialog
     * */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();

//        placesDBHelper = new PlacesDBHelper(activity);
//        Log.d(TAG, "attempting to create database");
//        
//        Log.d(TAG, "placesDBHelper: " + placesDBHelper.getDatabaseName());

        // set visibility of progress bars
        qProgress = (LinearLayout) activity.findViewById(R.id.lin_progressBar);
        qProgress.setVisibility(View.VISIBLE);
        aProgress = (LinearLayout) activity.findViewById(R.id.lin_progressBar2);
        aProgress.setVisibility(View.VISIBLE);

        // creating GPS Class object
        gps = new GPSTracker(activity);
    }

    /**
     * getting Places JSON
     * */
    protected String doInBackground(String... args) {
        // creating Places class object
        googlePlaces = new GooglePlaces();

        try {
            Log.d(TAG, "getting google places");
            String types = "cafe|restaurant"; // Listing places only cafes, restaurants

            // Radius in meters - increase this value if you don't find any places
            double radius = 1000; // 1000 meters 

            // get nearest places
            nearPlaces = googlePlaces.search(gps.getLatitude(),
                    gps.getLongitude(), radius, types);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * After completing background task Dismiss the progress dialog
     * and show the data in UI
     * Always use runOnUiThread(new Runnable()) to update UI from background
     * thread, otherwise you will get error
     * **/
    protected void onPostExecute(String file_url) {
        Log.d(TAG, "complete");         

        /**
         * Updating parsed Places into LISTVIEW
         * */
        // Get json response status
         String status = nearPlaces.status;

        // Check for all possible status
        if(status.equals("OK")){
            // Successfully got places details
            if (nearPlaces.results != null) {

                // get current list of Q Locations + their wait times
                qLocations = new ArrayList<String[]>();
                ParseQuery qs = new ParseQuery("QLocations");
                //EDIT: THIS SHOULD ONLY QUERY LOCATIONS THAT ARE CLOSEBY (TO MINIMIZE SEARCH TIME)
                qs.findInBackground(new FindCallback() {

                    @Override
                    public void done(final List<ParseObject> qList, ParseException e) {                        
                        if (e==null){

                            // add each parse location to the qLocations array
                            for(int i= 0; i < qList.size(); i++){
                                if(checking){
                                        final String name = qList.get(i).getString("name");
                                        checking = false;

                                        //get queue length for each location
                                        ParseObject place = qList.get(i);

                                        place.getParseObject("Line").fetchIfNeededInBackground(new GetCallback() {                                            
                                            @Override
                                            public void done(ParseObject object, ParseException e) {
                                                String lineLength = Integer.toString(object.getInt("length"));
                                                String[] locationArray = new String[] {name, lineLength};
                                                qLocations.add(locationArray);
                                                //Log.d(TAG, "added " + qLocations.get(qLocations.size()-1)[0] + " to qLocations");
                                                counter++;
                                                if (counter == qList.size()){
                                                    drawListViews();                                                    
                                                }
                                            }
                                        });
                                        checking = true;
                                    }
                            }                            
                        }
                        else{
                            Log.d("Error", "Error: " + e.getMessage());
                        }  
                        }
                    });
            }
        }
        else{
            Toast.makeText(activity, "Google Places Error!", Toast.LENGTH_SHORT);
        }
    }

    public void drawListViews(){

        Log.d(TAG, "in drawListViews()");

        // loop through each place
        int counter = 0;   

        for (Place p : nearPlaces.results) {

            //limit to 10 search results
            if (counter<9){
                  HashMap<String, String> map = new HashMap<String, String>();  

                // Place reference won't display in listview - it will be hidden
                // Place reference is used to get "place full details"
                map.put(KEY_REFERENCE, p.reference);

                // Place name
                map.put(KEY_NAME, p.name);

                boolean placeExists = false;
                String lineLength = "";

                for(int i=0; i<qLocations.size(); i++){
                    // if the google places exists as a Q
                    if (p.name.equals(qLocations.get(i)[0])){
                        placeExists = true;
                        lineLength = qLocations.get(i)[1];
                        break;
                    }
                }
                if (placeExists){
                    // add to qLocationData array
                    qLocationDataString.add(new String[] {p.name, p.reference, lineLength});
                }else{
                    // add to allLocationData array
                    allLocationDataString.add(new String[] {p.name, p.reference, lineLength});
                }

        }else{
                break;
        }
            counter++;
        }

        //sort qLocationDataString by length of line
        Collections.sort(qLocationDataString, new Comparator<String[]>(){
            public int compare(String[] strings, String[] otherStrings){
                return Integer.valueOf(strings[2]).compareTo(Integer.valueOf(otherStrings[2]));
            }
        });

        // add sorted qLocationDataString to qLocationData to pass to the array adapter
        for(int i=0; i<qLocationDataString.size(); i++){
            qLocationData.add(new QLocation(qLocationDataString.get(i)[0], qLocationDataString.get(i)[1], qLocationDataString.get(i)[2] ));
        }

        //sort allLocationDataString alphabetically
        Collections.sort(allLocationDataString, new Comparator<String[]>(){
            public int compare(String[] strings, String[] otherStrings){
                return strings[0].compareTo(otherStrings[0]);
            }
        });


        // add sorted allLocationDataString to allLocationData to pass to the array adapter
        for(int i=0; i<allLocationDataString.size(); i++){
            allLocationData.add(new QLocation(allLocationDataString.get(i)[0], allLocationDataString.get(i)[1], allLocationDataString.get(i)[2] ));
        }

        // Getting qListView: List View for locations with Qs
        qLV = (ListView) activity.findViewById(R.id.qListView);

        // list adapter
        ListAdapter qAdapter = new QAdapter(activity, R.layout.google_places_item, qLocationData, "qs"); 

        // Adding data into listview
        qLV.setAdapter(qAdapter);

        // Getting allListView: List View for locations without Qs
        allLV = (ListView) activity.findViewById(R.id.allListView);

        // list adapter
        ListAdapter allAdapter = new QAdapter(activity, R.layout.google_places_item, allLocationData, "all");

        // Adding data into listview
        allLV.setAdapter(allAdapter);

        // hide progress bars
        qProgress.setVisibility(View.GONE);
        aProgress.setVisibility(View.GONE);

//        // show refresh button
//        refresh = (Button) activity.findViewById(R.id.refreshButton);
//        refresh.setVisibility(View.VISIBLE);

        returnPlaces(nearPlaces);

        Button maps = (Button) activity.findViewById(R.id.btn_show_map);

        /** Button click event for showing map */
            maps.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                Log.d("attempting to create new intent", "show map");
            Intent i = new Intent(activity,
                    QMapActivity.class);
            // Sending user current geo location
            i.putExtra("user_latitude", Double.toString(gps.getLatitude()));
            i.putExtra("user_longitude", Double.toString(gps.getLongitude()));

            // passing near places to map activity
            i.putExtra("near_places", nearPlaces);
            // pass list of q locations to map activity
            i.putExtra("q_places", qLocationDataString);

            Log.d(TAG, nearPlaces.toString());
            // staring activity
            activity.startActivity(i);
        }
    });

}




    private PlacesList returnPlaces(PlacesList places){
        return places;
    }


}

Upvotes: 0

Views: 1460

Answers (2)

wtsang02
wtsang02

Reputation: 18863

You can do this by saving the instance in the Application Context which is a bad oop concept. You can also create you instance of AsyncTask in your Activity and execute it in your Fragments

class TabsFragmentActivity extends FragmentActivity implements TabHost.OnTabChangeListener{
    private static FetchGooglePlaces task;
        task=new FetchGooglePlaces ();//Create but not execute.
//You can decide to cancel it when onTabChange triggers with task.cancel();

}
class TabLocations extends Fragment {
        void onCreate(){
            TabsFragmentActivity .getTask().execute; //execute it here.
        }
}

Upvotes: 1

StarsSky
StarsSky

Reputation: 6711

Take a look at the Fragment life cycle lifecycle. Have you think to stop the asynctask during onStop() or onPause() overridden method ?

Upvotes: 1

Related Questions