后台默默付出的劳动者,四大组件之服务Service

  • 前言
  • 十、后台默默付出的劳动者,四大组件之服务(Service)
  • 10.1 服务是啥?
  • 10.2 Android异步消息处理机制
  • 10.2.1 Android异步消息处理机制介绍
  • 10.2.2 基于Android异步消息处理机制实现“子线程中更新UI操作”
  • 10.3 Android子线程如何切换到主线程?AsyncTask横空出世!
  • 10.4 服务的基本用法
  • 10.4.1 自定义服务类继承自Service
  • 10.4.2 在AndroidManifest.xml中配置服务
  • 10.4.3 在活动中启动和停止服务
  • 10.4.4 在活动中操作服务的方法
  • 10.5 服务的生命周期
  • 10.6 有时候服务露个面才能让人心安,前台服务的使用方法
  • 10.7 服务不占用主线程,IntentService用法
  • 参考书籍:第一行代码


前言

本文讲解Android异步消息处理机制,同时讲解Android四大组件中的最后一种组件–服务,讲解服务的基本使用方法及生命周期,最后讲解一种占用主线程的服务IntentService。

十、后台默默付出的劳动者,四大组件之服务(Service)

10.1 服务是啥?

服务是Android中实现程序后台运行的解决方案,简单点说服务就是处理那些不用露面,但又要一直在运行的任务。

注意:
服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程,并且服务默认运行在主线程中。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。

10.2 Android异步消息处理机制

10.2.1 Android异步消息处理机制介绍

Android异步消息处理机制,简单来说就是一个线程处理着任务,处理着任务,忽然就把一个任务丢给另一个线程去处理了,而自己直接继续走,也不管丢的那个任务是否完成。

Android异步消息处理机制流程如下图所示,主要由4个部分组成:Handler、Message、MessageQueue、Looper。Handler发送Message到MessgeQueue中,Looper一直无休无止的在MessageQueue中取Message,并将取到的Message传递到Handler的handleMessage方法中处理。

因为Handler可以在A线程中实例化,而发送消息的操作可以在B线程中执行,继而就可以实现将B线程的任务发送到A线程进行处理。

后端开发四层架构是什么 后端四大件_异步消息处理机制

10.2.2 基于Android异步消息处理机制实现“子线程中更新UI操作”

我们知道Android中是不能再子线程中操作UI控件的,必须在主线程中操作UI,那么如何实现在子线程中更新UI呢?我们就可以利用异步消息处理机制,子线程处理任务,处理完要更新UI内容,这时我们可以把更新UI内容的部分再丢给主线程去做就可以了,本质还是在主线程中更新的UI。

核心代码实现如下:

public static final int UPDATE_TEXT=1;//要发送的Message内容

    TextView text;//要更新内容的UI控件

    @SuppressLint("HandlerLeak")
    //主线程实例化Handler
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what)
            {
                case UPDATE_TEXT:
                    text.setText("你也好呀");
                    break;
                default:
                    break;
            }
        }
    };
public class MainActivity extends AppCompatActivity {
    public static final int UPDATE_TEXT=1;//要发送的Message内容

    TextView text;//要更新内容的UI控件

    @SuppressLint("HandlerLeak")
    //主线程中实例化Handle
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            //主线程根据Message中的what值,判断做什么操作
            switch (msg.what)
            {
                case UPDATE_TEXT:
                    text.setText("你也好呀");//更新UI控件内容
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        text=findViewById(R.id.text);
        //开启子线程处理任务
        new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //处理一堆任务
                        //......
                        //封住Message
                        Message message = new Message();
                        message.what=UPDATE_TEXT;
                        //子线程处理完任务,发送Message到Hadler的handleMessage方法
                        handler.sendMessage(message);
                    }
                }).start();
    }
}

10.3 Android子线程如何切换到主线程?AsyncTask横空出世!

Android为了方便处理这种子线程切换到主线程的情况,又设计了一个AsyncTask类,该类封装了异步消息处理机制的详细细节,使得用户在不清楚异步消息处理机制的情况下也能轻松实现子线程到主线程的切换。

AsyncTask是一个抽象类,开发者需要定义一个类继承AsyncTask,并且指定AsyncTask的三个泛型类型,然后重写四个主要的方法即可。

  1. onPreExecute方法在任务开始前调用,一般进行一些界面的初始化操作者,比如显示进度条
  2. doInBackground方法在任务开始后调用,该方法中的所有代码都在子线程中运行,因此一般耗时的任务都在这处理
  3. onProgressUpdate方法在publishProgress方法被调用后,就会执行这个方法.publishProgress方法一般在doInBackground中调用,多用于更新进度条
  4. onPostExecute方法在doInBackground任务执行完,会调用该方法,处理一些收尾的任务

AsyncTask使用示例代码框架

package com.xiaomi.androidthreadtest;

import android.os.AsyncTask;

// 三个泛型解释:
// 泛型1:代表执行AsyncTask时需要传入的参数;
// 泛型2:代表任务执行进度的单位类型
// 泛型3:代表任务执行完成后的返回类型
public class MyAsyncTask  extends AsyncTask<Void,Integer,Boolean> {

    @Override
    //任务开始前调用该方法,一般进行一些界面的初始化操作者,比如显示进度条
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    //任务开始后调用,该方法中的所有代码都在子线程中运行,因此一般耗时的任务都在这处理
    //参数类型由泛型1决定
    protected Boolean doInBackground(Void... voids) {
        try {
            while (true){
                int downloadPercent=0;
                //做一些耗时的任务
                downloadPercent=doDownlaod();
                //调用该方法publishProgress,让onProgressUpdate去更新进度条
                publishProgress(downloadPercent);
                if (downloadPercent>=100)
                    break;
            }
            
        }catch(Exception e) 
        {
            return false;
        }
        return true;
    }

