实验目的
熟悉和掌握Android线程的使用
实验要求
- 完成一个秒表,具备启停功能,正确使用工作线程完成界面刷新
- 分析秒表的计时是否准确,原因是什么,如何保证秒表计时准确?
实验过程
实验结果
实验代码
界面部分
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="秒表"
android:textAlignment="center"
android:textSize="40dp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="00:00:00"
android:id="@+id/tv_time"
android:textAlignment="center"
android:textSize="60dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Space
android:layout_width="120dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<Button
android:id="@+id/btn_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="清零"
android:textColor="#000000"
android:textSize="25dp"
android:background="#99FFFF"/>
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="计时"
android:layout_weight="1"
android:textSize="25dp"
android:background="#FFC8B4"
/>
<Button
android:id="@+id/btn_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止"
android:layout_weight="1"
android:textSize="25dp"
android:background="#CCFF99"
/>
</LinearLayout>
</LinearLayout>
核心代码
Timer.java
package com.example.administrator.exp5;
import android.app.Activity;
import android.content.Intent;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class Timer extends AppCompatActivity {
private static Handler handler = new Handler();
private static TextView tv_time = null;
private static Button btn_clear,btn_start,btn_stop;
private static String time;
private int min=0,sec=0,psec= 0;
boolean runflag = true;
int status = 0; // 0 - clear ; 1 - start ; 2 - stop
long last,templast = -1;
String str="";
private Thread workThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv_time = (TextView)findViewById(R.id.tv_time);
btn_clear = (Button)findViewById(R.id.btn_clear);
btn_start = (Button)findViewById(R.id.btn_start);
btn_stop = (Button)findViewById(R.id.btn_stop);
workThread = new Thread(null,RefreshLabel,"workThread");
workThread.start();
last = System.currentTimeMillis();
btn_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//计时开始
status = 1;
if(templast != -1)
{
last = templast;
}
}
});
btn_stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//停止计时
status = 2;
templast = last;
}
});
btn_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
min = sec = psec = 0;
//清零
status = 0;
last = System.currentTimeMillis();
tv_time.setText("00:00:00");
templast = -1;
}
});
}
private Runnable RefreshLabel = new Runnable() {
@Override
public void run() {
try{
while(runflag)
{
Thread.sleep(50);
if(status == 1)
{
int t=0;
long tmp = System.currentTimeMillis();
long cha = tmp - last;
t = (int)cha/1000;
psec = (int)cha - t * 1000;
psec %= 100;
sec = t - ((int)t/60)*60;
min = (int)t/60;
if(min == 60)
{
min = 0;
}
str = "";
str = str + String.format("%02d",min) + ":" +String.format("%02d",sec) + ":" + String.format("%02d",psec);
tv_time.post(new Runnable() {
@Override
public void run() {
tv_time.setText(str);
}
});
}
}
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
};
}
设计思路
秒表需要完成停启功能,也就是说需要能够停下来,而且在停下来以后需要能够继续计时。这个计时的工作需要交付给一个子进程来做,每隔一段时间就刷新一次界面,从而达到计时的效果。
如何使秒表计时更加准确?可以通过sleep(1000)来计时,或者采取不断访问系统时间的方式来计时。两种方式相比较,第二种方式更加准确,第一种解决方案由于sleep时间是人为设定的,跟手机的性能有一定关系,除此之外,由于程序间断性执行,这些时间也会计算到总时间中,所以第一种方案计时不准确。应该采用第二种计时方式。
实验总结
本实验主要是练习线程的使用,线程需要实现(Runnable)借口,并重载(run())函数,在该函数中写入秒表运行的代码,进行计算:
private Runnable RefreshLabel = new Runnable(){
@Override
public void run(){
try{
}
catch(InterruptedException e){
e.printStackTrace();
}
}
}
然后创建Thread对象,将Runnable对象作为参数传递给Thread对象。
private Thread workThread;
workThread = new Thread(null,RefreshLabel,"workThread")
需要的三个参数,第一个参数表示线程组,第二个参数是需要执行的Runnable对象,第三个参数是参数名称。
关于时间计算:
通过System.currentTimeMillis()
函数返回当前计算机的时间所对应的毫秒数。
时间换算如下:
1min = 60s
1s = 1000ms
时间计算部分代码:
long tmp = System.currentTimeMillis();
long cha = tmp - last;
t = (int)cha/1000;
psec = (int)cha - t * 1000;
psec %= 100;
sec = t - ((int)t/60)*60;
min = (int)t/60;
if(min == 60)
{
min = 0;
}
str = "";
str = str + String.format("%02d",min) + ":" +String.format("%02d",sec) + ":" + String.format("%02d",psec);
另外,由于单线程中,不能再UI线程外访问Android UI 工具包,则调用(View.post())方法,进行异步通信。实现界面的动态更新。