Android基础 - Service(服务)

老婆保佑,代码无BUG

目录

  • 什么是服务
  • 分类
    • 更具是否在同一进程分类
    • 更具运行类别分类
    • 启动方式上分类
  • 服务如何写
    • 启动式服务
    • 绑定式服务
    • 前台服务
    • 远程服务(AIDL 进程间通信)
    • intentService

一:什么是服务?

Service(服务)是一个没有用户界面的在后台运行执行耗时操作的应用组件。(其他应用组件能够启动Service,并且当用户切换到另外的应用场景,Service将持续在后台运行,你可以启动一个服务Service来播放音乐,或者记录你地理信息位置的改变,或者启动一个服务来运行并一直监听某种动作)

二:分类

更具是否在同一进程分类

类型 特点 优点 缺点 适用场景
本地服务 指的是服务和启动服务的activity在同一个进程中,开启的服务给本应用程序使用 (本app使用的) 节约资源。通信方便。不需要AIDL和IPC 限制大,主进程被杀死,服务也会停止 依附在某个进程的服务 如音乐播放器
远程服务 指的是服务和启动服务的activity不在同一个进程中。开启的服务给其他的应用程序使用 (给其他任何app使用的 基于IPC协议) 灵活,服务常住在后台,不受其他Activity影响 消耗资源:单独进程,使用AIDL,和IPC 系统级别服务

更具运行类别分类

类型 特点 适用场景
前台服务 在通知栏通知(用户可以看到) 服务运行时,让用户知道,并操作,音乐播放器
后台服务 处于后台的服务(用户无法看到) 服务终止时,用户无法知道

启动方式上分类

类型 特点
启动式 生命周期不依赖其他组件
绑定式 生命周期和其他组件绑定

三. 服务如何写

1. 启动式服务

生命周期

  onCreate-->onStartCommand-->onDestroy 

(1) 继承Service

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

/**
 * 描述:本地服务
 * 

* Created by allens on 2017/12/27. */ public class LocalService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); Log.e("TAG", "onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e("TAG", "onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Log.e("TAG", "onDestroy"); } }

onBind方法补充说明

onBind(...)函数是Service基类中的唯一抽象方法,子类都必须重写实现,此函数的返回值是针对Bound Service类型的Service才有用的,在Started Service类型中,此函数直接返回 null 即可

onStartCommand 方法补充说明

  1. 可以多次被调用。

  2. 这个方法的作用是处理服务和系统的关系,默认是START_STICKY_COMPATIBILITY , 返回值有四种具体如下

类型 名称 说明
START_STICKY 粘性服务 如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null
START_NOT_STICKY 非粘性 如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务
START_REDELIVER_INTENT 粘性服务强化 如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
START_STICKY_COMPATIBILITY START_STICKY的兼容版本 不保证服务被kill后一定能重启,此值一般不会使用

强调一点

当Service因为内存不足而被系统kill后一定要非常注意,因为此函数的返回值设定只是针对此种情况才有意义的,换言之,当认为的kill掉Service进程,此函数返回值无论怎么设定,接下来未来的某个时间内,即使系统内存足够可用,Service也不会重启。

onDestroy 方法补充说明

当用户强制kill掉进程时,onDestroy()是不会执行的

(2) 清单文件注册

 

清单文件申明补充说明

属性 说明
name Service的类名
process 设置具体的进程名称,不设置默认false为本地服务
exported 该服务是否能够被其他应用程序所控制或连接
icon Service的图标
label Service的名字,若不设置,默认为Service类名
permission 申明此Service的权限

(3) Activity 中启动

 
//启动服务
//生命周期 先执行onCreate 然后 onStartCommand
        findViewById(R.id.local_start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, LocalService.class);
                startService(intent);
            }
        });

//关闭服务
//生命周期 onDestroy
        findViewById(R.id.local_stop).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, LocalService.class);
                stopService(intent);
            }
        });

2. 绑定式服务

