0

I'm writting an application that run an infinite ping, with the system command /system/bin/ping, to measure network status.

To handle the running thread I'm using a Fragment. So the process still running during screen rotation or screen lock. The application seems to work well when I press Back button or Home button. But when I kill the application with Recent Apps the thread is still running even the main application is closed.

For sure I can limit the number of ping with -c option but I'd like to stop the thread immediatly after the application's close.

Is there a way to know that an application is killed using Recent Apps ?

Should I use another way than Fragment / AsyncTask to handle this type of thread ?

Thanks for the help

Main class:

public class MainActivity extends Activity implements PingFragment.PingTaskCallbacks {

    /* Fragment used to manage the ping task*/
    private PingFragment mPingFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FragmentManager fm = getFragmentManager();
        mPingFragment = (PingFragment) fm.findFragmentByTag("ping");

        // If the Fragment is non-null, then it is currently being
        // retained across a configuration change.
        if (mPingFragment == null) {
            mPingFragment = new PingFragment();
            fm.beginTransaction().add(mPingFragment, "ping").commit();
        }//endIf

        ...
    }//endFct

    @Override
    protected void onResume() {
        super.onResume();
    }//endFct

    @Override
    protected void onPause() {
        super.onPause();
    }//endFct

    public void doPing() {

        String url = "www.google.com";
        mPingFragment.startPing(url);
    }

    public void stopPing() {

        mPingFragment.stopPing();
    }

    @Override
    public void onProgressUpdate(String aPingLine) {

        // method called on every ping
    }
}

Fragment class:

public class PingFragment extends Fragment {

    /**
     * Callback interface through which the fragment will report the
     * task's progress and results back to the Activity.
     */
    static interface PingTaskCallbacks {
        void onProgressUpdate(String aPingLine);
    }//endIntf

    private PingTaskCallbacks mCallbacks;
    private PingTask mTask;

    /**
     * This method will only be called once when the retained
     * Fragment is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Retain this fragment across configuration changes.
        setRetainInstance(true);
    }

    /**
     * Hold a reference to the parent Activity so we can report the
     * task's current progress and results. The Android framework
     * will pass us a reference to the newly created Activity after
     * each configuration change.
     */
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mCallbacks = (PingTaskCallbacks) activity;
    }

    /**
     * Set the callback to null so we don't accidentally leak the
     * Activity instance.
     */
    @Override
    public void onDetach() {
        super.onDetach();
        mCallbacks = null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("PingFragment.onDestroy()", "*** Fragment call destroy ***");
        stopPing();
    }

    public void startPing(String url) {
        if (mTask != null && ! mTask.getStatus().equals(AsyncTask.Status.FINISHED)) {
            mTask.stop();
        }//endIf

        // Create and execute the background task.
        mTask = new PingTask();
        mTask.execute(url);
    }

    public void stopPing() {
        mTask.stop();
        mTask = null;
    }

    /**
     * A task that performs some ping in background.
     *
     * Note that we need to check if the callbacks are null in each
     * method in case they are invoked after the Activity's and
     * Fragment's onDestroy() method have been called.
     */
    private class PingTask extends AsyncTask<String, String, Void> {

        private PipedOutputStream mPOut;
        private PipedInputStream mPIn;
        private LineNumberReader mReader;
        private Process mProcess;

        @Override
        protected void onPreExecute() {

            mPOut = new PipedOutputStream();
            try {
                mPIn = new PipedInputStream(mPOut);
                mReader = new LineNumberReader(new InputStreamReader(mPIn));
            } catch (IOException e) {
                cancel(true);
            }//endTry
        }//endF

        /**
         * Note that we do NOT call the callback object's methods
         * directly from the background thread, as this could result
         * in a race condition.
         */
        @Override
        protected Void doInBackground(String... params) {

            String aLine = "";

            try {
                mProcess = new ProcessBuilder()
                        .command("/system/bin/ping", params[0])
                        .redirectErrorStream(true)
                        .start();

                try {
                    InputStream in = mProcess.getInputStream();
                    OutputStream out = mProcess.getOutputStream();
                    byte[] buffer = new byte[1024];
                    int count;

                    // in -> buffer -> mPOut -> mReader -> 1 line of ping information to parse
                    while ((count = in.read(buffer)) != -1) {
                        mPOut.write(buffer, 0, count);

                        while (mReader.ready()) {
                            aLine = mReader.readLine();
                            publishProgress(aLine);
                        }//endWhile

                        if (isCancelled()) {
                            stop();
                            break;
                        }//endIf

                    }//endWhile

                    out.close();
                    in.close();
                    mPOut.close();
                    mPIn.close();
                } finally {
                    stop();
                }//endTry
            } catch (IOException e) {
            }//endTry

            return null;
        }//endFct

        @Override
        protected void onProgressUpdate(String... aPingLine) {
            if (mCallbacks != null) {
                mCallbacks.onProgressUpdate(aPingLine[0]);
            }//endIf
        }//endFct

        public void stop() {
            if (mProcess != null) {
                mProcess.destroy();
                mProcess = null;
            }//endIf

            cancel(true);
        }//endStop
    }
}
RapazP
  • 101
  • 3
  • Rather use onDestroy() in stead of onDestroyView() – the-ginger-geek Nov 04 '13 at 10:01
  • Using PingFragment.onDestroy() work better than PingFragment.onDestroyView() but sometime the process continue in background. Actually, I don't have any certitude that the process is killed – RapazP Nov 04 '13 at 11:06
  • Have a look at this. Maybe this works better for you http://stackoverflow.com/questions/11359604/stop-asynctask-in-fragments-when-back-button-is-pressed – the-ginger-geek Nov 04 '13 at 11:18
  • By using PingFragment.onStop(), the thread is killed during screen rotation and screen lock. In my case this is not the expected operation. Moreover, I don't know if that the same with Fragment, but the Activity documentation about onDestroy() say that: 'There are situations where the system will simply kill the activity's hosting process without calling this method'. That can explain why my process is not killed every time. – RapazP Nov 04 '13 at 13:05
  • I think that when the hosting process is killed all other threads for the app will also be destroyed. I see you call super.onDetach in your onDestroy. Don't think that is very good practice, change that to super.onDestroy(); – the-ginger-geek Nov 04 '13 at 16:13
  • Oh sorry! The call of super.onDestroy() is a copy/paste mistake. Nevertheless the command onDestroy is not called on every application exit and the thread continue to run – RapazP Nov 04 '13 at 20:12

3 Answers3

0

you have to stopPing when the activity is destroyed .

http://developer.android.com/reference/android/app/Activity.html

you have also to do

mTask=null ;
  • no because if I call stopPing in the activity onDestroy, the thread is killed when I do a screen rotation. But I've added the mTask=null inside the PingFragment.stopPing() method. – RapazP Nov 04 '13 at 10:34
0

Please override the method onPostExecute in PingTask

Balaji Dhanasekar
  • 1,066
  • 8
  • 18
  • Unfortunately, this method is not called when I kill the application with "Recents Apps" menu – RapazP Nov 04 '13 at 10:46
0

check this link, it may helpful to you,

http://developer.android.com/reference/android/os/AsyncTask.html#isCancelled%28%29

check that method and write this in your code:

if( isCancelled ()){
break;
}
balaji koduri
  • 1,321
  • 9
  • 25