对于Android四大组件Service的的学习

先看一下他的简述

Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。服务可由其他应用组件启动(如Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。

 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行,Service基本上分为两种形式(也就是启动模式吧)

启动状态

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

绑定状态

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

来一张官方说明


接下来边打边说

先建一个Service

这时AndroidManifest.xml文档为我们生成了这样一段,其实和Activity差不多

那正好先看一下AndroidManifest.xml中一些简单的声明语法都有什么,其格式如下:

<service android:enabled=["true" | "false"]

    android:exported=["true" | "false"]

    android:icon="drawable resource"

    android:isolatedProcess=["true" | "false"]

    android:label="string resource"

    android:name="string"

    android:permission="string"

    android:process="string" >

    . . .

</service>

android:exported:代表是否能被其他应用隐式调用,其默认值是由service中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。为false的情况下,即使有intent-filter匹配,也无法打开,即无法被其他应用隐式调用。

android:name:对应Service类名

android:permission:是权限声明

android:process:是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。

android:isolatedProcess :设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。

android:enabled:是否可以被系统实例化,默认为 true因为父标签 也有 enable 属性,所以必须两个都为默认值 true 的情况下服务才会被激活,否则不会激活。

ok!


接下来我们先试一下startService()

我们重写这几个生命周期的标签在给他写上打印日志,logonUnbind和onBind按理说是不会出现的我们试一下。

怎么启动service呢?很简单和activity一样用Intent就可以

startService(new Intent(this,MyService.class));

结果如下

即使我们back,home甚至打开别的应用一直没有onDestroy

除非你把他后台关闭或者在别的程序中很久让手机自动杀死进程这是为什么呢?

我们在service里面运行一小点东西看看

@Override

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

Log.i(TAG, "onStartCommand: ");

    try {

Thread.sleep(5000);

    }catch (InterruptedException e) {

e.printStackTrace();

    }

Toast.makeText(this,"我还活着",Toast.LENGTH_SHORT).show();

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

}

然后方便测试我们让他自己在一个线程上运行

刚学过的

android:process=":remote"

果然即使我Activity,back甚至把开启他的进程都关了他还活着

问什么呢看看上面,因为上面说到,一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务, 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。

那我们要给他停止才会出现onDestroy么?怎么停止?

我们在activity的onstop方法中加入下面这个方法就成功了

stopService(intent);

ok我们重新回过头来进一步分析onStartCommand(Intent intent,int flags,int startId),这个方法有3个传入参数,它们的含义如下:

onStartCommand(Intent intent,int flags,int startId)

intent:启动时,启动组件传递过来的Intent,如Activity可利用Intent封装所需要的参数并传递给Service

flags:表示启动请求时是否有额外数据,可选值有0,START_FLAG_REDELIVERY,START_FLAG_RETRY,0代表没有,它们具体含义如下:

START_FLAG_REDELIVERY

这个值代表了onStartCommand方法的返回值为

START_REDELIVER_INTENT,而且在上一次服务被杀死前会去调用stopSelf方法停止服务。其中START_REDELIVER_INTENT意味着当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个Intent调用onStartCommand(),此时Intent时有值的。

START_FLAG_RETRY

该flag代表当onStartCommand调用后一直没有返回值时,会尝试重新去调用onStartCommand()。

startId:指明当前服务的唯一ID,与stopSelfResult(int startId)配合使用,stopSelfResult可以更安全地根据ID停止服务。

实际上onStartCommand的返回值int类型才是最最值得注意的,它有三种可选值,START_STICKY,START_NOT_STICKY,START_REDELIVER_INTENT,它们具体含义如下:

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。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

  由于每次启动服务(调用startService)时,onStartCommand方法都会被调用,因此我们可以通过该方法使用Intent给Service传递所需要的参数,然后在onStartCommand方法中处理的事件,最后根据需求选择不同的Flag返回值,以达到对程序更友好的控制。


现在 试一下bindService()


我们看到除了intent还要需要传递两个参数

