Android – код после Looper

У меня есть дочерний поток, выполняющий задачу бесконечно. Я хочу (1) постоянно отправлять данные обратно в поток пользовательского интерфейса и (2) время от времени отправлять данные (соответствующие кнопкам) в дочерний поток, чтобы приостановить/продолжить бесконечную задачу. Моя проблема в том, что дочерний поток застревает в цикле, что означает, что задача не выполняется.

У меня есть следующий вопрос: как заставить дочерний поток получать сообщения из потока пользовательского интерфейса, не блокируя бесконечную задачу?

Вот что у меня есть до сих пор: для задачи (1) у меня есть обработчик в моем потоке пользовательского интерфейса, который работает, и бесконечный цикл в дочернем потоке, который отправляет обратно сообщение, которое работает само по себе.

В потоке пользовательского интерфейса:

mMainHandler = new Handler() {
    public void handleMessage(Message msg) {
        Bundle b;
        b = msg.getData();
        if (msg.what==1)
            Log.i("main", "from child (running) - " + b.getBoolean("running"));
        else if (msg.what == 2)
            Log.i("main", "from child (count) - " + b.getInt("count"));
    }
};

В дочернем потоке (в настоящее время используется фиктивная задача, пока я не разработаю структуру):

while (true) {
    if (running) {
        try {
            curCount += up;
            if (curCount == maxCount)
                up = -1;
            else if (curCount == minCount)
                up = 1;
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Log.e("", "local Thread error", e);
        }

        Bundle b = new Bundle(1);
        b.putInt("count", curCount);
        Message toMain = mMainHandler.obtainMessage();
        toMain.what = 2;
        toMain.setData(b);
        mMainHandler.sendMessage(toMain);
    }
}

Для задачи (2) у меня есть метод в моем потоке пользовательского интерфейса, соответствующий нажатию кнопки, которое отправляет сообщение в дочерний поток, который работает, и обработчик в дочернем потоке, который работает сам по себе.

В потоке пользовательского интерфейса:

private void sendRunning(boolean running) {
    if (mChildHandler != null) {
        Bundle b = new Bundle(1);
        b.putBoolean("running", running);

        Message msg = mChildHandler.obtainMessage();
        msg.what = 1;
        msg.setData(b);
        mChildHandler.sendMessage(msg);
    }
}

В дочерней теме:

Looper.prepare();
mChildHandler = new Handler() {
    public void handleMessage(Message msg) {
        Bundle b;
        if (msg.what==1){
            b = msg.getData();
            running = b.getBoolean("running");
            Log.i(INNER_TAG, "from main (running) - " + b.getBoolean("running"));
            Log.i(INNER_TAG, "running - " + running);
            try {
                Message toMain = mMainHandler.obtainMessage();
                toMain.what = 1;
                toMain.setData(b);
                mMainHandler.sendMessage(toMain);
            } finally {}
        }
    }
};
Looper.loop();

Каждый из этих сценариев прекрасно работает сам по себе, но проблема в том, чтобы сделать оба одновременно. Если я поставлю бесконечную задачу после Looper.loop(), она никогда не будет достигнута. Если я поставлю его перед Looper.prepare(), он запустится один раз. Если я помещу его в петлитель, он все равно запустится только один раз.

Благодарим за любую идею :)

Вот мой полный код (без пакета/импорта) для справки:

public class MainActivity extends Activity {

Thread thread;
private Handler mMainHandler, mChildHandler;

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

    mMainHandler = new Handler() {
        public void handleMessage(Message msg) {
            Bundle b;
            b = msg.getData();
            if (msg.what==1)
                Log.i("main", "from child (running) - " + b.getBoolean("running"));
            else if (msg.what == 2)
                Log.i("main", "from child (count) - " + b.getInt("count"));
        }
    };

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

    // Get a reference to the button
    Button buttonStart = (Button)findViewById(R.id.btnStart);
    Button buttonStop = (Button)findViewById(R.id.btnStop);

    // Set the click listener to run my code
    buttonStart.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this,
                    "Starting...", Toast.LENGTH_SHORT).show();
            sendRunning(true);
        }
    });
    buttonStop.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this,
                    "Stopping...", Toast.LENGTH_SHORT).show();
            sendRunning(false);
        }
    });
}

private void sendRunning(boolean running) {
    if (mChildHandler != null) {
        Bundle b = new Bundle(1);
        b.putBoolean("running", running);

        Message msg = mChildHandler.obtainMessage();
        msg.what = 1;
        msg.setData(b);
        mChildHandler.sendMessage(msg);
    }
}

@Override
protected void onDestroy() {

    Log.i("tag", "stop looping the child thread's message queue");
    mChildHandler.getLooper().quit();

    super.onDestroy();

}

class ChildThread extends Thread {

    private static final String INNER_TAG = "ChildThread";
    private boolean running = true;     
    final int maxCount = 10;
    final int minCount = 0;
    public int curCount = minCount;
    private int up = 1;

    public void run() {

        while (true) {
            if (running) {
                try {
                    curCount += up;
                    if (curCount == maxCount)
                        up = -1;
                    else if (curCount == minCount)
                        up = 1;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Log.e("", "local Thread error", e);
                }

                Bundle b = new Bundle(1);
                b.putInt("count", curCount);
                Message toMain = mMainHandler.obtainMessage();
                toMain.what = 2;
                toMain.setData(b);
                mMainHandler.sendMessage(toMain);
            }

            this.setName("child");
            Looper.prepare();
            mChildHandler = new Handler() {
                public void handleMessage(Message msg) {
                    Bundle b;
                    if (msg.what==1){
                        b = msg.getData();
                        running = b.getBoolean("running");
                        Log.i(INNER_TAG, "from main (running) - " + b.getBoolean("running"));
                        Log.i(INNER_TAG, "running - " + running);
                        try {
                            Message toMain = mMainHandler.obtainMessage();
                            toMain.what = 1;
                            toMain.setData(b);
                            mMainHandler.sendMessage(toMain);
                        } finally {}
                    }
                }
            };

            Log.i(INNER_TAG, "Child handler is bound to - " +
                    mChildHandler.getLooper().getThread().getName());
            Looper.loop();
        }


    }
}

}


person Andy    schedule 16.10.2013    source источник
comment
примечание: я провел некоторое время, исследуя и пытаясь понять, как использовать потоки, и эта страница изложила это очень хорошо. На на этой странице показано, как использовать пакеты для обмена данными между потоками.   -  person Andy    schedule 16.10.2013
comment
если это вообще возможно, избегайте запуска потока на неопределенный срок...   -  person JoxTraex    schedule 16.10.2013


Ответы (2)


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

person Bhanu Sharma    schedule 16.10.2013

В итоге я просто использовал переменное время для потока, чтобы избежать ситуации. Спасибо за совет.

person Andy    schedule 16.10.2013