二、Service 执行耗时工作

发布时间:2024年01月02日

一、Service 开启和停止
二、Service 执行耗时工作


Service 执行耗时工作

工程代码

根据Service开启和关闭打印日志,可知,Activity中点击按钮函数和ServiceonCreateonStartCommandonDestroy都在同一进程和线程执行。所以,ServiceActivity一样,都在主线程运行

2024-01-01 08:44:29.672 11694-11694 com.ieenin...inActivity com.ieening.blogsservicedemo         D  click start service button to start service
2024-01-01 08:44:29.675 11694-11694 com.ieenin...topService com.ieening.blogsservicedemo         D  executing StartStopService constructor
2024-01-01 08:44:29.677 11694-11694 com.ieenin...topService com.ieening.blogsservicedemo         D  executing StartStopService onCreate method
2024-01-01 08:44:29.684 11694-11694 com.ieenin...topService com.ieening.blogsservicedemo         D  executing onStartCommand method
2024-01-01 08:44:40.759 11694-11694 com.ieenin...inActivity com.ieening.blogsservicedemo         D  click restart service button to restart service
2024-01-01 08:44:40.768 11694-11694 com.ieenin...topService com.ieening.blogsservicedemo         D  executing onStartCommand method
2024-01-01 08:44:47.630 11694-11694 com.ieenin...inActivity com.ieening.blogsservicedemo         D  click stop service button to stop service
2024-01-01 08:44:47.634 11694-11694 com.ieenin...topService com.ieening.blogsservicedemo         D  executing onDestroy method

查看下面ActivityThread.java源代码,可知,ServiceonCreateonBindonUnbind等方法都是在主线程里被调用的。既然Service各个方法是在主线程里执行,那么想要实现计数功能就必须开启子线程来完成此事。

    public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
            updateProcessState(processState, false);
            CreateServiceData s = new CreateServiceData();
            s.token = token;
            s.info = info;
		   // 发送创建 service 消息
            sendMessage(H.CREATE_SERVICE, s);
        }
    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) {
            Slog.v(TAG,
                    "SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj);
        }
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg); // mH 为 ActivityThread 里构造的 Handler,也就是说在主线程里构造的 Handler
    }
        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                ......
                case CREATE_SERVICE: // 创建 Service
                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                                ("serviceCreate: " + String.valueOf(msg.obj)));
                    }
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case BIND_SERVICE: // 绑定 Service
                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind: "
                                + String.valueOf(msg.obj));
                    }
                    handleBindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case UNBIND_SERVICE: // 解绑 Service
                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind: "
                                + String.valueOf(msg.obj));
                    }
                    handleUnbindService((BindServiceData)msg.obj);
                    schedulePurgeIdler();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case SERVICE_ARGS:
                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                                ("serviceStart: " + String.valueOf(msg.obj)));
                    }
                    handleServiceArgs((ServiceArgsData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case STOP_SERVICE: // 停止 Service
                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop: "
                                + String.valueOf(msg.obj));
                    }
                    handleStopService((IBinder)msg.obj);
                    schedulePurgeIdler();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                ......
            }
            Object obj = msg.obj;
            if (obj instanceof SomeArgs) {
                ((SomeArgs) obj).recycle();
            }
            if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
        }
    }

Service一直在计数,计数结果怎么通知给调用者呢,此处假设调用者是Activity。根据前面介绍,可知有两种方式:

  1. 如果是显示开启的Service,则Service可选择广播将数据发送给Activity
  2. 如果是绑定开启的ServiceActivity拿到IBinder引用,进而拿到Service引用,最终可以调用getCount()获得计数值,并更新UI

2.1 显示开启Service+广播更新Activity

代码1

com/ieening/blogsservicedemo/services/StartStopCounterService.java

package com.ieening.blogsservicedemo.services;

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

public class StartStopCounterService extends Service {
    private boolean serviceThreadStopFlag = false;

    public static final String COUNT = "count";

