21 天好习惯第一期-1

文章目录

 


前言

今天学习了Service,来记录一下学习历程。


提示:以下是本篇文章正文内容,下面案例可供参考

一、startService

startService()生命周期(感兴趣的可以自己去验证一下,在每个重写方法中输出日志信息,然后运行,再在LogCat中查看日志信息):

 onCreate() --> onStartCommand() --> Servicerunning --> onDestroy() --> Service shut down

特别注意:其中onStartCommand()方法是Service已经在启动状态时,用户每次startService(intent)时就会调用onStartCommand()。还有就是startService()不能调用Service的方法,因为它不执行onBind()

首先来讲讲startService,看我运行效果

<iframe allowfullscreen="true" data-mediaembed="csdn" id="sIPVEtUz-1634973309733" src="https://live.csdn.net/v/embed/179083"></iframe>

startService

项目结构如下图所示

21 天好习惯第一期-1

MainActivity代码如下


import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_start;
    private Button btn_stop;
    private Intent intent;

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

        btn_start = findViewById(R.id.btn_start);
        btn_stop = findViewById(R.id.btn_stop);
        btn_start.setOnClickListener(this);
        btn_stop.setOnClickListener(this);

        intent = new Intent(MainActivity.this,MyService.class);
    }

    @Override
    public void onClick(View v) {
       switch (v.getId()){
           case R.id.btn_start:
               Log.i("TAG", "onClick: Service启动");
               startService(intent); //启动Service
               Toast.makeText(MainActivity.this, "Service启动成功", Toast.LENGTH_SHORT).show();
               break;
           case R.id.btn_stop:
               Log.i("TAG", "onClick: Service停止");
               stopService(intent); //停止Service
               Toast.makeText(MainActivity.this, "Service已经停止", Toast.LENGTH_SHORT).show();
               break;
       }
    }
}

 

MainActivity的布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="horizontal"
    android:gravity="center">


    <Button
        android:id="@+id/btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动Service" />

    <Button
        android:id="@+id/btn_stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止Service"
        android:layout_marginLeft="20dp"/>
</LinearLayout>

MyService代码如下

package com.example.mystartservice;

import android.app.ActivityManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

import java.util.ArrayList;

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 1;
                while (isRunning()){//如果Service正在运行
                    Log.i("Service", "run: "+i++);
                    try {
                        Thread.sleep(1500); //线程休眠1.5秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    //判断Service是否正在运行
    public boolean isRunning(){
        ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        //获取所有正在运行的Service
        ArrayList<ActivityManager.RunningServiceInfo> runningService = (ArrayList<ActivityManager.RunningServiceInfo>)
                activityManager.getRunningServices(20);
        for (int i = 0;i < runningService.size();i++){
            if(runningService.get(i).service.getClassName().toString().equals("com.example.mystartservice.MyService")){
                //判断当前Service是否正在运行
                return true;
            }
        }
        return false;
    }
}

二、bindService

 bindService()生命周期(感兴趣的可以自己去验证一下,在每个重写方法中输出日志信息,然后运行,再在LogCat中查看日志信息):

onCreate() --> onBind() --> Clients are bound to service --> onUnbind --> onDestroy() --> Service shut down

执行bindService()时,在这个时候调用者和Service就绑定在一起了。调用者调用unbindService方法或者调用者不存在了(比如Activity被销毁了),Service就会执行 onUnbind --> onDestroy。不难发现,调用者和Service相当于共存亡了。

特别注意:unBindService()只能调用一次,否则会造成ServiceConnection泄露。

我写了个bindService测试,看我运行效果

<iframe allowfullscreen="true" data-mediaembed="csdn" id="7hlnB2hm-1634974528603" src="https://live.csdn.net/v/embed/179085"></iframe>

bindService测试

 

MainActivity代码如下


import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    BinderService binderService; //声明Service类对象
    //文本框组件ID
    int[] tvid = {R.id.tv1,R.id.tv2,R.id.tv3,R.id.tv4,
            R.id.tv5,R.id.tv6,R.id.tv7,R.id.tv8,R.id.tv9};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn = findViewById(R.id.btn);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //单击抽奖按钮,随机获取一组号码
                List number = binderService.getRandomNumber();
                for (int i=0;i < number.size();i++){
                    TextView tv = (TextView) findViewById(tvid[i]); //获取文本框组件
                    tv.setText(number.get(i).toString()); //显示生成的随即号码
                }
            }
        });
    }

    //创建ServiceConnection对象
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            binderService = ((BinderService.MyBinder)service).getService(); //获取后台Service
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(MainActivity.this,BinderService.class);
        bindService(intent,connection,BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(connection); //解绑Service
    }
}