我们来写一个serviceconnection(是个接口有两个必须实现的方法)

这两个方法是干什么的呢

onServiceConnected访问到sever的操作,另一个就是没有访问的。

然后最后添加BIND_AUTO_CREATE属性(没有就创建)的意思也可以选择0这个选项(不创建)

点击运行

但是onServiceConnected我们发现还有一个参数,而且service和activity不能相互传值这时我们可以使用广播,但是并不推荐因为这样会破坏代码的模块化。

此时我们就要使用ibinder service这个参数,他就是service的onbind返回过来的值我们可以看到onbind的返回值正是ibinder


而binder正是继承了ibinder所以可以创建一个内部类实现调用自身

class ibinder extends Binder{

public MyService getservice(){

return MyService.this;

    }

}

如果想要调用service就通过

MyService.ibinder iservice = (MyService.ibinder) service;

iservice.getservice.XXXXX

的方式获得需要的方法或者参数


试一下

package com.hrk.service2;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;

import android.content.Intent;

import android.content.ServiceConnection;

import android.os.Binder;

import android.os.Bundle;

import android.os.Handler;

import android.os.IBinder;

import android.os.Looper;

import android.view.View;

import android.widget.TextView;

public class MainActivityextends AppCompatActivity {

Intentintent;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        TextView text = findViewById(R.id.text);

        View viewById = findViewById(R.id.btn);

        viewById.setOnClickListener(view -> {

MyService.num +=1;

            text.setText(String.valueOf(MyService.num));

        });

    }

public void btn_1(View view) {

intent =new Intent(this, MyService.class);

        startService(intent);

    }

public void btn_2(View view) {

intent =new Intent(this, MyService.class);

        bindService(intent,conn,BIND_AUTO_CREATE);

    }

ServiceConnectionconn =new ServiceConnection() {

@Override

        public void onServiceConnected(ComponentName name, IBinder service) {

MyService.ibinder iservice = (MyService.ibinder) service;

            iservice.getservice();

        }

@Override

        public void onServiceDisconnected(ComponentName name) {

}

};

    public void btn_3(View view) {

stopService(intent);

    }

}

package com.hrk.service2;

import android.app.Service;

import android.content.Intent;

import android.os.Binder;

import android.os.IBinder;

import android.util.Log;

public class MyServiceextends Service {

private static final StringTAG ="ssd";

    public static int num =0;

    public MyService() {

}

@Override

    public void onCreate() {

super.onCreate();

        Log.i(TAG, "onCreate: ");

    }

@Override

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

Log.i(TAG, "onStartCommand: ");

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

    }

@Override

    public IBinderonBind(Intent intent) {

Log.i(TAG, "onBind: ");

        return new ibinder();

    }

class ibinderextends Binder{

public MyServicegetservice(){

return MyService.this;

        }

}

@Override

    public boolean onUnbind(Intent intent) {

Log.i(TAG, "onUnbind: ");

        return super.onUnbind(intent);

    }

@Override

    public void onDestroy() {

Log.i(TAG, "onDestroy: ");

        super.onDestroy();

    }

}



IntentService(据说基本上没用了)

它本质是一种特殊的Service,继承自Service并且本身就是一个抽象类

它会提供单独的worker线程来处理所有的intent请求

它可以用于在后台执行耗时的异步任务,当任务完成后会自动停止

它拥有较高的优先级,不易被系统杀死(继承自Service的缘故),因此比较适合执行一些高优先级的异步任务

它内部通过HandlerThread和Handler实现异步操作

@Override

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

try {

Thread.sleep(20000);

    }catch (InterruptedException e) {

e.printStackTrace();

    }

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

}

@Override

protected void onHandleIntent(@Nullable Intent intent) {

try {

Thread.sleep(20000);

    }catch (InterruptedException e) {

e.printStackTrace();

    }

}

分别在service和intentservice中让线程等待20秒模拟一个20秒的任务,分别启动service和intentservice发现启动service会报错出现anr异常而intentservice不会

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

推荐阅读更多精彩内容