Android using Fragment as a container for AsyncTask

When the AsyncTask is implemented in an Activity, and when activity restarts on configuration changes, it will cause issues such as memory leaks and spanning the same tasks multiple times if it is not handled correctly. This post addresses this issue by using a fragment as a container for AsyncTask and retain the fragment when the activity is restarted on configuration changes.

The task in this example is to download a zip file and unpack it.

1. Make sure the INTERNET and WRITE_EXTERNAL_STORAGE permissions are granted in the manifest file.

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2. In the MainActivity or any other Activity that you want to launch the AsyncTask in a Fragment. Write a method to launch the DownloadTaskFragment when a button is clicked.

public void launchTaskFragment() {
	FragmentManager fm = getSupportFragmentManager();
	downloadFragment = (DownloadFragment) fm.findFragmentByTag("task_fragment");

	// if it's null, it was created, otherwise it was created and retained
	if (downloadFragment == null) {
	    downloadFragment = new DownloadFragment();
	    fm.beginTransaction().add(downloadFragment, "task_fragment").commit();
	}	
}

3. In the same Activity class, make it to implement the interface DownloadFragment.DownloadCallbacks defined in the DownloadFragment.java.

@Override
public void onPostExecute(String msg) {
    Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    removeDownloadFragment();
}

private void removeDownloadFragment() {
    FragmentManager fm = getSupportFragmentManager();
    downloadFragment = (DownloadFragment) fm.findFragmentByTag(DOWNLOAD_FRAGMENT);
    if (downloadFragment != null) {
        fm.beginTransaction()
                .remove(downloadFragment)
                .commit();
    }
}

4. The DownloadFragment.java, this fragment is retained by setRetainInstance(true);, attached by onAttach and detached by onDetach(), when the activity is restarted on configuration change, this fragment is retained, the new activity will be attached to this fragment after its restart. Once the AsyncTask finishes it’s job and calls the onPostExecute which is implemented by the activity, only then the fragment will be destroyed.

public class DownloadFragment extends Fragment {
    interface DownloadCallbacks {
        void onPostExecute(String msg);
    }

    private DownloadCallbacks mCallbacks;
    private DownloadTask mTask;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true); // Retain this fragment on configuration changes.

        File gameDir = new File("/data/data/" + getActivity().getPackageName() + "/games");
        gameDir.mkdirs();

        // Create and execute the background task.
        mTask = new DownloadTask();
        mTask.execute("https://github.com/gabrielecirulli/2048/archive/master.zip", "/data/data/" + getActivity().getPackageName() + "/games/2048.zip");
    }


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mCallbacks = (DownloadCallbacks) getActivity();
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCallbacks = null;
    }

    private boolean unpackZip(String filePath) {
        InputStream is;
        ZipInputStream zis;
        try {

            File zipfile = new File(filePath);
            String parentFolder = zipfile.getParentFile().getPath();
            String filename;

            is = new FileInputStream(filePath);
            zis = new ZipInputStream(new BufferedInputStream(is));
            ZipEntry ze;
            byte[] buffer = new byte[1024];
            int count;

            while ((ze = zis.getNextEntry()) != null) {
                filename = ze.getName();

                if (ze.isDirectory()) {
                    File fmd = new File(parentFolder + "/" + filename);
                    fmd.mkdirs();
                    continue;
                }

                FileOutputStream fout = new FileOutputStream(parentFolder + "/" + filename);

                while ((count = zis.read(buffer)) != -1) {
                    fout.write(buffer, 0, count);
                }

                fout.close();
                zis.closeEntry();
            }

            zis.close();
        } catch(IOException e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }

    private class DownloadTask extends AsyncTask<String, Integer, String> {

        @Override
        protected String doInBackground(String... args) {
            InputStream input = null;
            OutputStream output = null;
            HttpURLConnection connection = null;
            String destinationFilePath = "";
            try {
                URL url = new URL(args[0]);
                destinationFilePath = args[1];

                connection = (HttpURLConnection) url.openConnection();
                connection.connect();

                if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                    return "Server returned HTTP " + connection.getResponseCode() + " " + connection.getResponseMessage();
                }

                // download the file
                input = connection.getInputStream();

                Log.d("DownloadFragment ", "destinationFilePath=" + destinationFilePath);
                new File(destinationFilePath).createNewFile();
                output = new FileOutputStream(destinationFilePath);

                byte data[] = new byte[4096];
                int count;
                while ((count = input.read(data)) != -1) {
                    output.write(data, 0, count);
                }
            } catch (Exception e) {
                e.printStackTrace();
                return e.toString();
            } finally {
                try {
                    if (output != null)
                        output.close();
                    if (input != null)
                        input.close();
                } catch (IOException ignored) {
                }

                if (connection != null)
                    connection.disconnect();
            }

            File f = new File(destinationFilePath);

            Log.d("DownloadFragment ", "f.getParentFile().getPath()=" + f.getParentFile().getPath());
            Log.d("DownloadFragment ", "f.getName()=" + f.getName().replace(".zip", ""));
            unpackZip(destinationFilePath);
            return "Download Completed!";
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            Log.d("DownloadFragment ", s);
            mCallbacks.onPostExecute(s);
        }
    }
}

Search within Codexpedia

Custom Search

Search the entire web

Custom Search