Android四大组件之Service

Android学习的第一步从四大组件开始


在四大组件中ActivityService应该算的上是一对亲兄弟了,两者的使用都比较频繁而且在使用上亦有很多相似之处(毕竟都是有Context的人)。但是“亲兄弟也要明算账”这对兄弟一个负责在外面“抛头露面”,一个负责在后面“默默无闻”。
“抛头露面”的那位已经介绍完了,现在来看看久居后台的这位。

Service 是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。
服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。

1.注册

既然进行注册那肯定少不了AndroidManifest.xml文件,毕竟package中所有暴露的组件都需要在这里声明。与Activity相同,Service的注册就是在 <application/> 节点下添加一个<service/>节点:

<manifest ... > 
    ... 
    <application ... >     
        <service android:name=".TestService" />    
        ...  
    </application>
</manifest>

在注册时只有android:name属性是必须的属性,其他属性可以根据自己的情况进行添加,这里不额外进行介绍了,推荐在开发者文档进行查看。

注意一点点:
为了确保应用的安全性,请始终使用显式 Intent 启动或绑定 Service,且不要为服务声明 Intent 过滤器。

2.生命周期的前一章(启动/绑定状态)

因为service的生命周期和使用service的处理方式密切相关,所以这里插一段说明一下。
对于service的处理方式,有两种态度,一种是 "放养" :任务给你了,你去干吧,其他的我就不管了,但是让你停下时必须停下;另一种是 "放风筝" :任务给你了,但是你要跟着绳子走,如果绳子断了,你就会掉下来。

  • 启动状态
      当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务, 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。

  • 绑定状态
      当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

3.生命周期

不一样的选择会有不一样的人生,不同的状态自然有不同的生命周期。下面我们看一下Service这位默默无闻的人的“人生路线”。

Service的生命周期

使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。(就是在这里写遗书,分配一下“死”后的“财产”)

