关于Service你要知道的事

service是什么?

按照官方文档中的解释,Service是一个可以在后台执行长时间运行操作而不需要用户界面的应用组件。Service可由其他组件启动,即使用户切换到其他应用Service仍旧运行。另外组件也可以绑定到服务,并与之进行交互,甚至IPC通信。

Service
Service

Service的开启

  • startService()

    用户通过startService()启动服务时,服务就处于启动状态,一旦启动服务,服务即可在后台无线运行,即使启动服务的组件被销毁也不受影响。不会反回结果给调用方。
    通过startService()开启的服务将会一直运行,直到使用stopSelf(),或由其他组件通过调用stopService()停止。

  • bindService()

用户通过bindService()绑定到服务时,服务就处于"绑定"状态,绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果。仅当与另一个应用组件绑定时,绑定服务才运行。
多个组件可绑定到同一个服务上,全部取消绑定后,该服务就会被销毁。

  • 注意

服务托管在主线程中运行,所以如果有耗时的操作,应创建新的线程来完成这项工作。

Service的回调方法

  • onStartCommand()

当另一个组件通过startService()请求启动服务时,系统将调用此方法。一旦执行此方法,服务就会在后台无限期运行。如果你实现此方法,则在服务工作完成后需要通过调用stopSelf()或stopService(Intent intent)来停止服务。如果您只需实现绑定,则无需实现此方法。

  • onBand()

当另一个组件通过bindService()与服务绑定时,系统将调用此方法。在此方法的实现中,必须通过返回IBinder提供一个接口,供客户端用来与服务端通信。如果不允许绑定,则返回null。

  • onCreate()

首次创建服务时,系统将调用此方法。如果服务已运行,则不用调用此方法。

  • onDestory()

当服务被销毁或不再使用时,调用此方法。应实现此方法清理资源。

IntentService:

Service的子类,使用工作线程串行处理多个启动请求,你只需实现onHandleIntent()方法。该方法会接收每一个启动请求的Intent。
IntentService执行的操作:

  1. 创建工作线程,用于执行执行传递给onStartCommand()的所有Intent。
  2. 创建工作队列,将Intent逐一传递给onHandleIntent()实现,串行的处理各Intent请求。
  3. 在处理完所有启动请求后自动停止服务,不用程序员自行处理。
  4. onBind()默认返回null。
  5. onStartCommand()方法的默认是实现,将Intent一次发送到工作队列和onHandleIntent()。
public class HelloIntentService extends IntentService {
  public HelloIntentService() {
      super("HelloIntentService");
  }
  @Override  protected void onHandleIntent(Intent intent) {
      try {
          Thread.sleep(5000);
      } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
      }
  }
} 

继承Service类

使用 IntentService显著简化了启动服务的实现。但是,若要求服务执行多线程(而不是通过工作队列处理启动请求),则可扩展Service类来处理每个 Intent。

public class HelloService extends Service {    
private Looper mServiceLooper;    
private ServiceHandler mServiceHandler;    
private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override        public void handleMessage(Message msg) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            stopSelf(msg.arg1);
        }
    }    
   @Override    public void onCreate() {
        HandlerThread thread = new
        HandlerThread("ServiceStartArguments",
                Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    } 
   @Override    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(this, "service starting",                 Toast.LENGTH_SHORT).show();
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        mServiceHandler.sendMessage(msg);
        return START_STICKY;
    }    
@Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public void onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
    }
}

onStartCommand的返回

onStartCommand必须返回一个整数,代表不同的意义。

  1. START_NOT_STICKY:如果系统在onStartCommand返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
  2. START_STICKY:如果系统在 onStartCommand()返回后终止服务,则会重建服务并调用onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。
  3. START_REDELIVER_INTENT:如果系统在 onStartCommand()返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

启动Service

  1. 自从Lollipop以后,只能显示启动Service。
  2. 如果需要在5.0以上是设备上实现隐式启动,需要使用以下方式。给Intent设置包名。
Intent mIntent = new Intent();
mIntent.setAction("XXX.XXX.XXX");//你定义的service的action
mIntent.setPackage(getPackageName());//这里你需要设置你应用的包名
context.startService(mIntent);

startService()方法将立即返回,且 Android 系统调用服务的 onStartCommand()方法。如果服务尚未运行,则系统会先调用 onCreate(),然后再调用onStartCommand()。

前台运行服务

前台服务被认为是用户主动意识到的一种服务,因此在内存不足时,系统也不会考虑将其终止** 前台服务必须为状态栏提供通知
**,放在“正在进行”标题下方,这意味着除非服务停止或从前台移除,否则不能清除通知。
要请求让服务运行于前台,请调用 startForeground()。
具体使用方法请查阅相关资料!

绑定服务

基础知识