MainActivity的布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:gravity="center">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:text="00" />

        <TextView
            android:id="@+id/tv2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:text="00" />

        <TextView
            android:id="@+id/tv3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:text="00" />

        <TextView
            android:id="@+id/tv4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:text="00" />

        <TextView
            android:id="@+id/tv5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:text="00" />

        <TextView
            android:id="@+id/tv6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:text="00" />

        <TextView
            android:id="@+id/tv7"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:text="00" />

        <TextView
            android:id="@+id/tv8"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:text="00" />

        <TextView
            android:id="@+id/tv9"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00"
            app:layout_constraintTop_toTopOf="parent" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:layout_marginTop="30dp">

        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="抽奖" />
    </LinearLayout>

</LinearLayout>

BinderService代码如下


import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class BinderService extends Service {
    public BinderService(){
    }
    //创建MyBinder内部类
    public class MyBinder extends Binder{
        public BinderService getService(){//创建获取Service的方法
            return BinderService.this; //返回当前的Service类
        }
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder(); //返回MyBinder Service对象
    }

    //自定义方法,用于生成随机数
    public List getRandomNumber(){
        List resArr = new ArrayList();
        String strNumber = ""; //用于保存生成的随机数
        for(int i = 0;i < 9;i++){
            int number = new Random().nextInt(60)+1; //生成指定范围的随机整数
            if(number < 10){
                strNumber = "0"+String.valueOf(number);
            }else {
                strNumber = String.valueOf(number);
            }
            resArr.add(strNumber); //把转换后的字符串添加到List集合中
        }
        return resArr;
    }

    @Override
    public void onDestroy() { //销毁Service
        super.onDestroy();
    }
}

在BinderService类中可能有人会疑惑(包括我)为什么要写一个内部类MyBinder继承Binder?

 public class MyBinder extends Binder{
        public BinderService getService(){//创建获取Service的方法
            return BinderService.this; //返回当前的Service类
        }
    }

于是我点进Binder里查看源码,发现Binder是实现接口IBider的实现类

21 天好习惯第一期-1

然后再去看MainActivity里创建了一个ServiceConnection对象,并重写了两个方法,你会发现

onServiceConnted()的两个参数中有一个IBinder接口对象

//创建ServiceConnection对象
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            binderService = ((BinderService.MyBinder)service).getService(); //获取后台Service
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

这里为了能调用MyBinder类里的getService方法,进行了强转,这样一梳理是不是就理解为什么要在BinderService里写内部类MyBinder了,onBind回调方法将返回给MainActivity一个IBinder接口实例,IBinder允许MainActivity回调服务的方法,比如得到Service运行的状态或其他操作。我们需要IBinder对象返回具体的Service对象才能操作,所以说具体的Service对象必须首先实现Binder对象。


 

总结

遇到的问题:在实现startService时,我是用Android Studio自带的Service

21 天好习惯第一期-1

 所以它会自动在AndroidManifest.xml写如下代码

<service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>

在实现bindService时就是自定义了一个BinderService,所以就忘记在AndroidManifest.xml注册

报了一个错误

21 天好习惯第一期-1

 所以千万别忘了还要在AndroidManifest.xml写如下代码

 <service android:name=".BinderService"
            android:enabled="true"
            android:exported="true">
        </service>

粗略讲了一下startService和bindService,如果还有哪里不懂可以自行百度看大佬的博客也可以留言,如果上面我哪里写的不对可以指出,我也会及时改正,谢谢。

上一篇:软件架构设计原则之接口隔离原则


下一篇:接口(interface)