Как показать и скрыть один вид из разных потоков в Java?

Я пишу свою первую Java-программу для Android, которая рисует некоторые 3D-функции (f(x,y)) с использованием OpenGL, и программа почти завершена, но у меня возникают проблемы с отображением и скрытием пользовательского индикатора выполнения, разработанного как LinearLayout (который я создал) с некоторым текстом и прогрессбаром. Мне нужно, чтобы сделать progressBarLL видимым до и во время расчета значений 3d-функций. Для этого я реализовал обработчик в Graph3dActivity:

public class Graph3dAct extends Activity {
    (...)
    static ProgressBar progressBarPB;
    static LinearLayout progressBarLL;
    public static Handler progressHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.e("HANDLER:",Integer.toString(msg.arg1));
            //toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 500);             
            switch (msg.arg1)
            {
            case 1: progressBarLL.setVisibility(LinearLayout.VISIBLE);
                    break;
            case 2: progressBarLL.setVisibility(LinearLayout.INVISIBLE);
                    break;
            case 3: progressBarPB.setProgress(msg.arg2);
                    //to be implemented
                    break;
            default: Log.e("Graph3dAct","Handler misused;");
            }
            progressBarLL.invalidate();
            progressBarPB.invalidate();           
      }
    };
    @Override
    public void onCreate(Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);
        (...)
        progressBarPB = (ProgressBar) findViewById(R.id.surface3dPB);
        progressBarLL = (LinearLayout) findViewById(R.id.progressLL);
        (...)
        final Button moreStepsB = (Button) findViewById(R.id.buttonMoreS);
        moreStepsB.setOnClickListener(new View.OnClickListener() {          
            @Override
            public void onClick(View v) {  
                int max=stepsVals.length-1;
                if (stepsIndex<max) 
                {
                    stepsIndex++;
                    steps=stepsVals[stepsIndex];
                    FuncSurf fs = new FuncSurf(steps);
                    renderer.funcSurf =  fs;

Затем у меня есть FuncSurf с его конструктором, выполняющим длительный расчет поверхности. Мне нужно показать мой progressBarLL перед расчетами:

public class FuncSurf {
    public FuncSurf(int steps_) {        
            Log.e("FuncSurf","CONSTRUCTOR");
            Message msg = new Message();
            msg.arg1=1; //instruct handler to show progress layout: progressBarLL
            Graph3dAct.progressHandler.sendMessage(msg);

            (...) HERE GOES LONG-LASTING CALCULATION        

            msg = new Message();
            msg.arg1=2; //instruct handler to hide progress layout: progressBarLL
            Graph3dAct.progressHandler.sendMessage(msg);    
    }

Проблема состоит в том, что когда я вызываю конструктор функции FuncSurf из события onClick, реализованного в вышеупомянутом Activity, то это приводит к отображению progressBarLL в течение очень короткого времени и, к сожалению, после выполнения расчета, а не до этого. Другими словами, обработчик обрабатывает сообщения после того, как конструктор FuncSurf завершается не одновременно. Это наверное потому что это одна и та же тема? Но когда конструктор FuncSurf() вызывается из Renderer, все в порядке:

class MyOpenGLRenderer implements Renderer {
        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            funcSurf = new FuncSurf(steps); //progressBarLL shown OK

Мой вопрос заключается в том, как, возможно, аккуратно исправить код так, чтобы progressBarLL показывался перед вычислениями в конструкторе FuncSurf() независимо от того, где я вызываю этот конструктор? Я не могу напрямую использовать progressBarLL.setVisibility в funcSurf, потому что это не поток GUI, когда вызывается onSurfaceChanged(...) выше. Извините, я не смог объяснить проблему яснее. Я с нетерпением жду вашей помощи.

2 ответа

Использование AsyncTask, он разработан именно для этого случая (и принимает случай взаимодействия потоков без необходимости сообщений или чего-то в этом роде).

  • onPreExecute() работает в потоке пользовательского интерфейса, делает индикатор выполнения видимым.
  • doInBackground() выполняет длинный расчет в фоновом потоке. Регулярные звонки publishProgress() уведомить о прогрессе.
  • onProgressUpdate() работает в потоке пользовательского интерфейса и продвигает индикатор выполнения.
  • onPostExecute() запускается в потоке пользовательского интерфейса и делает индикатор выполнения невидимым.

См. Документацию " Поддержание адаптивности вашего приложения", в которой приведен аналогичный пример (в котором долгосрочное задание является сетевой операцией).

Тем временем я нашел "конструкцию нити", которая решает проблему на данный момент. Он принудительно выполняет "долгосрочную задачу" в отдельном потоке, позволяя потоку графического интерфейса продолжить и обновить графический интерфейс обработчиком. Небольшая модификация первого фрагмента кода, и программа теперь работает нормально:

Runnable run = new Runnable() 
{
    @Override
    public void run() {
        FuncSurf fs = new FuncSurf(steps);
        renderer.funcSurf =  fs;
    }
};       
Thread thr = new Thread(run);
thr.start();
Другие вопросы по тегам