生命周期

  onCreate-->onBind-->onUnbind-->onDestroy 

(1) 继承Service


public class BindService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("TAG", "onCreate");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("TAG", "onBind");
        //获取Acitivity传递的值
        String value = intent.getStringExtra("data1");
        Log.e("TAG", "获取的内容--->" + value);
        return new MyBinder();
    }


    @Override
    public boolean onUnbind(Intent intent) {
        Log.e("TAG", "onUnbind");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        Log.e("TAG", "onDestroy");
        super.onDestroy();
    }

    //自定义一个Binder类继承 Binder
    public class MyBinder extends Binder {
        //实现自己的逻辑
        public int getNumber() {
            //返回给Activity一个随机数,Activity显示就可以了
            return new Random().nextInt(20);
        }
    }
}

(2) 清单文件注册

 

(3) Activity

 //绑定服务
        findViewById(R.id.bind_start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, BindService.class);

                connection = new ServiceConnection() {
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                        Log.e("TAG", "绑定服务成功了");
                        binder = (BindService.MyBinder) service;
                    }

                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                        Log.e("TAG", "绑定服务断开了");
                    }
                };
                //通过intent的extras进行值的传递
                intent.putExtra("data1", "前台的界面绑定服务了");
                bindService(intent, connection, Service.BIND_AUTO_CREATE);
            }
        });

//下面是Log信息
//12-27 01:06:42.499 3300-3300/com.allens.servicedemo E/TAG: onCreate
//12-27 01:06:42.499 3300-3300/com.allens.servicedemo E/TAG: onBind
//12-27 01:06:42.499 3300-3300/com.allens.servicedemo E/TAG: 获取的内容--->前台的界面绑定服务了
//12-27 01:06:42.506 3300-3300/com.allens.servicedemo E/TAG: 绑定服务成功了


        //获取服务中传过来的参数
        findViewById(R.id.bind_getData).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (binder != null) {
                    int number = binder.getNumber();
                    Log.e("TAG", "NUMBER---->" + number);
                }
            }
        });
//下面是Log信息
//12-27 01:08:23.518 3300-3300/com.allens.servicedemo E/TAG: NUMBER---->13
        
        //解除绑定
        findViewById(R.id.bind_stop).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //解除绑定的方法
                if (connection != null) {
                    unbindService(connection);
                    //自己控制引用的myBinder对象
                    binder = null;
                }
            }
        });

//下面是Log信息
//12-27 01:08:39.856 3300-3300/com.allens.servicedemo E/TAG: onUnbind
//12-27 01:08:39.856 3300-3300/com.allens.servicedemo E/TAG: onDestroy

注意

在四大基本组件中,需要注意的的是BroadcastReceiver不能作为Bind ServiceClient(依附对象),因为BroadcastReceiver的生命周期很短,当执行完onReceive(..)回调时,BroadcastReceiver生命周期完结。而Bind Service又与Client本身的生命周期相关,因此,Android中不允许BroadcastReceiverbindService(..),当有此类需求时,可以考虑通过startService(..)替代。


3. 前台服务

前台服务其实只是在启动式服务基础上,加上了Notification通知

给出onCreate()中的代码

 @Override
    public void onCreate() {
        super.onCreate();
        Log.e("TAG", "onCreate");

        //添加下列代码将后台Service变成前台Service
        //构建"点击通知后打开MainActivity"的Intent对象
        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

        //新建Builer对象
        Notification.Builder builer = new Notification.Builder(this);
        //设置通知的标题
        builer.setContentTitle("前台服务通知的标题")
                //设置通知的内容
                .setContentText("前台服务通知的内容")
                //设置通知的图标
                .setSmallIcon(R.mipmap.ic_launcher)
                //设置点击通知后的操作
                .setContentIntent(pendingIntent);
        //将Builder对象转变成普通的notification
        Notification notification = builer.getNotification();
        //让Service变成前台Service,并在系统的状态栏显示出来
        startForeground(1, notification);
    }