    @Override
    //publishProgress方法被调用后,就会执行这个方法.publishProgress方法一般在doInBackground中调用,多用于更新进度条
    //参数类型由泛型2决定
    protected void onProgressUpdate(Integer... values) {
        //更新进度条
        //....
    }

    @Override
    //当doInBackground任务执行完,会调用该方法,处理一些收尾的任务
    //参数类型由泛型3决定
    protected void onPostExecute(Boolean aBoolean) {
        //处理一些收尾工作,如关闭进度条
        //......
    }
}

10.4 服务的基本用法

10.4.1 自定义服务类继承自Service

public class MyService1 extends Service {

    //服务第一次启动时调用该方法
    @Override
    public void onCreate() {
        super.onCreate();
    }

    //服务每次启动都会调用该方法
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
 
    //绑定服务,可以使得该服务与活动进行交互
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    //停止服务后会调用此方法
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

10.4.2 在AndroidManifest.xml中配置服务

至此Android的四大组件:活动、广播机制,内容提供器,服务我们都接触到了,它们有一个共性,那就是必须要在AndroidManifest.xml文件中配置。

服务配置方法,是在application标签下添加service子标签,示例配置如下

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.xiaomi.servicetest">

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

</manifest>

10.4.3 在活动中启动和停止服务

  1. 启动服务核心代码
Intent intent = new Intent(this, MyService.class);
startService(intent);
  1. 停止服务核心代码
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);

10.4.4 在活动中操作服务的方法

  1. 自定义一个类继承自Binder
    示例代码如下:
class DownloadBinder extends Binder
    {
        public void startDownload()
        {
            Log.d(TAG, "startDownload: executed");
        }

        public int getProgress()
        {
            Log.d(TAG, "getProgress: executed");
            return 0;
        }
    }
  1. 在自定义服务类的OnBind中返回自动Binder对象
    示例代码如下
@Override
public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBinder;
    }
  1. 在活动中实现ServiceConnection匿名类,在该类中获取服务中绑定的对象,通过获得的绑定对象,就可以对服务中的方法进行操作。
    示例代码如下:
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取服务中绑定的对象
            downloadBinder= (MyService.DownloadBinder) service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
  1. 绑定服务的示例代码
Intent bindIntent =new Intent(MainActivity.this,MyService.class);
bindService(bindIntent,connection,BIND_AUTO_CREATE);
  1. 解绑服务的示例代码
unbindService(connection);

10.5 服务的生命周期

服务和活动及碎片一样,也有自己的生命周期,服务的生命周期内可能回调的方法包括我们上面提到的onCreate,onStartCommand,onBind,onDestory等等。

  • 服务经典生命周期情况一:Context.startService->[onCreate,服务首次创建的时候调用]->onStartCommand->Context.stopService/stopSelf->onDestory
  • 服务经典生命周期情况二:Context.bindService->[onCreate,服务首次创建的时候调用]->onBind->Context.unbindService->onDestory

注意:
若对一个服务既调用了Context.startService和Context.bindService,如何才能销毁这个服务呢?此时我们必须对应的调用Context.stopService和Context.unbindService后才能销毁这个服务。

10.6 有时候服务露个面才能让人心安,前台服务的使用方法

服务一直在后台默默的干活,但是服务的系统优先级比较低,当系统出现内存不足的时候,就有可能回收正在运行的服务。那么如果需要一个服务一直保持运行,或者想要在系统的状态栏看到正在运行的服务,就可以使用前台服务

前台服务和普通服务最大的区别就在于,它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。

普通服务如何转为前台服务呢?实现方法非常简单,只需要在服务的onCreate方法中调用startForeground方法即可,该方法需要传入一个非0的标识id,和一个通知Notification对象。

示例代码如下:

public class MyService extends Service {

    //......
   
    
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: executed");

        //将服务变为前台服务
        //1. 构建通知对象
        String CHANNEL_ONE_ID = "CHANNEL_ONE_ID";
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent= PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
        Notification notification= new Notification.Builder(this, CHANNEL_ONE_ID)
                .setChannelId(CHANNEL_ONE_ID)
                .setTicker("Nature")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("这是一条前台服务标题")
                .setContentIntent(pendingIntent)
                .setContentText("这是一条前台服务内容")
                .build();
        notification.flags|= Notification.FLAG_NO_CLEAR;
        //2. 调用startForeground方法
        startForeground(1, notification);
    }

   //......
}

10.7 服务不占用主线程,IntentService用法

我们知道服务默认运行在主线程中,因此,如果服务处理的一些耗时的逻辑,就会出现ANR(Application Not Responding)的情况。

为了避免ANR,我们可以在服务的具体方法中开子线程去处理耗时的逻辑,示例代码如下:

public class MyService1 extends Service {

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                //处理逻辑
                //...
                //处理完成,主动停止服务
                stopSelf();
            }
        }).start();

        return super.onStartCommand(intent,flags,startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

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

Android为了更方便开发者,在子线程中运行服务的耗时逻辑,也提供了一个IntentService类,示例代码如下:

//该服务中的onHandleIntent在子线程中运行,并且任务完成会自动停止服务
public class MyIntentService extends IntentService {
    private static final String TAG = "MyIntentService";
    
    public MyIntentService() {
        super(TAG);
    }
    //服务启动后,会执行该方法,该方法在子线程中运行,并且执行完成会自动停止服务
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.d(TAG, "MyIntentService Thread id is "+ Thread.currentThread().getId());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: executed");
    }
}

参考书籍:第一行代码

链接:https://pan.baidu.com/s/1aXtOQCXL6qzxEFLBlqXs1Q?pwd=n5ag
提取码:n5ag