安卓学习笔记1——使用服务实现计时器

计时器

根据李先生的要求,我们需要通过服务来实现一个计时器,但是没给具体要求,全靠一手自学,那么设计一个简单功能,即开始计时和停止计时,同时显示相应的时间,具体可能用到服务组件以及通过绑定服务的方式来完成与Service的交互,下面看具体实现。

XML布局

只用到一个文本控件(用来显示计时)和两个按钮控件(分别用来开始和结束计时),下面是布局文件:
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="160dp"
        android:text="Hello Timer!"
        android:textColor="@color/colorBlack"
        android:textSize="36sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="InvalidId" />

    <Button
        android:id="@+id/btstar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开始计时"
        android:layout_marginTop="53dp"
        app:layout_constraintTop_toBottomOf="@+id/tv"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        ></Button>

    <Button
        android:id="@+id/btend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="结束计时"
        android:layout_marginTop="53dp"
        app:layout_constraintTop_toBottomOf="@+id/btstar"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        ></Button>

</androidx.constraintlayout.widget.ConstraintLayout>

整体大概就是下面这个样子,这部分就不过多说明

布局预览

AndroidMainfest

使用服务一定要记得配置!配置!配置!不然就可能出现写完代码结果没得到预期效果并且百思不得其解的笨比行为
在application节点中添加service节点
服务节点

Service

在服务中要创建Binder对象以便调用,注意设置相应的onBind和onUnbind方法,在onStartCommand方法(也可以自定义一个方法)中new一个线程来计时,也就是简单的sleep操作,同时设置一个标志位threadDisable来停止循环,代码如下

countservice.java

package com.artoria.timecount;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

public class countservice extends Service {

   private volatile boolean threadDisable;
   Thread timeThread = new Thread();
   public static   int count;
    private LocalBinder binder = new LocalBinder();
    /** * 创建Binder对象,返回给客户端即Activity使用,提供数据交换的接口 */
    public class LocalBinder extends Binder {
        // 声明一个方法,getService。(提供给客户端调用)
        countservice getService() {
            // 返回当前对象LocalService,这样我们就可在客户端端调用Service的公共方法了
            return countservice.this;
        }
    }
    @Override

    public IBinder onBind(Intent intent) {

        return this.binder;

    }
    /** * 解除绑定时调用 * @return */
    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }


    public int onStartCommand(Intent intent, int flags, int startId) {
        // Let it continue running until it is stopped.
        Toast.makeText(this, "开始计时", Toast.LENGTH_LONG).show();
        count = 0;
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!threadDisable){

                    try{
                        timeThread.sleep(10);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                        break;
                    }
                    count++;
                    Log.v("countservice","now:"+count);
                }
            }
        }).start();


        return START_STICKY;
    }
   
    @Override
    public void onDestroy() {
    
        this.threadDisable=true;
        
        super.onDestroy();

        Toast.makeText(this, "计时结束", Toast.LENGTH_LONG).show();
    }
    public int getCount(){
        return count;
    }
    public void clear(){
        count=0;
    }
}

MainActivity

在主活动中需要绑定服务,通过获得的IBinder对象获取Service引用,然后就可以获取Service中的数据和方法,监听两个按钮事件通过itente开始和结束服务,通过服务对象myService获取count值并同步更新UI,代码如下
MainActivity.java

package com.artoria.timecount;

import androidx.appcompat.app.AppCompatActivity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;


public class MainActivity extends AppCompatActivity {
    private countservice myService;
    private Timer time = new Timer();
    private ServiceConnection conn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Button star = (Button) findViewById(R.id.btstar);
        final Button end = (Button) findViewById(R.id.btend);
        final TextView tv = (TextView) findViewById(R.id.tv);
        final Intent t = new Intent(MainActivity.this, countservice.class);

        conn = new ServiceConnection() { /** * 与服务器端交互的接口方法 绑定服务的时候被回调,在这个方法获取绑定Service传递过来的IBinder对象, * 通过这个IBinder对象,实现宿主和Service的交互。 */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {// 获取Binder
            countservice.LocalBinder binder = (countservice.LocalBinder) service;// 获取服务对象
            myService = binder.getService();
        }
            /** * 当取消绑定的时候被回调。但正常情况下是不被调用的,它的调用时机是当Service服务被意外销毁时, * 例如内存的资源不足时这个方法才被自动调用。 */
            @Override
            public void onServiceDisconnected(ComponentName name) {
                myService=null;
            }
        };

        star.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                Log.v("service", "star");
                //myService.clear();
                bindService(t, conn, Service.BIND_AUTO_CREATE);
                startService(t);
                //myService.star();
                //myService.clear();
                TimerTask timerTask = new TimerTask() {
                    @Override
                    public void run() {

                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                tv.setText(showtime(myService.count));
                            }
                        });

                    }
                };
                if (time == null){
                    time = new Timer();
                }
                time.schedule(timerTask,0,10);
            }
        });
        end.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {

                unbindService(conn);

                stopService(t);
            }
        });
    }

    private String showtime(int t)
    {
        int s=(t/100)%60;
        int m=(t/6000)%60;
        int ms = t%100;
        return String.format(Locale.CHINA,"%02d : %02d : %02d",m,s,ms);
    }
}

效果预览

在虚拟机上的运行效果

计时结束

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