然后就是Service正式“参与工作”(有效生命周期):

  1. 第一种是通过 调用startService() 开启服务:
  • onStartCommand()
    当开启者(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行(一直运行到onDestroy,与开启者无关)。 如果自己实现此方法,则需要在服务工作完成后,通过调用 stopSelf()stopService() 来停止服务。
  1. 第二种是通过调用bindService() 开启服务:
  • onBind()
    当开启者(如 Activity)想通过调用 bindService() 与服务绑定时,系统将调用此方法。在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。
  • onUnbind()
    当开启者(如 Activity)想通过调用 unbindService() 与服务解除绑定时,系统将调用此方法。Service解除绑定后将被销毁

注意一点点:
无论是启动状态还是绑定状态,onBind() 方法必须重写,但在启动状态的情况下直接返回 null
在绑定状态下,无需实现onStartCommand() 方法

4.Service启动状态

从上面生命周期的图中,我们可以看到启动服务是通过startService()启动,这会调用服务的onCreate()(仅第一次)和onStartCommand()方法,直到stopService()停止服务。直到了基本流程了,我们来写一个启动状态的Service:

package com.test.servicetest;

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

/**
 * Service 启动状态
 *
 * @author ydd
 */
public class StartServiceTest extends Service{

    private static final String TAG = "StartServiceTest";

    /**
     * 服务绑定时调用,且必须要实现该方法(abstract函数)
     * @param intent 接收 bindService()的传入
     * @return 返回IBinder接口的实现,在绑定状态使用,本处为空
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind()");
        return null;
    }

    /**
     * 首次创建服务时调用,且super中无任何实现
     */
    @Override
    public void onCreate() {
        Log.d(TAG,"onCreate()");
        super.onCreate();
    }

    /**
     * 每次通过startService()方法启动Service时都会被回调。
     * @param intent 启动时startService()传输过来的数据包
     * @param flags 表示启动请求时是否有额外数据
     * @param startId 指定当前服务的唯一Id
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG,"onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 销毁服务时调用
     */
    @Override
    public void onDestroy() {
        Log.d(TAG,"onDestroy()");
        super.onDestroy();
    }
}

服务写起来很简单Service中的abstract方法只有onBind(),所以这里必须实现onBind,为了看一下完整的生命周期运行,这里在几个关键的回调函数中打印Log,看一下运行情况。下面再实现一下操作界面:

package com.test.servicetest;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

/**
 * 主界面
 *
 * @author ydd
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener{


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startServiceBtn = (Button) findViewById(R.id.btnStartService);
        Button stopServiceBtn = (Button) findViewById(R.id.btnStopService);
        startServiceBtn.setOnClickListener(this);
        stopServiceBtn.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        Intent intent = new Intent(this , StartServiceTest.class);
        switch (v.getId()){
            case R.id.btnStartService:
                startService(intent);
                break;
            case R.id.btnStopService:
                stopService(intent);
                break;
            default:
                break;
        }
    }
}

界面只添加了两个按钮一个用于启动服务(调用startService())一个用于关闭服务(调用stopService()).代码都准备好了,下面我们开始进行测试。
说明一下:本次操作为连续2次启动服务,然后停止服务。再次开启服务并停止服务。

    ——————————第一次开启————————————
    com.test.servicetest D/StartServiceTest: onCreate()
    com.test.servicetest D/StartServiceTest: onStartCommand()
    ——————————第二次开启————————————
    com.test.servicetest D/StartServiceTest: onStartCommand()
    ——————————关闭服务—————————————
    com.test.servicetest D/StartServiceTest: onDestroy()
    ——————————重新开启服务———————————
    com.test.servicetest D/StartServiceTest: onCreate()
    com.test.servicetest D/StartServiceTest: onStartCommand()
    ——————————关闭服务—————————————
    com.test.servicetest D/StartServiceTest: onDestroy()

第一次启动时从Log中我们可以清晰的看出第一次启动时调用了onCreate()onStartCommand()方法,之后在开启时,因为 Service 已经存在了,就只调用onStartCommand()方法。关闭服务调用onDestroy()方法,当服务关闭后,如果再次启动就会开始新的生命周期。
下面来点好玩的东西:
从上面可以看出onStartCommand()是整个开启服务的一个“有趣”点,这里简单介绍一下这个函数。
我们先看传入的参数:

  • intent:是startService()时传过来的;

  • flags:是系统传入 有如下三种值:

    1. 直接通过 startService() 启动时,flags为0;
    2. onStartCommand()返回为START_STICKY_COMPATIBILITY或者START_STICKY并且服务异常杀死后由系统启动;flags为START_FLAG_REDELIVERY=1:
    3. onStartCommand()返回为START_REDELIVER_INTENT并且服务异常杀死后由系统启动;flags为START_FLAG_REDELIVERY=2:
  • startId:表示此特定启动请求的唯一整数。用于唯一表示某次请求。大概是onStartCommand()的启动次数,第一次通过startService启动为是1,不断startService启动依次累加,一般配合stopSelf(startId)使用可以看IntentService中使用。

上面出现了几个陌生的面孔:START_STICKY_COMPATIBILITY、START_STICKY、START_REDELIVER_INTENT
这几个参数对应了onStartCommand()的函数返回值:

  • START_STICKY
    当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此Service,一旦创建成功后将回调onStartCommand方法,但其中的Intent将是null,除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务。
  • START_NOT_STICKY
    当Service因内存不足而被系统kill后,即使系统内存再次空闲时,系统也不会尝试重新创建此Service。除非程序中再次调用startService启动此Service,这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
  • START_REDELIVER_INTENT
    当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),任何挂起 Intent均依次传递。与START_STICKY不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
  • START_STICKY_COMPATIBILITY
    START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

有点乱,这里梳理一下思路:使用startService()启动服务后,在Service中onStartCommand()被调用(如果是第一次,会首先调用onCreate())。我们在onStartCommand()方法中根据接收传入的参数,并根据flags和startId确认当前开启服务的状态,开始处理事务,最后根据当前的需求设置不同的返回值,以便程序更好的对当前Service进行控制。

5.Service绑定状态

不同于启动状态的服务,绑定状态的Service不能单独存在,需要与其他的组件之间建立联系。从生命周期上看,只需要通过onBind()函数返回一个IBinder接口的实现类,用以提供客户端用来与服务进行交互的编程接口,再通过onUnbind()接触绑定结束工作。想起来总是比实际简单,根据情况的不同实际可以通过三种方法定义接口:

  • 扩展 Binder 类
    如果服务是供您的自有应用专用,并且在与客户端相同的进程中运行(常见情况),则应通过扩展 Binder 类并从 onBind() 返回它的一个实例来创建接口。客户端收到 Binder 后,可利用它直接访问 Binder 实现中乃至 Service 中可用的公共方法。
    如果服务只是您的自有应用的后台工作线程,则优先采用这种方法。 不以这种方式创建接口的唯一原因是,您的服务被其他应用或不同的进程占用。
  • 使用 Messenger
    如需让接口跨不同的进程工作,则可使用 Messenger 为服务创建接口。服务可以这种方式定义对应于不同类型Message 对象的 Handler。此 Handler 是 Messenger 的基础,后者随后可与客户端分享一个 IBinder,从而让客户端能利用 Message 对象向服务发送命令。此外,客户端还可定义自有 Messenger,以便服务回传消息。
    这是执行进程间通信 (IPC) 的最简单方法,因为 Messenger 会在单一线程中创建包含所有请求的队列,这样您就不必对服务进行线程安全设计。
  • 使用 AIDL
    AIDL(Android 接口定义语言)执行所有将对象分解成原语的工作,操作系统可以识别这些原语并将它们编组到各进程中,以执行 IPC。 之前采用 Messenger 的方法实际上是以 AIDL 作为其底层结构。 如上所述,Messenger 会在单一线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。 不过,如果您想让服务同时处理多个请求,则可直接使用 AIDL。 在此情况下,您的服务必须具备多线程处理能力,并采用线程安全式设计。
    如需直接使用 AIDL,您必须创建一个定义编程接口的 .aidl 文件。Android SDK 工具利用该文件生成一个实现接口并处理 IPC 的抽象类,您随后可在服务内对其进行扩展。

5.1 扩展 Binder 类

本方法无法跨进程工作,所以只适用于仅提供本应用服务的 Service。通过实现自有的 IBinder ,让客户端通过该接口直接访问服务中的公共方法。
扩展 Binder 类的使用方法大致分为三步:

  1. 在您的服务中,创建一个可满足下列任一要求的IBinder 实例
    • 包含客户端可调用的公共方法
    • 返回当前 Service 实例,其中包含客户端可调用的公共方法
    • 或返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法
  2. onBind() 回调方法返回此 Binder 实例
  3. 在客户端中,从 onServiceConnected() 回调方法接收 Binder实例,并使用提供的方法调用绑定服务。

有启动状态的铺垫,这里直接上代码:

package com.test.servicetest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Service 绑定状态
 *
 * @author ydd
 */
public class BindServiceTest extends Service{

    private static final String TAG = "BindServiceTest";
    private final IBinder mBinder = new TestBinder();
    
    private int count;
    private boolean stop;

    /**
     * 服务绑定时调用,且必须要实现该方法(abstract函数)
     * @param intent 接收 bindService()的传入
     * @return 返回IBinder接口的实现
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind()");
        return mBinder;
    }

    /**
     * 首次创建服务时调用,且super中无任何实现
     */
    @Override
    public void onCreate() {
        Log.d(TAG,"onCreate()");
        super.onCreate();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!stop) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                }
            }
        }).start();
    }

    /**
     * 解除Service绑定
     * @param intent
     * @return
     */
    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG,"onUnbind()");
        return super.onUnbind(intent);
    }

    /**
     * 销毁服务时调用
     */
    @Override
    public void onDestroy() {
        Log.d(TAG,"onDestroy()");
        stop = true;
        super.onDestroy();
    }
    
    /**
     * 获取服务时长
     */
    public int getCount(){
        return count;
    }

    /**
     * 定制需要传出的Binder
     */
    public class TestBinder extends Binder {
        BindServiceTest getService(){
            return BindServiceTest.this;
        }
    }
}

