Android with Service上的并发

本文探讨了Android中的服务组件,特别是如何利用服务进行并发处理。服务是Android中执行后台任务的重要工具,可以独立于用户交互和活动生命周期。文章详细介绍了服务的类型,包括启动服务和绑定服务,以及如何在工作线程上运行服务,如IntentService。同时,还讲解了进程间通信(IPC)的概念,以及服务如何与其他组件进行通信。
摘要由CSDN通过智能技术生成

在本教程中,我们将探索Service组件及其超类IntentService 。 您将学习何时以及如何使用此组件为长时间运行的后台操作创建出色的并发解决方案。 我们还将快速浏览IPC (进程间通信),以学习如何与运行在不同进程上的服务进行通信。

1.服务组件

Service组件是Android并发框架中非常重要的一部分。 它满足了在应用程序中执行长时间运行的需求,或者为其他应用程序提供了某些功能。 在本教程中,我们将只专注于Service的长期运行任务功能,以及如何使用此功能来提高并发性。

什么是服务?

Service是一个简单的组件,由系统实例化以执行一些长期运行的工作,这些工作不一定依赖于用户交互。 它可以独立于活动生命周期,也可以在完全不同的过程中运行。

在深入讨论Service代表什么之前,必须强调一点,即使服务通常用于长时间运行的后台操作并在不同的进程上执行任务, Service也不代表Thread或进程 。 如果明确要求,它将仅在后台线程或其他进程中运行。

Service具有两个主要功能:

  • 应用程序的一种功能,可以在后台将系统要执行的操作告知系统。
  • 应用程序将其某些功能公开给其他应用程序的工具。

服务和线程

关于服务和线程有很多困惑。 声明Service ,它不包含Thread 。 实际上,默认情况下,它直接在主线程上运行,并且对它所做的任何工作都可能冻结应用程序。 (除非它是IntentService ,它是已经配置了工作线程的Service子类。)

那么,服务如何提供并发解决方案? 嗯, Service默认情况下不包含线程,但是可以轻松地将其配置为与自己的线程或线程池一起使用。 我们将在下面看到更多信息。

无论缺少内置线程,对于某些情况下的并发问题, Service都是一个很好的解决方案。 选择Service不是其他并发解决方案(例如AsyncTask或HaMeR框架)的主要原因是:

  • Service可以独立于活动生命周期。
  • Service适合于长时间运行。
  • 服务不依赖于用户交互。
  • 当在不同的进程上运行时,即使系统资源短缺,Android也可以尝试保持服务活动。
  • 可以重新启动Service以恢复其工作。

服务类型

有两种Service类型,即启动绑定。

启动的服务通过Context.startService()启动。 通常,它仅执行一项操作,并且会无限期运行,直到操作结束,然后它会自行关闭。 通常,它不会向用户界面返回任何结果。

绑定服务是通过Context.bindService()启动的,它允许客户端和Service之间进行双向通信。 它还可以与多个客户端连接。 当没有任何客户端连接时,它会自我毁灭。

要在这两种类型之间进行选择, Service必须实现一些回调: onStartCommand()作为启动服务运行, onBind()作为绑定服务运行。 Service可以选择仅实现这些类型中的一种,但也可以同时采用这两种类型,而不会出现任何问题。

2.服务实施

要使用的服务,扩展的Service类并覆盖其回调方法,根据类型Service 。 如前所述,对于启动的服务,必须实现onStartCommand()方法,对于绑定的服务,必须实现onBind()方法。 实际上, onBind() 必须为这两种服务类型声明方法,但是对于已启动的服务,它可以返回null。

public class CustomService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // Execute your operations
        // Service wont be terminated automatically
        return Service.START_NOT_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        // Creates a connection with a client
        // using a interface implemented on IBinder
        return null;
    }
}
  • onStartCommand() :由Context.startService()启动。 通常从活动中调用它。 调用后,该服务可能会无限期运行,您可以通过调用stopSelf()stopService()来停止该服务。
  • onBind() :当组件要连接到服务时调用。 在系统上由Context.bindService()调用。 它返回一个IBinder ,它提供与客户端进行通信的接口。

服务的生命周期也很重要。 应该实现onCreate()onDestroy()方法来初始化和关闭服务的任何资源或操作。

声明舱单服务

必须在清单上使用<service>元素声明Service组件。 在此声明中,也可以但非强制性地设置Service运行的其他过程。

<manifest ... >
  ...
  <application ... >
      <service 
        android:name=".ExampleService"
        android:process=":my_process"/>
      ...
  </application>
</manifest>

使用启动服务

要启动已启动的服务,必须调用Context.startService()方法。 必须使用ContextService类创建Intent 。 任何相关信息或数据也应在此Intent传递。

Intent serviceIntent = new Intent(this, CustomService.class);
// Pass data to be processed on the Service
Bundle data = new Bundle();
data.putInt("OperationType", 99);
data.putString("DownloadURL", "https://mydownloadurl.com");
serviceIntent.putExtras(data);
// Starting the Service
startService(serviceIntent);

Service类中,您应关注的方法是onStartCommand() 。 在此方法上,您应该调用要在启动的服务上执行的任何操作。 您将处理Intent以捕获客户端发送的信息。 startId代表为该特定请求自动创建的唯一ID,并且flags也可以包含有关此请求的其他信息。

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

        Bundle data = intent.getExtras();
        if (data != null) {
            int operation = data.getInt(KEY_OPERATION);
            // Check what operation to perform and send a msg
            if ( operation == OP_DOWNLOAD){
                // make a download
            }
        }

        return START_STICKY;
    }

