Подтвердить что ты не робот

Остановка/уничтожение нити

У меня есть Служба, которая запускает Thread и Runnable, как это.

t = new Thread(new Runnable() {
    public void run() {
        doSomething();
    }
});

t.start();

Причиной потока является выполнение задачи Async doSomething(). Пока давайте не будем беспокоиться о другом классе AsyncTask. Я пробовал это, и это не работает для моего дела. Изменить: я не могу использовать AsyncTask, потому что он предназначен только для потока пользовательского интерфейса. Этот кусок кода должен работать внутри Сервиса, поэтому нет, нет AsyncTask: (

doSomething() содержит некоторые внешние библиотеки, поэтому проблема, с которой я сталкиваюсь, заключается в том, что ее можно потенциально повесить на одну из команд, без возврата какого-либо значения (следовательно, проверка ошибок даже не может быть выполнена)

Чтобы обойти это, я захочу, в какой-то момент, уничтожить Службу.

stopService(new Intent("net.MyService.intent));

Это прекрасно работает и легко проверяется по телефону. Тем не менее, Thread, который был создан выше, будет продолжать работать, даже когда Служба, которая породила его, будет уничтожена.

Поэтому я ищу правильные команды для вставки в Сервис onDestroy(), который очистит Thread для меня.

t.destroy();
t.stop();

обе амортизируются и вызывают сбои приложений.

Я взял этот код откуда-то

@Override
public void onDestroy() {

    Thread th = t;
    t = null;

    th.interrupt();

    super.onDestroy();
}

но он все равно не работает, поток продолжает работать. Любая помощь ребятам?

4b9b3361

Ответ 1

Методы потока destroy и stop по своей сути являются тупиковыми и небезопасными. Их существование также дает иллюзию, что может быть какой-то способ немедленно остановить другой поток, когда что-то еще сообщает ему.

Я понимаю ваше мышление, с вашей точки зрения, это один основной поток, и когда этот поток не получил ответа от рабочего потока, вы хотите его убить и перезапустить, не заботясь о том, что это до. Но причина, по которой эти методы являются устаревшими, заключается в том, что вам следует заботиться о том, что такое поток. Много.

Что делать, если поток имеет блокировку вокруг переменной, которую нужно использовать позже? Что делать, если поток имеет дескриптор файла? Во всех этих случаях и многих других, просто останавливая поток в нем, текущая операция оставит все в беспорядке - скорее всего, ваше приложение просто сработает дальше по линии.

Итак, для того, чтобы поток был прерываемым или отменяемым или остановленным, он должен сам управлять этим потоком. Если нить или операция не позволяют прервать себя, то вы не можете ее прервать - предполагается, что это будет небезопасно.

Если вы runnable буквально

public void run() {
   doSomething();
}

тогда нет способа прервать его. Можно было бы надеяться, что если doSomething была длинной операцией, возможно, существует способ взаимодействовать с ней пошагово с чем-то вроде

public void run() {
   while (running) {
       MyParser.parseNext();
   }
}

или иметь возможность передавать переменную по ссылке, которая указывает, прерывается ли поток или нет, и, надеюсь, метод будет прервать себя в подходящем месте.

Помните, что операция blocking блокируется. Невозможно обойти это, вы не можете отменить его частично.

Ответ 3

почему бы вам не использовать AsyncTask?

Задача может быть отменена в любое время invoke cancel (boolean). Вызов этот метод приведет к последующему вызывает isCancelled(), чтобы вернуть true. После вызова этого метода, onCancelled (Object), вместо onPostExecute (Object) будет вызываться после doInBackground (Object []) возвращается. Чтобы обеспечить выполнение задачи отменить как можно быстрее, вы всегда следует проверять возвращаемое значение of isCancelled() периодически из doInBackground (Object []), если возможно (например, внутри цикла.)

Ответ 4

Альтернативный ответ

Используйте следующий код:

MyThread thread;     // class field

Создайте и запустите поток, как вы делаете это прямо сейчас.

thread = new MyThread();
thread.start();

Когда служба уничтожается, "сигнализируйте" поток для выхода

public void onDestroy() {
    // Stop the thread
    thread.abort = true;
    thread.interrupt();
}

Вот реализация потока

//another class or maybe an inner class
class MyThread extends Thread {
    syncronized boolean abort = false;

    //ugly, I know
    public void run() {
       try {
           if(!abort) doA();
           if(!abort) doB();
           if(!abort) doC();
           if(!abort) doD();
       } catch (InterruptedException ex) {
          Log.w("tag", "Interrupted!");
       }
    }
}

Возможно, вы захотите прочитать следующее:

Я думаю, что вы могли бы полагаться на то, чтобы поймать исключение и не проверить прерывание, но я решил сохранить его таким образом.

UPDATE

Я видел этот образец в codeguru:

public class Worker implements Runnable {
    private String result;
    public run() {
        result = blockingMethodCall();
    }
    public String getResult() {
        return result;
    }
}

public class MainProgram {
    public void mainMethod() {
        ...
        Worker worker = new Worker(); 
        Thread thread = new Thread(worker); 
        thread.start();
        // Returns when finished executing, or after maximum TIME_OUT time
        thread.join(TIME_OUT); 
        if (thread.isAlive()) {
            // If the thread is still alive, it still blocking on the methodcall, try stopping it
            thread.interrupt();
            return null;
        } else {
            // The thread is finished, get the result
            return worker.getResult(); 
        }
    }
}

Ответ 5

Мне нравится использовать следующий подход:

class MyHandler extends Handler {
    final Semaphore stopEvent = new Semaphore(0);

    @Override
    public void handleMessage(Message msg) {
        try {
            while (!stopEvent.tryAcquire(0, TimeUnit.SECONDS)) {
                doSomething();

                if (stopEvent.tryAcquire(SLEEP_TIME, TimeUnit.MILLISECONDS)) {
                    break;
                }
            }
        } catch (InterruptedException ignored) {
        }

        stopSelf();
    }
}

В службе onDestroy просто отпустите stopEvent:

@Override
public void onDestroy() {
    myHandler.stopEvent.release();
    myHandler = null;

    super.onDestroy();
}

Ответ 6

Лучше использовать глобальную переменную stopThread, остановите поток после изменения переменной в true.

btnStop.setOnClickListener(new OnClickListener() {          
        @Override
        public void onClick(View arg0){
            stopThread = true;
        }
    }); 


public void run() {
        while (!stopThread) {
           //do something
        }
}

Ответ 7

Я думаю, что лучший способ создать и установить связь с другим потоком - использовать AsyncTask. Вот пример:

public class Task extends AsyncTask<Void, Void, Void> {

    private static final String TAG = "Task";

    private boolean mPaused;

    private Runnable mRunnable;

    public Task(Runnable runnable) {
        mRunnable = runnable;
        play();
    }

    @Override
    protected Void doInBackground(Void... params) {
        while (!isCancelled()) {
            if (!mPaused) {

                mRunnable.run();
                sleep();
            }
        }
        return null;
    }

    private void sleep() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            Log.w(TAG, e.getMessage());
        }
    }

    public void play() {
        mPaused = false;
    }

    public void pause() {
        mPaused = true;
    }

    public void stop() {
        pause();
        cancel(true);
    }

    public boolean isPaused() {
        return mPaused;
    }

}

Теперь вы можете легко использовать этот класс и начать поток, написав:

Task task = new Task(myRunnable);
task.execute((Void) null);

Кроме того, вы можете легко приостановить или остановить поток из цикла:

Пример приостановки и воспроизведения потока:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (task.isPaused()) {
            task.play();
        } else {
            task.pause();
        }
    }
});

Пример остановки и запуска потока:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (task.isCancelled()) {
            task = new Task(myRunnable);
            task.execute((Void) null);
        } else {
            task.stop();
        }
    }
});