BindService的创建和StartService的最大的区别是增加了Binder的实现以及,在onBind()函数中不再返回空值,更改为一个Binder对象。另外既然是可以互相通信的,我们就在Service中记录开启的时间长短,并在开启者组件中获取一下这个数据看一下。下面看一下控制界面的代码。

package com.test.servicetest;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

/**
 * 主界面
 *
 * @author ydd
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "BindServiceTest";
    private ServiceConnection connection;
    private BindServiceTest mService;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startServiceBtn = (Button) findViewById(R.id.btnStartService);
        Button stopServiceBtn = (Button) findViewById(R.id.btnStopService);
        Button getDataBtn = (Button) findViewById(R.id.btnGetData);
        startServiceBtn.setOnClickListener(this);
        stopServiceBtn.setOnClickListener(this);
        getDataBtn.setOnClickListener(this);
        connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG,"——onServiceConnected");
                mService = ((BindServiceTest.TestBinder) service).getService();

            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.d(TAG,"——onServiceDisconnected");
                mService = null;
            }
        };
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent(this, BindServiceTest.class);
        switch (v.getId()) {
            case R.id.btnStartService:
                bindService(intent, connection, Service.BIND_AUTO_CREATE);
                break;
            case R.id.btnStopService:
                unbindService(connection);
                break;
            case R.id.btnGetData:
                if (mService != null) {
                    Log.d(TAG, "服务当前已经开启:" + mService.getCount() + "秒");
                }else {
                    Log.d(TAG, "服务当前未开启!");
                }
                break;
            default:
                break;
        }
    }
}

根据启动模式的Service的讲解和两种状态的生命周期,我们应该可以知道两者的开启和关闭调用的方法是不同的。在绑定状态下,我们使用的是bindService()unbindService()函数来控制Service。另外这里使用了ServiceConnected,并实现了两个函数:

  • onServiceConnected(ComponentName name, IBinder service)
    该函数在服务连接成功后调用,获取onBind() 方法返回的 IBinder。这里的service便是w我们在Service中定义的Binder实现类对象。通过该对象我们便可以按照我们设计的方法对Service进行操作,在上面的例子中我们获取了BindServiceTest的实例,进而调用服务端的公共方法。

ComponentName是一个封装了组件(Activity, Service, BroadcastReceiver, or ContentProvider)信息的类,如包名,组件描述等信息,较少使用该参数。

  • onServiceDisconnected(ComponentName name)
    Android 系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法。
    注意:当客户端取消绑定时,系统“绝对不会”调用该方法。

看完ServiceConnected我们再重新看一下函数bindService(),在参数中处理Intent和刚刚提到的ServiceConnected还有一个int flags,这个参数有时负责控制什么的?
简单的说就是对绑定进行控制操作(因为我只是知道简单的),例如上面使用到的参数:

  • BIND_AUTO_CREATE:就是用来控制创建Service。在启动模式中调用startService()时实际上是通过启动者来创建一个Service实例。在绑定状态中有些区别,是向serviceMessenger发送一个请求,由它来控制是否创建Service

补充一点点

  1. 当 flags 不等于 BIND_AUTO_CREATE 时,bindService是不会自动启动service的。
  2. 与启动模式与绑定模式的参数不同:
    bindService(Intent service, ServiceConnection conn, int flags)
    unbindService(ServiceConnection conn)

5.2 Messenger

终于到Messenger了,一种可以跨进程通讯的方式,用起来稍微比上面的方式复杂一点,不过毕竟功能更强(可以跨进程通讯),稍微难一点也是可以理解的。这里想大致介绍一下使用方法:

  • 服务端(Service)
  1. 创建一个handler(Service)对象,并实现hanlemessage方法,用于接收来自客户端的消息,并作处理;
  2. 创建一个messenger(Service),,封装handler(Service);
  3. 用messenger(Service)的getBinder()方法获取一个IBinder对象,通过onBind返回给客户端;
  4. 等待客户端发来的消息;
  • 客户端(Activity)
  1. 在activity中绑定服务;
  2. 创建一个handler(Client)对象,并实现hanlemessage方法,用于接收Service返回的消息,并作处理;
  3. 创建一个messenger(Client),,封装handler(Client);
  4. 创建ServiceConnection并在其中使用 IBinder 将 messenger(Service)实例化(这里就是Service中的创建一个messenger,通过IBinder的方式来到了客户端) ;
  5. 创建Message并在其中加载数据,注意为了能接收Service的回复信息,这里将Message.replyTo设置为messenger(Client);
  6. 使用Messenger(Service)向服务端发送消息;
  • 服务端(Service)
  1. 接收到Messager(Service)带回来的消息,进行处理;
  2. 创建一个新的Message,装载处理结果;
  3. 使用Message.replyTo调用Messenger(Client)向客户端发送消息;
  • 客户端(Activity)
  1. 接收到Messager(Client)带回来的消息,进行处理进行相关显示操作;
  2. 整体操作完成,解除绑定;

补充一点点:
Messenger个人感觉,理解为收信人比较好。如果A需要向B发消息,A中就需要有一个B的收信人B在,A将Message交给收信人B,并由收信人B带给(send)B。这时如果A想知道B的回复,就派自己的收信人A,跟着(replyTo)收信人B一起去B,等B的处理结果,由收信人A带回A。

说起来有点绕,大家看一下代码,应该会更好理解:
服务端

package com.test.servicetest;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.annotation.Nullable;

/**
 * 绑定服务使用Messenger
 *
 * @author ydd
 */
