两种方法:
第一种:
代码语言:javascript复制// 5000ms后执行run方法
// 可以在这run()里面更新ui
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
tv.setText("呵呵呵呵呵");
Log.d(TAG, "5s后我执行了");
}
}, 5000);
上面效果就是5s后将TextView控件内容改为“呵呵呵呵呵”
第二种:
代码语言:javascript复制timer = new Timer();
// 不能在这run()里面更新ui,除非使用runOnUiThread方法
timerTask = new TimerTask() {
@Override
public void run() {
tv.setText("哈哈哈哈哈");
Log.d(TAG, "任务执行");
}
};
timer.schedule(timerTask, 2000, 2000);
timer.schedule的第三个参数是间隔多久重复一次,可以不设置,做一次性的任务。
如果设置第三个参数就要记得在OnDestroy取消,不然activity销毁后定时任务仍然存在。
如果在这里的run方法更新ui就需要使用runOnUiThread()方法。
下面效果是每隔2s将TextView控件内容设置为“哈哈哈哈哈”
代码语言:javascript复制timer = new Timer();
// 不能在这run()里面更新ui,除非使用runOnUiThread方法
timerTask = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("哈哈哈哈哈");
}
});
Log.d(TAG, "任务执行");
}
};
timer.schedule(timerTask, 2000, 2000);
否则抛异常android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its view
Android中相关的view和控件操作都不是线程安全的,所以Android才会禁止在非UI线程更新UI,对于显式的非法操作,比如说直接在Activity里创建子线程,然后直接在子线程中操作UI等,Android会直接异常退出,并提示should run on UIThread之类的错误日志信息。而对于隐式的非法操作,App不会直接简单粗暴地异常退出,只是出现奇怪的结果,Only the original thread that created a view hierarchy can touch its views便是一个例子,字面意思是只有创建视图层次结构的原始线程才能操作它的View,明显是线程安全相关的。s.说明在错误的线程更新UI。
总结点:
不能在主线程(UI线程)进行耗时的操作,比如连接网络,拷贝大数据,睡眠等操作。
比如连接谷歌网络。warning:java.net.SocketTimeoutException: connect timed out
只要主线程超时 info:The application may be doing too much work on its main thread.
在4.0之后谷歌强制要求连接网络不能在主线程进行访问
只有主线程(UI线程)才可以更新UI
定时代码如下
MainActivity .java
代码语言:javascript复制import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends AppCompatActivity {
final String TAG = "MainActivity";
private Timer timer;
private TimerTask timerTask;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
// 5000ms后执行run方法
// 可以在这run()里面更新ui
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
tv.setText("呵呵呵呵呵");
Log.d(TAG, "5s后我执行了");
}
}, 5000);
/*timer = new Timer();
// 不能在这run()里面更新ui,除非使用runOnUiThread方法
timerTask = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("哈哈哈哈哈");
}
});
Log.d(TAG, "任务执行");
}
};
timer.schedule(timerTask, 2000, 2000);*/
}
@Override
protected void onDestroy() {
super.onDestroy();
// 终止此计时器,丢弃任何当前计划的任务。 不干扰当前执行的任务(如果存在)。
// 比如说定时器重复10个任务,cancel调用,我正在执行的任务就是最后一个任务,剩下的9个任务我不做了。
// 一旦计时器被终止,它的执行线程就会顺利地终止,并且不会再安排任务了。
timer.cancel();// 如果是第二种定时方法需要取消定时器
// 如果此方法发生时任务正在运行,则任务将运行到完成,但不会再运行。
// 也就是重复任务取消,最后任务表中的所有任务你就善始善终做完吧,可能还需要做几个任务这一轮才结束
// 从重复定时器任务的run方法中调用此方法绝对保证计时器任务不会再次运行。
// timerTask.cancel();
Log.d(TAG, "onDestroy: ");
}
}