效果

Android基础 - Service(服务)_第1张图片
效果图片

4. 远程服务

为了让远程Service与多个应用程序的组件(四大组件)进行跨进程通信(IPC),需要使用AIDL

  1. IPC:Inter-Process Communication,即跨进程通信
  2. AIDL:Android Interface Definition Language,即Android接口定义语言;用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。

服务端 Service

(1)兴建一个AIDL 文件

Android基础 - Service(服务)_第2张图片
新建一个AIDL 文件

建立好以后好会在与java同级的目录下看到一个aidl的文件夹,如下图所示


Android基础 - Service(服务)_第3张图片
屏幕快照 2017-12-27 下午3.10.10.png

(2) Rebuild Project

一定不能忘了,


Android基础 - Service(服务)_第4张图片
屏幕快照 2017-12-27 下午3.10.29.png

完成以后,可以在 build 中看到 已经编译好了


Android基础 - Service(服务)_第5张图片
屏幕快照 2017-12-27 下午3.11.10.png

(3) 在Service子类中实现AIDL中定义的接口方法

package com.allens.servicedemo.Service;

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

/**
 * 描述:  AIDL 服务端service
 * 

* Created by allens on 2017/12/27. */ public class ServerService extends Service { private MyBinder myBinder; //继承IServer.Stub 并实现其接口 public class MyBinder extends IServer.Stub { @Override public void onServer() throws RemoteException { Log.e("TAG", "客户端与远程服务通信成功"); System.out.print("客户端与远程服务通信成功 " + Thread.currentThread().getName()); } } @Nullable @Override public IBinder onBind(Intent intent) { Log.e("TAG", "onBind"); return myBinder; } @Override public void onCreate() { super.onCreate(); Log.e("TAG", "onCreate"); myBinder = new MyBinder(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e("TAG", "onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Log.e("TAG", "onDestroy"); } }

(4) 清单文件注册信息

        
        
        
            
            
                
            
        

客户端 Service

(1) 将服务端的aidl 文件直接复制到 客户端

Android基础 - Service(服务)_第6张图片
复制之后的目录

小提示

最好将AS 的目录切换到Project,在进行复制


Android基础 - Service(服务)_第7张图片
在这里可以切换

(2) Activity

package com.allens.servicecliet;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.allens.servicedemo.Service.IServer;

public class MainActivity extends AppCompatActivity {


    //创建ServiceConnection的匿名类
    private ServiceConnection connection = new ServiceConnection() {

        //重写onServiceConnected()方法和onServiceDisconnected()方法
        //在Activity与Service建立关联和解除关联的时候调用
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e("TAG", "onServiceDisconnected");
        }

        //在Activity与Service建立关联时调用
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e("TAG", "onServiceConnected");
            //IService.Stub.asInterface()方法获取服务器端返回的IBinder对象
            //将IBinder对象传换成了mAIDL_Service接口对象
            IServer iServer = IServer.Stub.asInterface(service);
            try {
                //通过该对象调用在IService.aidl文件中定义的接口方法,从而实现跨进程通信
                iServer.onServer();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_bind).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("TAG", "onClick");
                //通过Intent指定服务端的服务名称和所在包,与远程Service进行绑定
                //参数与服务器端的action要一致,即"服务器包名.aidl接口文件名"
                Intent intent = new Intent("com.allens.servicedemo.IServer");

                //Android5.0后无法只通过隐式Intent绑定远程Service
                //需要通过setPackage()方法指定包名
                intent.setPackage("com.allens.servicedemo");

                //绑定服务,传入intent和ServiceConnection对象
                bindService(intent, connection, Context.BIND_AUTO_CREATE);
            }
        });
    }
}

最后看Log

当我们点击Client 中的Button时候,

12-27 02:52:06.702 5661-5661/com.allens.servicecliet E/TAG: onClick
12-27 02:52:06.979 5661-5661/com.allens.servicecliet E/TAG: onServiceConnected