要提供绑定服务,必须实现onBind()回调方法。该方法返回的IBinder对象定义了客户端与服务进行交互的编程接口。
客户端通过bindService()绑定到服务,调用时必须提供ServiceConnection的实现,来监控与服务的连接。bindService()方法会立即无值返回,但当 Android 系统创建客户端与服务之间的连接时,会对ServiceConnection调用onServiceConnected(),向客户端传递用来与服务通信的IBinder。
多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的onBind()方法来检索IBinder。系统随后无需再次调用onBind(),便可将同一IBinder传递至任何其他绑定的客户端。
当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非startService()也启动了该服务)。

创建绑定服务

创建提供绑定的服务时,您必须提供IBinder,用以提供客户端用来与服务进行交互的编程接口。 您可以通过三种方法定义接口:

  • 扩展Binder类
  • 使用Messenger
  • 使用AIDL

扩展Binder类

  • 在您的服务中,创建一个可满足下列任一要求的Binder实例:
    1.包含客户端可调用的公共方法
    2.返回当前Service实例,其中包含客户端可调用的公共方法
    3.返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法
public class LocalService extends Service {
    //返回给客户端的Binder
    private final IBinder mBinder = new LocalBinder();
    private final Random mGenerator = new Random();
    //继承Binder类
    public class LocalBinder extends Binder {
        //通过该方法取得service实例,以便客户端能够拿到service实例,使用service中的方法
        LocalService getService() {
            return LocalService.this;
        }
    }
    @Override    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    //暴露给客户端的函数
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}
public class BindingActivity extends Activity {
    LocalService mService;
    //标志位,判断客户端是否已经绑定服务。
    boolean mBound = false;
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    @Override    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);    }
    @Override    protected void onStop() {
        super.onStop();
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
   public void onButtonClick(View v) {
        if (mBound) {
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " +
                      num,Toast.LENGTH_SHORT).show();
        }
    }
    //ServiceConnection实例,用来监控客户端和Service的连接
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override   public void onServiceConnected(ComponentName className, IBinder service) {
            //建立连接时,通过Service传来的IBinder实例得到相应的具体Binder类
            LocalBinder binder = (LocalBinder) service;
            //通过Binder得到service实例,用该实例使用service中对外开放的方法。
            mService = binder.getService();
            mBound = true;
        }
        @Override  public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}

使用Messenger

如需让服务与远程进程通信,则可使用Messenger为您的服务提供接口。利用此方法,您无需使用 AIDL 便可执行进程间通信 (IPC)。

Messenger方法概要

  • 服务实现一个Handler,用来接收来自客户端的每一个回调。
  • Handler用于创建Messenger对象
  • Messenger创建一个IBinder,服务通过onBinde()返回客户端。
  • 客户端使用IBinder将Messenger实例化,然后使用Messenger将Message发送给服务
  • 服务在其Handler中处理每一个Message

一个messenger服务

public class MessengerService extends Service {
    static final int MSG_SAY_HELLO = 1;
    //处理客户端发来的message
    class IncomingHandler extends Handler {
        @Override   public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
     final Messenger mMessenger = new Messenger(new IncomingHandler());
    @Override    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        //通过messenger返回Binder
        return mMessenger.getBinder();
    }
}

客户端根据service返回的Binder创建一个Messenger

public class ActivityMessenger extends Activity {
    Messenger mService = null;
    boolean mBound;
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            //通过service返回的IBinder创建一个Messenger
            mService = new Messenger(service);
            mBound = true;
        }
        public void onServiceDisconnected(ComponentName className){
            mService = null;
            mBound = false;
        }
    };
    public void sayHello(View v) {
        if (!mBound) return;
        Message msg = Message.obtain(null,
                 MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    @Override    protected void onStart() {
        super.onStart();
        bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);
    }
    @Override    protected void onStop() {
        super.onStop();
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,695评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,569评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,130评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,648评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,655评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,268评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,835评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,740评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,286评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,375评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,505评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,873评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,357评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,466评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,921评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,515评论 2 359

推荐阅读更多精彩内容

  • 什么是Service Service是一个可以在后台长时间执行的应用组件。 Service的启动方式 startS...
    MGLEE阅读 364评论 0 0
  • 上篇我们讲解了Android中的5中等级的进程,分别是:前台进程、可见进程、服务进程、后台进程、空进程。系统会按照...
    徐爱卿阅读 3,864评论 6 33
  • 目录 Service 介绍 Service两种启动方式 使用 测试 IntentService Activity与...
    gaaaaaaaaaao阅读 1,682评论 0 4
  • 那时候的路尽是山里 那时候的梦也有大海 鼻子里发出闪烁的声音 一手抓不住,用两只手 哼出的歌谣从山这头飘到山那头 ...
    人谷阅读 188评论 0 0
  • 文:耳双生 今天在朋友圈看到一个分享,很有禅机特别有味道,于是关注了这个微博——一禅小和尚。 虽然知道这是一个虚拟...
    耳双生阅读 6,958评论 10 12