public class MessengerService extends Service {

    private static final int SIGN = 1;
    private final Messenger mMessenger = new Messenger(new CustomHandler());

    private class CustomHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            Message message = new Message();
            switch (msg.what) {
                case SIGN:
                    message.arg1 = msg.arg1 + msg.arg2;
                    message.what = SIGN;
                    try {
                        msg.replyTo.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }
            super.handleMessage(msg);
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

客户端

package com.test.servicetest;

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.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
/**
 * 主界面
 *
 * @author ydd
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    public static final int SIGN = 1;
    private ServiceConnection connection;
    private boolean mBound;
    private Messenger mService;

    private Messenger mMessenger = new Messenger(new MainHandler());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startServiceBtn = (Button) findViewById(R.id.btnStartService);
        Button stopServiceBtn = (Button) findViewById(R.id.btnStopService);
        Button setDataBtn = (Button) findViewById(R.id.btnSetData);
        startServiceBtn.setOnClickListener(this);
        stopServiceBtn.setOnClickListener(this);
        setDataBtn.setOnClickListener(this);
        connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mService = new Messenger(service);
                mBound = true;
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                mService = null;
                mBound = false;
            }
        };
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent(this, MessengerService.class);
        switch (v.getId()) {
            case R.id.btnStartService:
                bindService(intent, connection, Service.BIND_AUTO_CREATE);
                break;
            case R.id.btnStopService:
                unbindService(connection);
                break;
            case R.id.btnSetData:
                if (mBound) {
                    try {
                        Message message = new Message();
                        message.arg1 = 3;
                        message.arg2 = 4;
                        message.what = SIGN;
                        message.replyTo = mMessenger;
                        mService.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                } else {
                    Toast.makeText(this,
                            "当前服务未打开,请开启服务!", Toast.LENGTH_LONG).show();
                }
                break;
            default:
                break;
        }
    }

    private class MainHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SIGN:
                    Toast.makeText(MainActivity.this,
                            "服务器说等于:" + msg.arg1, Toast.LENGTH_LONG).show();
                    break;
                default:
                    break;
            }
            super.handleMessage(msg);
        }
    }

}

客户端连接到服务器后,向服务器发送两个值“3”“4”,然后服务器求和计算回复“7”,服务器接收到回复消息,并显示在界面。

补充一点点
想深入了解Messager的话推荐《Android 基于Message的进程间通信 Messenger完全解析》鸿洋大神的文章

6.注意事项

6.1同时启动和绑定

前面介绍了Service的启动状态和绑定状态,应该还记得在Service中onBind()是必须实现的方法,那是不是就意味着,在启动状态的Service中,onBind()不返回null,就可以启动绑定模式?同理,在绑定状态的Service中,是不是也可以进入启动状态?
答案是肯定的。
需要注意一点的时,只要Service进入过启动状态(调用startService()),服务将在后台运行,直到有Context调用了stopService()或是服务本身调用了stopSelf()方法抑或内存不足时才会销毁服务。

6.2生命周期控制

因为Service本身没有界面空着,为了把控Service的生命周期,通常情况下需要在客户端(例如Activity)生命周期中于合适的时间开启(绑定)和关闭(解绑)Service。需要注意的是切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,这样反复绑定与解绑是不合理的。

6.3使用服务还是线程?

简单地说,服务是一种即使用户未与应用交互也可在后台运行的组件。 因此,您应仅在必要时才创建服务。
如需在主线程外部执行工作,不过只是在用户正在与应用交互时才有此需要,则应创建新线程而非服务。 例如,如果您只是想在 Activity 运行的同时播放一些音乐,则可在 onCreate() 中创建线程,在 onStart() 中启动线程,然后在 onStop() 中停止线程。您还可以考虑使用 AsyncTask 或 HandlerThread,而非传统的 Thread 类。如需了解有关线程的详细信息,请参阅进程和线程文档。
请记住,如果您确实要使用服务,则默认情况下,它仍会在应用的主线程中运行,因此,如果服务执行的是密集型或阻止性操作,则您仍应在服务内创建新线程。——《Android Developers Doc》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容