    public static final String COUNTER_SERVICE_BROADCAST = "com.ieening.blogsservicedemo.COUNTER_SERVICE_BROADCAST";
    private final String TAG = StartStopCounterService.class.getName();
    private static int count = 0;

    public StartStopCounterService() {
        Log.d(TAG, "executing StartStopCounterService constructor");
    }

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

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

        serviceThreadStopFlag = false;

        Thread serviceThread = new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                if (serviceThreadStopFlag) {
                    break;
                }
                Log.d(TAG, "executing StartStopCounterService onCreate method, count= " + count);
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                count++;
                Intent intent = new Intent(COUNTER_SERVICE_BROADCAST);
                intent.putExtra(COUNT, count);
                intent.setPackage(getPackageName());
                sendBroadcast(intent);
            }
            Log.d(TAG, "executing StartStopCounterService onCreate method, Service stopped");
            stopSelf();
        });
        serviceThread.start();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        serviceThreadStopFlag = false;
        count = 0;
        Intent sendedIntent = new Intent(COUNTER_SERVICE_BROADCAST);
        sendedIntent.putExtra(COUNT, count);
        sendedIntent.setPackage(getPackageName()); // 一定需要加上,变为显示广播
        sendBroadcast(sendedIntent);
        Log.d(TAG, "executing StartStopCounterService onStartCommand method");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        serviceThreadStopFlag = true;
        Log.d(TAG, "executing StartStopCounterService onDestroy method");
        super.onDestroy();
    }
}

代码1中StartStopCounterService类在onCreate中开启子线程,在count每次更新后,发出广播。

代码2

com/ieening/blogsservicedemo/receivers/CounterServiceReceiver.java

package com.ieening.blogsservicedemo.receivers;

import static com.ieening.blogsservicedemo.services.StartStopCounterService.COUNT;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class CounterServiceReceiver extends BroadcastReceiver {
    private static final String TAG = CounterServiceReceiver.class.getName();
    public static final String SERVICE_COUNT_ACTION = "com.ieening.blogsservicedemo.COUNTER_SERVICE_COUNT";

    @Override
    public void onReceive(Context context, Intent intent) {
        int count = intent.getIntExtra(COUNT, -1);
        Intent activity_intent = new Intent(SERVICE_COUNT_ACTION);
        activity_intent.putExtra(COUNT, count);
        activity_intent.setPackage(context.getPackageName());
        context.sendBroadcast(activity_intent);
        Log.d(TAG, "executing CounterServiceReceiver onReceive method, count=" + count);
    }
}

代码2处理代码1中发出的广播,并将结果转发。

代码3

com/ieening/blogsservicedemo/MainActivity.java

public class MainActivity extends AppCompatActivity {
    ........
    @Override
    protected void onCreate(Bundle savedInstanceState) { 
        ........
        // ! 注册 Receiver
        counterServiceReceiver = new CounterServiceReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(SERVICE_COUNT_ACTION);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            registerReceiver(counterServiceReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
        } else {
            registerReceiver(counterServiceReceiver, filter);
        }

        binding.startCounterServiceButton.setOnClickListener(v -> {
            Log.d(TAG, "click start counter service button to start counter service");
            // ! 显示开启 Service
            Intent intent = new Intent(this, StartStopCounterService.class);
            startService(intent);

            v.setEnabled(false);
            binding.restartCounterServiceButton.setEnabled(true);
            binding.stopCounterServiceButton.setEnabled(true);
        });

        binding.restartCounterServiceButton.setOnClickListener(v -> {
            Log.d(TAG, "click restart counter service button to restart counter service");
            // ! 显示重启 Service
            Intent intent = new Intent(this, StartStopCounterService.class);
            startService(intent);
        });

        binding.stopCounterServiceButton.setOnClickListener(v -> {
            Log.d(TAG, "click stop counter service button to stop counter service");
            // ! 显示停止 Service
            Intent stopIntent = new Intent(this, StartStopCounterService.class);
            stopService(stopIntent);

            v.setEnabled(false);
            binding.startCounterServiceButton.setEnabled(true);
            binding.restartCounterServiceButton.setEnabled(false);
        });
        ......
    }
    private class CounterServiceReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (SERVICE_COUNT_ACTION.equals(intent.getAction())) {
                int count = intent.getIntExtra(COUNT, -1);
                if (count != -1) {
                    binding.startStopCounterServiceCounterText.setText(Integer.toString(count));
                }
                Log.d(TAG, "executing MainActivity CounterServiceReceiver onReceive, count=" + count);
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(counterServiceReceiver);
    }
}
    

