在UI线程外运行的东西

时间:2013-12-05 14:22:46

标签: java android android-asynctask

我正在尝试创建一个简单的Android秒表应用程序。每次按下开始按钮时,我都无法冻结应用程序。我从在线阅读各种内容中了解到,它挂起的原因是我在UI线程中运行了一个while循环,以便应用程序不会崩溃,而while循环必须在某个不同的地方。 XDA论坛上的帖子表明遇到此问题的人应该使用AsyncTask来完成此任务。我无法准确理解如何使用AsyncTask来执行此操作。

TL; DR:我正在尝试计算时间,然后用相应的时间更新textview

在UI线程中使用while循环的原始代码

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity 
{
    Button start, stop, reset;
    TextView time;
    boolean timeStopped = false;
    long timeInNanoSeconds, startTimeInNanoSeconds;
    double timer;

    public double getTimeInSeconds()
    {
        timeInNanoSeconds = System.nanoTime() - startTimeInNanoSeconds;
        double timeSeconds = (double) timeInNanoSeconds / 1000000000.0;
        double roundOff = Math.round(timeSeconds * 100.0) / 100.0;
        return roundOff;
    }

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

        start = (Button) findViewById(R.id.startButton);
        stop = (Button) findViewById(R.id.stopButton);
        reset = (Button) findViewById(R.id.resetButton);
        time = (TextView) findViewById(R.id.timeField);

        start.setOnClickListener(new View.OnClickListener() 
        {
            public void onClick(View arg0) 
            {
                startTimeInNanoSeconds = System.nanoTime();
                while(timeStopped == false)
                {
                    double timer = getTimeInSeconds();
                    String stringTimer = Double.toString(timer);
                    CharSequence sequenceTimer = stringTimer;
                    time.setText(sequenceTimer);
                }


            }
        });

        stop.setOnClickListener(new View.OnClickListener() 
        {
            public void onClick(View v) 
            {

            }
        });

        reset.setOnClickListener(new View.OnClickListener() 
        {   
            public void onClick(View v) 
            {
                time.setText("");

            }
        });

    }

    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
}

编辑:使用处理程序的工作版

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

    Button start, stop, reset;
    TextView time;
    Handler m_handler;
    Runnable m_handlerTask;
    int timeleft = 0;
    boolean timeStopped;

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

        start = (Button) findViewById(R.id.buttonStart);
        stop = (Button) findViewById(R.id.buttonStop);
        reset = (Button) findViewById(R.id.buttonReset);
        time = (TextView) findViewById(R.id.textTime);

        start.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                timeStopped = false;
                m_handler = new Handler();
                m_handlerTask = new Runnable()
                {
                    public void run() {
                        if(timeStopped == false){
                            if(timeleft > -1) {
                                Log.i("timeleft","" + timeleft);
                                time.setText(String.valueOf(timeleft));
                                timeleft++;
                            }
                            else{
                                m_handler.removeCallbacks(m_handlerTask);
                            }
                        }

                        m_handler.postDelayed(m_handlerTask, 1000);
                    }

                };

                m_handlerTask.run();
            }
        });

        stop.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                timeStopped = true;
                m_handler.removeCallbacks(m_handlerTask);
            }
        });

        reset.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                timeStopped = true;
                m_handler.removeCallbacks(m_handlerTask);
                timeleft = 0;
                time.setText(String.valueOf(timeleft));
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

3 个答案:

答案 0 :(得分:2)

在后台线程上调用

doInbackground。你无法从后台更新ui

time.setText(sequenceTimer);      
// should be in a ui thread.

runOnUithread中使用onPostExecute或setText。

您可以根据需要使用处理程序,计时器任务或CountDowntimer。

Android Thread for a timer

编辑:

使用处理程序

       public class MainActivity extends Activity 
        {
            Button start;
            TextView time;
            Handler m_handler;
            Runnable m_handlerTask ; 
            int timeleft=100;

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

                start = (Button) findViewById(R.id.button1);
                time = (TextView)findViewById(R.id.textView1);

                start.setOnClickListener(new View.OnClickListener() 
                {
                    public void onClick(View arg0) 
                    {

                        m_handler = new Handler(); 
                        m_handlerTask = new Runnable() 
                        { 
                        @Override
                        public void run() {
                        if(timeleft>=0)
                        {  
                             // do stuff
                             Log.i("timeleft",""+timeleft);
                             time.setText(String.valueOf(timeleft));
                             timeleft--; 

                        }      
                        else
                        {
                          m_handler.removeCallbacks(m_handlerTask); // cancel run
                        } 
                          m_handler.postDelayed(m_handlerTask, 1000); 
                         }
                         };
                         m_handlerTask.run();   

                    }
                });

            }
 }

答案 1 :(得分:2)

在我看来,AsyncTask并不适合你,因为在我看来这是一次单枪行动。

我会建议这样的事情:

private ScheduledExecutorService exec;

private void startExec() {
    shutDownExec();
    exec = Executors.newSingleThreadScheduledExecutor();
    exec.scheduleWithFixedDelay(new Runnable() {

        @Override
        public void run() {
            // this starts immediately and is run once every minute

            double timer = getTimeInSeconds();
            String stringTimer = Double.toString(timer);
            CharSequence sequenceTimer = stringTimer;
            runOnUiThread(new UpdateUI(R.id.yourtime_textview, sequenceTimer));

        }
    }, 0, 1, TimeUnit.MINUTES); // adjust how often run() is executed here
}

private void shutDownExec() {
    if (exec != null && !exec.isTerminated()) {
        exec.shutdown();
    }
}

private class UpdateUI implements Runnable {

    private String mText;
    private TextView mTv;

    public UpdateUI(int textviewid, String text) {
        this.mTv = (TextView) findViewById(textviewid);
        this.mText = text;
    }

    @Override
    public void run() {
        mTv.setText(mText);
    }

}

答案 2 :(得分:1)

我最近不得不做一个类似的任务,我使用以下代码创建了一个单独的线程。它允许您按照我认为适合您的任务的设定时间间隔进行更新。

希望它有所帮助。

import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.content.Context;
import android.util.Log;


public class AutomationTreadClass {    

    Activity refToUIActivity;


    //Declare the timer
    Timer t = new Timer();

//pass UI activity so you can call update on it
    AutomationTreadClass( Activity callingActivity ){

        refToUIActivity =  callingActivity; 
        startTimerTread();

    }



private void startTimerTread(){ 

    //Set the schedule function and rate
            t.scheduleAtFixedRate(new TimerTask() {

                @Override
                public void run() {

                    //do any updates to the time you need to do here



                    updateLevelMeter();

                }
            },
            //Start Time of thread
            0,
            //interval of updates
            30);



}


private void updateLevelMeter() {         
        refToUIActivity.runOnUiThread(new Runnable() {
            public void run() {


                //access what ever UI comment you need to here. like giving you textview a value.     


            }
        });
    }


}