然后去看Server中的log

12-27 02:52:06.905 5687-5687/com.allens.servicedemo:div E/TAG: onCreate
12-27 02:52:06.906 5687-5687/com.allens.servicedemo:div E/TAG: onBind
12-27 02:52:06.980 5687-5704/com.allens.servicedemo:div E/TAG: 客户端与远程服务通信成功

如果没看服务端的Log,请注意看一下,是否把进程切换到自己定义的进程下

Android基础 - Service(服务)_第8张图片
我是div 自己定义的

最后 双手奉上代码

ServiceServerDemo 点击跳转
ServiceClietDemo 点击跳转


5. IntentService

特点

  • 不需要主动调用stopSelft()来结束服务。因为,在所有的intent被处理完后,系统会自动关闭服务。

  • 可以在onHandleIntent中做耗时操作

  • 当多次点击时候,会将任务添加到队列中,直到所有的任务都完成了,销毁服务

生命周期

onCreate-->onStartCommand-->onHandleIntent-->onDestroy

继承IntentService

/**
 * 描述:intentService
 * 

* Created by allens on 2017/12/27. */ public class MyIntentService extends IntentService { /*** * 比较规范的action条件,包名+action+功能意义的描述 */ private static final String ACTION_FOO = "com.allens.servicedemo.Service.FOO"; private static final String ACTION_BAZ = "com.allens.servicedemo.Service.BAZ"; /*** * 规范的key的写法 */ private static final String EXTRA_PARAM1 = "com.allens.servicedemo.Service.PARAM1"; private static final String EXTRA_PARAM2 = "com.allens.servicedemo.Service.PARAM2"; public MyIntentService() { //给IntentService内部创建的工作线程起名字 super("MyIntentService"); } /** * 外部直接调用该方法就可以启动该服务了(设置了action条件,传递的参数,并且启动了服务) * * @see IntentService */ public static void startActionFoo(Context context, String param1, String param2) { Intent intent = new Intent(context, MyIntentService.class); intent.setAction(ACTION_FOO); intent.putExtra(EXTRA_PARAM1, param1); intent.putExtra(EXTRA_PARAM2, param2); context.startService(intent); } public static void startActionBaz(Context context, String param1) { Intent intent = new Intent(context, MyIntentService.class); intent.setAction(ACTION_BAZ); intent.putExtra(EXTRA_PARAM1, param1); context.startService(intent); } /** * 进行耗时操作的处理 (当该方法执行完毕以后,服务会自动关闭) * 通过action区分不同的耗时操作的逻辑,各自实现各自的逻辑 *

* 从核心代码入手,分析每一行代码表达意思,(可以中间涉及其他方法和成员变量的定义,以此扩展出去查看) * * @param intent */ @Override protected void onHandleIntent(Intent intent) { Log.e("TAG", "onHandleIntent"); if (intent != null) { //获取action条件 final String action = intent.getAction(); if (ACTION_FOO.equals(action)) { //获取字符串内容 final String param1 = intent.getStringExtra(EXTRA_PARAM1); final String param2 = intent.getStringExtra(EXTRA_PARAM2); handleActionFoo(param1, param2); } else if (ACTION_BAZ.equals(action)) { final String param1 = intent.getStringExtra(EXTRA_PARAM1); handleActionBaz(param1); } } } //模拟耗时 private void handleActionBaz(String param1) { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } Log.e("TAG", "param1 " + param1); } private void handleActionFoo(String param1, String param2) { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void onCreate() { Log.e("TAG", "onCreate"); super.onCreate(); } @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { Log.e("TAG", "onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { Log.e("TAG", "onDestroy"); super.onDestroy(); } }

清单文件注册

 

Activity

  MyIntentService.startActionBaz(MainActivity.this,"test1");

写在最后

首先本文全部的DEMO 都可以在GitHub 上看到

ServiceServerDemo 点击跳转

你可能感兴趣的:(Android基础 - Service(服务))