代码3中Activity自定义内部类CounterServiceReceiver作为广播Receiver接受处理信息,并在onCreateonDestroy中注册和取消注册广播Receiver。这样,每次在StartStopCounterService中发出的广播,Activity中都可以接受并获取count更新值。

2.2 绑定 Service+Binder

代码4

com/ieening/blogsservicedemo/services/BindUnbindCounterService.java

package com.ieening.blogsservicedemo.services;

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

import com.ieening.blogsservicedemo.binders.BindUnbindServiceBinder;

public class BindUnbindCounterService extends Service {
    private final String TAG = BindUnbindCounterService.class.getName();

    private boolean serviceThreadStopFlag = false;


    public int getCount() {
        return count;
    }

    private int count = 0;


    public BindUnbindCounterService() {
        Log.d(TAG, "executing BindUnbindCounterService constructor");
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "executing BindUnbindCounterService onBind");
        return new BindUnbindServiceBinder(this);
    }

    @Override
    public void onCreate() {
        // Service初次创建会调用该方法,我们可以做一些初始化操作,与 onDestroy相对
        Log.d(TAG, "executing BindUnbindCounterService onCreate");
        serviceThreadStopFlag = false;

        super.onCreate();

        Thread serviceThread = new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                if (serviceThreadStopFlag) {
                    break;
                }
                Log.d(TAG, "executing BindUnbindCounterService onCreate method, count= " + count);
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                count++;
            }
            Log.d(TAG, "executing BindUnbindCounterService onCreate method, Service stopped");
            stopSelf();
        });
        serviceThread.start();
    }

    @Override
    public void onDestroy() {
        serviceThreadStopFlag = true;
        // 每次销毁 Service 时都会调用该方法,在该方法内,可以添加释放资源的操作,与 onCreate 相对
        Log.d(TAG, "executing BindUnbindService onDestroy");
        super.onDestroy();
    }
}

代码4中定义BindUnbindCounterService类,onCreate中新建子线程并更新count,定义getCount函数,对外开放count结果。

代码5

com/ieening/blogsservicedemo/binders/BindUnbindServiceBinder.java

package com.ieening.blogsservicedemo.binders;


import android.app.Service;
import android.os.Binder;
import android.util.Log;

public class BindUnbindServiceBinder extends Binder {
    private final String TAG = BindUnbindServiceBinder.class.getName();
    private final Service service;

    public BindUnbindServiceBinder(Service service) { // ! 持有 Service 引用
        Log.d(TAG, "executing BindUnbindServiceBinder constructor");
        this.service = service;
    }

    public Service getService() { // ! 返回 Service 引用
        Log.d(TAG, "executing BindUnbindServiceBinder getService");
        return service;
    }
}

Binder类,持有Service实例,供Activity使用。

代码6

com/ieening/blogsservicedemo/MainActivity.java