onStartCommand()返回控制行为的常量int

  • Service.START_STICKY :如果服务终止,则重新启动。
  • 服务Service.START_NOT_STICKY :服务未重新启动。
  • Service.START_REDELIVER_INTENT :崩溃后,服务将重新启动,然后将重新传送其意图。

如前所述,已启动的服务需要停止,否则它将无限期地运行。 这可以通过Service本身调用stopSelf()或客户端对其调用stopService()来完成。

void someOperation() {
        // do some long-running operation
        // and stop the service when it is done
        stopSelf();
    }

绑定服务

组件可以与服务建立连接,并与它们建立双向通信。 客户端必须调用Context.bindService() ,并传递IntentServiceConnection接口和flag作为参数。 一个Service可以绑定到多个客户端,并且在没有客户端连接的情况下它将被销毁。

void bindWithService() {
        Intent intent = new Intent(this, PlayerService.class);
        // bind with Service
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

可以将Message对象发送到服务。 为此,您需要在ServiceConnection.onServiceConnected接口实现中的客户端上创建Messenger ,并将其用于将Message对象发送到Service

private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            // use the IBinder received to create a Messenger
            mServiceMessenger = new Messenger(service);
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
            mServiceMessenger = null;
        }
    };

也可以将响应Messenger传递给Service以使客户端接收消息。 但是请当心,因为客户端可能不再会收到该服务的消息。 您也可以使用BroadcastReceiver或任何其他广播解决方案。

private Handler mResponseHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // handle response from Service
        }
    };
    Message msgReply = Message.obtain();
    msgReply.replyTo = new Messenger(mResponseHandler);
    try {
        mServiceMessenger.send(msgReply);
    } catch (RemoteException e) {
        e.printStackTrace();
    }

销毁客户端时,取消与服务的绑定很重要。

@Override
    protected void onDestroy() {
        super.onDestroy();
        // disconnect from service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

Service方面,您必须实现Service.onBind()方法, IBinder提供从Messenger提供的IBinder 。 这将中继一个响应Handler以处理从客户端收到的Message对象。

IncomingHandler(PlayerService playerService) {
            mPlayerService = new WeakReference<>(playerService);
        }

        @Override
        public void handleMessage(Message msg) {
            // handle messages
        }
    }
    
    public IBinder onBind(Intent intent) {
        // pass a Binder using the Messenger created
        return mMessenger.getBinder();
    }
    
    final Messenger mMessenger = new Messenger(new IncomingHandler(this));

3并发使用服务

最后,是时候讨论如何使用服务解决并发问题了。 如前所述,标准Service不包含任何额外的线程,默认情况下它将在主Thread上运行。 为了克服这个问题,您必须添加一个辅助Thread ,线程池或在其他进程上执行Service 。 您还可以使用一个名为IntentServiceService子类,该子类已经包含Thread

使服务在工作线程上运行

要使Service在后台Thread上执行,您可以只创建一个额外的Thread并在那里运行作业。 但是Android为我们提供了更好的解决方案。 充分利用该系统的一种方法是在Service内部实现HaMeR框架,例如,通过将Thread与可以无限期处理消息的消息队列进行循环。

重要的是要了解此实现将按顺序处理任务。 如果需要同时接收和处理多个任务,则应使用线程池。 使用线程池不在本教程的讨论范围内,我们今天不再讨论。

要使用HaMeR,必须为Service提供一个Looper ,一个Handler和一个HandlerThread

private Looper mServiceLooper;
    private ServiceHandler mServiceHandler;
    // Handler to receive messages from client
    private final class ServiceHandler extends Handler {
        ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // handle messages

            // stopping Service using startId
            stopSelf( msg.arg1 );
        }
    }
    
    @Override
    public void onCreate() {
        HandlerThread thread = new HandlerThread("ServiceThread",
                Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);

    }

IntentService

如果不需要长时间保持Service处于活动状态,则可以使用IntentService ,它是一个Service子类,可以在后台线程上运行任务。 在内部, IntentService是一种Service ,其实现与上述提议的实现非常相似。

要使用此类,您要做的就是扩展它并实现onHandleIntent() ,这是一个挂钩方法,每次客户端在此Service上调用startService()时都会调用该方法。 重要的是要记住, IntentService将在其工作完成后立即停止。

public class MyIntentService extends IntentService {
    
    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // handle Intents send by startService
    }
}

IPC(进程间通信)

Service可以在完全不同的Process上运行,而独立于主流程上发生的所有任务。 进程具有自己的内存分配,线程组和处理优先级。 当您需要独立于主要流程工作时,此方法非常有用。

不同进程之间的通信称为IPC(进程间通信) Service ,有两种主要的IPC方法:使用Messenger或实现AIDL接口。

我们已经学习了如何在服务之间发送和接收消息。 所有你需要做的就是用创建一个Messenger使用IBinder在连接过程中接收到的实例,并用它来发送回复 MessengerService

private Handler mResponseHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // handle response from Service
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            // use the IBinder received to create a Messenger
            mServiceMessenger = new Messenger(service);

            Message msgReply = Message.obtain();
            msgReply.replyTo = new Messenger(mResponseHandler);
            try {
                mServiceMessenger.send(msgReply);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

AIDL接口是一个非常强大的解决方案,它允许直接调用在不同进程上运行的Service方法,并且当您的Service非常复杂时,它是合适的。 但是, AIDL实现复杂且很少使用,因此在本教程中不会讨论它的使用。

4.结论

服务可以是简单的也可以是复杂的。 这取决于您的应用程序的需求。 我在本教程中尝试了尽可能多的内容,但是我只专注于将服务用于并发目的,并且该组件还有更多的可能性。 我想进一步研究,请参阅文档Android指南

再见!

翻译自: https://code.tutsplus.com/tutorials/concurrency-on-android-with-service--cms-27277

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值