public class MainActivity extends AppCompatActivity {
    ......
    private BindUnbindCounterService bindUnbindCounterService;
    ........
    @Override
    protected void onCreate(Bundle savedInstanceState) { 
        ........
        // ! 绑定 Activity 和 Service
        ServiceConnection counterServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG, "executing ServiceConnection onServiceConnected");
                BindUnbindServiceBinder bindUnbindServiceBinder = (BindUnbindServiceBinder) service;
                // 获取 MyService 引用
                bindUnbindCounterService = (BindUnbindCounterService) bindUnbindServiceBinder.getService();
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                bindUnbindCounterService = null;
                Log.d(TAG, "executing ServiceConnection onServiceDisconnected");
                // Service 被销毁时调用(内存不足等,正常解绑不会走这)
            }
        };

        binding.bindCounterServiceButton.setOnClickListener(v -> {
            Log.d(TAG, "click bind counter service button to bind counter service");
            Intent bindIntent = new Intent(this, BindUnbindCounterService.class);
            bindService(bindIntent, counterServiceConnection, BIND_AUTO_CREATE);

            binding.bindCounterServiceButton.setEnabled(false);
            binding.unbindCounterServiceButton.setEnabled(true);
            binding.counterServiceGetCountButton.setEnabled(true);
        });

        binding.unbindCounterServiceButton.setOnClickListener(v -> {
            Log.d(TAG, "click unbind counter service button to unbind counter service");
            unbindService(counterServiceConnection);

            binding.bindCounterServiceButton.setEnabled(true);
            binding.unbindCounterServiceButton.setEnabled(false);
            binding.counterServiceGetCountButton.setEnabled(false);
        });

        binding.counterServiceGetCountButton.setOnClickListener(v -> {
            if (!Objects.isNull(bindUnbindCounterService)) {
                binding.bindUnbindCounterServiceCounterText.setText(Integer.toString(bindUnbindCounterService.getCount()));
            }
        });
        ......
    }
   
}

ServiceConnection counterServiceConnectiononServiceConnected获取bindUnbindServiceBinder,通过bindUnbindServiceBinder获取BindUnbindCounterService实例,从而可以直接调用getCount获取子线程更新的数据。

2.3 Service、Thread和Manager

2.3.1 Service与Thread

既然Service无法直接执行耗时操作,那么需要Service干嘛呢,还不如直接开启子线程执行任务呢? 我们都知道Service是长时间在后台运行。 实际上说的是Service的生命周期,也就是说Service对象一直存在,当我们需要使用Service的时候,通过Intent或者IBinder就能找到它,进而使用它提供的功能。同样实现计数功能,如果直接在Activity里开启Thread计数,当Activity退出的时候,要把Thread关闭了。再次开启Activity时,已经找不到Thread引用了,无法继续上次的累计计数。再者,就算不考虑内存泄漏,Activity退出时候不关闭Thread,再次开启Activity的时候,依然找不到Thread引用。另外如果想将计数功能抽出来,供多个Activity使用,直接使用Thread也无法实现多Activity共用计数功能。

2.3.2 Service与Manager

既然维护Thread全局引用方法不太推荐,那么实现一个单例的Manager(管理类)来持有Thread,进而使用Thread执行耗时任务,而外界通过调用这个Manager来获取数据,代码如下:

代码7

com/ieening/blogsservicedemo/manager/CountManager.java

package com.ieening.blogsservicedemo.manager;

import android.util.Log;

import java.util.Objects;

public class CountManager {
    private static final String TAG = CountManager.class.getName();
    private static volatile CountManager instance;

    public int getCount() {
        return count;
    }

    private int count = 0;

    private boolean stopCountFlag = false;

    public static CountManager getInstance() {
        if (Objects.isNull(instance)) {
            synchronized (CountManager.class) {
                if (Objects.isNull(instance)) {
                    instance = new CountManager();
                }
            }
        }
        return instance;
    }

    public void startCount() {
        stopCountFlag = false;
        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                if (stopCountFlag) {
                    break;
                }
                count++;
                Log.d(TAG, "executing startCount, count=" + count);
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public void stopCount() {
        stopCountFlag = true;
    }
}

事实上不少的项目都是采用Activity + Manager方式来实现页面展示 + 数据获取。 Activity展示UI,后台通过Manager获取数据,如从数据库获取或者从网络获取等,最后将数据反馈给Activity用以刷新UI。 到此你可能疑惑了,都有了Manager了,Service还有使用的必要吗? 答案是肯定的。 Service作为Android 四大组件之一,是广泛使用于Android系统里的。

  1. Service可以调整优先级,尽可能避免在资源紧张的时候被销毁
  2. 借助Service + Binder,实现Android进程间通信

工程代码

文章来源:https://blog.csdn.net/qq_31654025/article/details/135330306
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。