Android服务

参考:

一. 什么是服务

服务是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

二. 服务的两种启动方式

  1. start模式启动
  • 当应用组件(如 Activity)通过调用 startService()启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 操作完成后,服务会自行停止运行。
  1. bind模式启动
  • 当应用组件通过调用 bindService()绑定到服务时,服务即处于“绑定”状态(与活动绑定)。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

三. Start模式启动服务

  1. 该模式会回调的方法
/**
     * 该方法是必须实现的抽象方法,用于bind模式启动,在调用bindService之后会调用,用于将服务和调用组件进行绑定。在start模式的时候返回null即可
     * @param intent 用来绑定该服务的intent
     * @return 返回的就是自定义的用于操作服务的接口
     */
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind: ");
        Log.i(TAG, "onBind: intent "+intent);
        Log.i(TAG, "onBind: data "+intent.getStringExtra("data"));
        return mDownloadBinder;
    }
/**
     * 该方法在服务只在第一次启动/绑定的时候会调用
     */
    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate: ");
        super.onCreate();
    }

    /**
     *
     * 该方法在每次调用startService方法的时候都会调用,
     * 一旦执行此方法,服务即会启动并可在后台无限期运行。
     * 服务工作完成后,需要通过调用 stopSelf() 或 stopService() 来停止服务。
     *
     * @param intent 就是用来启动该服务的Intent,传递的数据可以通过该intent拿出来
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand: ");
        Log.i(TAG, "onStartCommand: "+intent);
        Log.i(TAG, "onStartCommand: data "+intent.getStringExtra("data"));
        Log.i(TAG, "onStartCommand: flags "+flags);
        Log.i(TAG, "onStartCommand: startId "+startId);
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 该方法在调用stopService/unbindService方法的时候会调用,服务停止运行
     */
    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy: ");
        super.onDestroy();
    }
  1. 使用该模式启动服务的步骤
  • 新建sevice类继承自Service
  • 至少实现onStartCommand回调方法
  • 注册服务
  • 调用startService(Intent)方法启动该服务,该服务就会一直在后台运行,直到调用stopService(Intent)或stopSelf(Intent)方法

四. bind方式启动服务

  1. 该模式会回调的方法
@Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind: ");
        Log.i(TAG, "onBind: intent "+intent);
        Log.i(TAG, "onBind: data "+intent.getStringExtra("data"));
        return mDownloadBinder;
    }

    /**
     * 当与服务连接的所有组件都断开连接的时候调用(Called when all clients have disconnected
     * from a particular interface published by the service.
     * The default implementation does nothing and returns false.)
     * 
     * @param intent 用来绑定服务的intent
     * @return 默认返回false
     */
    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, "onUnbind: ");
        Log.i(TAG, "onUnbind: intent "+intent.getStringExtra("data"));
        return super.onUnbind(intent);
    }

    /**
     * 当有新的组件绑定到服务的时候会调用该方法
     * @param intent 用来绑定服务的intent
     */
    @Override
    public void onRebind(Intent intent) {
        Log.i(TAG, "onRebind: ");
        super.onRebind(intent);
    }
  1. 使用bind方式启动服务的步骤
  • 创建Binder类作为操作服务的接口
/**
     * 用于Activity操作Service的类,给与服务绑定的组件一个操作服务的接口
     */
    class DownloadBinder extends Binder{

        public void startDownload(){
            Log.i(TAG, "startDownload: ");
        }

        public void getProgress(){
            Log.i(TAG, "getProgress: ");
        }
    }
  • 实现onBind方法并返回Binder对象
  • Activity实现ServiceConnection类,重写onServiceConnectedonServiceDisconnected方法
private MyService.DownloadBinder mDownloadBinder;//Binder对象,用来操作服务

    private ServiceConnection mConnection = new ServiceConnection() {
        /**
         * 活动与服务绑定的时候调用
         * @param name
         * @param service
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "onServiceConnected: name " + name);
            mDownloadBinder = (MyService.DownloadBinder) service;//将IBinder参数强转,实例化自定义的Binder对象
            mDownloadBinder.startDownload();//调用Binder的方法操作服务
            mDownloadBinder.getProgress();
        }

        /**
         * 活动与服务解绑的时候调用
         * @param name
         */
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "onServiceDisconnected: ");
        }
    };
  • 调用bindService方法进行绑定
/*参数:Intent,ServiceConnection,标志位(BIND_AUTO_CREATE表示活动与服务绑定之后自动创建服务)*/
bindService(startService, mConnection, BIND_AUTO_CREATE);

五. 前台服务

  1. 通过调用startForeground方法(借助通知)使服务显示在通知栏就是前台服务(如通知栏显示的QQ这种)
  2. 使用方法(该段代码写在Service类的onCreate方法中)
Intent intent = new Intent(this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
        Notification notification = new NotificationCompat.Builder(this)
                .setContentTitle("This is title")
                .setContentText("This is text")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
                .setContentIntent(pendingIntent)
                .build();
        startForeground(1,notification);

六. IntentService

  1. 什么是IntentService?
  • 因为服务是运行在主线程的,所以当需要进行耗时操作的时候,就需要在服务内开启子线程,然后在运行结束的时候调用stopSelf方法结束该服务。为了避免这一大堆麻烦事,IntentService就诞生了。
  1. IntentService做的事(来自官网)
  1. 使用方法
  • 新建一个类继承自IntentService
  • 实现无参构造函数并调用父类的有参构造函数(必须)
  • 实现onHandleIntent方法(该方法运行在子线程),在该方法内进行想要的操作
public class MyIntentService extends IntentService {

    private final String TAG = getClass().getSimpleName();

    /**
     * 必须实现的无参构造函数,且必须调用父类有参构造方法
     */
    public MyIntentService(){
        super("MyIntentService");
    }

    /**
     * 处理具体逻辑的抽象方法,该方法运行在子线程
     * @param intent 启动服务用的intent
     */
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.i(TAG, "onHandleIntent: ");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 服务销毁时会调用
     */
    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy: ");
        super.onDestroy();
    }
}
  • 正常调用startService方法启动服务

七. 源码

  • MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    /**
     * start service
     */
    private Button mBtStartService;
    /**
     * stop service
     */
    private Button mBtStopService;
    private final String TAG = getClass().getSimpleName();
    /**
     * bind service
     */
    private Button mBindService;
    /**
     * unbind service
     */
    private Button mUnbindService;

    private MyService.DownloadBinder mDownloadBinder;//Binder对象,用来操作服务

    private ServiceConnection mConnection = new ServiceConnection() {
        /**
         * 活动与服务绑定的时候调用
         * @param name
         * @param service
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "onServiceConnected: name " + name);
            mDownloadBinder = (MyService.DownloadBinder) service;//将IBinder参数强转,实例化自定义的Binder对象
            mDownloadBinder.startDownload();//调用Binder的方法操作服务
            mDownloadBinder.getProgress();
        }

        /**
         * 活动与服务解绑的时候调用
         * @param name
         */
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "onServiceDisconnected: ");
        }
    };
    /**
     * start service
     */
    private Button mBtBindService;
    /**
     * start activity
     */
    private Button mBtStartActivity;
    /**
     * start intentservice
     */
    private Button mStartIntentService;

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

    @Override
    public void onClick(View v) {
        Intent startService = new Intent(MainActivity.this, MyService.class);
        startService.putExtra("data", "data");
        switch (v.getId()) {
            case R.id.bt_bind_service:
                Log.i(TAG, "onClick: " + startService);
                startService(startService);
                break;
            case R.id.bt_stop_service:
                //Log.i(TAG, "onClick: "+startService);
                stopService(startService);
                break;
            case R.id.bind_service:
                /*参数:Intent,ServiceConnection,标志位(BIND_AUTO_CREATE表示活动与服务绑定之后自动创建服务)*/
                bindService(startService, mConnection, BIND_AUTO_CREATE);
                //mDownloadBinder.startDownload();
                break;
            case R.id.unbind_service:
                unbindService(mConnection);
                break;
            case R.id.bt_start_activity:
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
                break;
            case R.id.start_intent_service:
                startService(new Intent(MainActivity.this,MyIntentService.class));
                break;
        }
    }

    private void initView() {
        mBtStartService = (Button) findViewById(R.id.bt_bind_service);
        mBtStartService.setOnClickListener(this);
        mBtStopService = (Button) findViewById(R.id.bt_stop_service);
        mBtStopService.setOnClickListener(this);
        mBindService = (Button) findViewById(R.id.bind_service);
        mBindService.setOnClickListener(this);
        mUnbindService = (Button) findViewById(R.id.unbind_service);
        mUnbindService.setOnClickListener(this);
        mBtBindService = (Button) findViewById(R.id.bt_bind_service);
        mBtBindService.setOnClickListener(this);
        mBtStartActivity = (Button) findViewById(R.id.bt_start_activity);
        mBtStartActivity.setOnClickListener(this);
        mStartIntentService = (Button) findViewById(R.id.start_intent_service);
        mStartIntentService.setOnClickListener(this);
    }
}
  • MyService
public class MyService extends Service {

    private final String TAG = getClass().getSimpleName();

    private DownloadBinder mDownloadBinder = new DownloadBinder();

    /**
     * 用于Activity操作Service的类,给与服务绑定的组件一个操作服务的接口
     */
    class DownloadBinder extends Binder{

        public void startDownload(){
            Log.i(TAG, "startDownload: ");
        }

        public void getProgress(){
            Log.i(TAG, "getProgress: ");
        }
    }

    public MyService() {
    }

    /**
     * 该方法是必须实现的抽象方法,用于bind模式启动;
     * 在调用bindService之后会调用,用于将服务和调用组件进行绑定。
     * 在start模式的时候返回null即可
     * @param intent 用来绑定该服务的intent
     * @return 返回的就是自定义的用于操作服务的接口
     */
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind: ");
        Log.i(TAG, "onBind: intent "+intent);
        Log.i(TAG, "onBind: data "+intent.getStringExtra("data"));
        return mDownloadBinder;
    }

    /**
     * 当与服务连接的所有组件都断开连接的时候调用(Called when all clients have disconnected
     * from a particular interface published by the service.
     * The default implementation does nothing and returns false.)
     *
     * @param intent 用来绑定服务的intent
     * @return 默认返回false
     */
    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, "onUnbind: ");
        Log.i(TAG, "onUnbind: intent "+intent.getStringExtra("data"));
        return super.onUnbind(intent);
    }

    /**
     * 当有新的组件绑定到服务的时候会调用该方法
     * @param intent 用来绑定服务的intent
     */
    @Override
    public void onRebind(Intent intent) {
        Log.i(TAG, "onRebind: ");
        super.onRebind(intent);
    }

    /**
     * 该方法在服务只在第一次启动/绑定的时候会调用
     */
    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate: ");
        /*//将该服务变成前台服务
        Intent intent = new Intent(this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
        Notification notification = new NotificationCompat.Builder(this)
                .setContentTitle("This is title")
                .setContentText("This is text")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
                .setContentIntent(pendingIntent)
                .build();
        startForeground(1,notification);*/
        super.onCreate();
    }

    /**
     *
     * 该方法在每次调用startService方法的时候都会调用,
     * 一旦执行此方法,服务即会启动并可在后台无限期运行。
     * 服务工作完成后,需要通过调用 stopSelf() 或 stopService() 来停止服务。
     *
     * @param intent 就是用来启动该服务的Intent,传递的数据可以通过该intent拿出来
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand: ");
        Log.i(TAG, "onStartCommand: "+intent);
        Log.i(TAG, "onStartCommand: data "+intent.getStringExtra("data"));
        Log.i(TAG, "onStartCommand: flags "+flags);
        Log.i(TAG, "onStartCommand: startId "+startId);
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 该方法在调用stopService/unbindService方法的时候会调用,服务停止运行
     */
    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy: ");
        super.onDestroy();
    }
}
  • MyIntentService
public class MyIntentService extends IntentService {

    private final String TAG = getClass().getSimpleName();

    /**
     * 必须实现的无参构造函数,且必须调用父类有参构造方法
     */
    public MyIntentService(){
        super("MyIntentService");
    }

    /**
     * 处理具体逻辑的抽象方法,该方法运行在子线程
     * @param intent 启动服务用的intent
     */
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.i(TAG, "onHandleIntent: ");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 服务销毁时会调用
     */
    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy: ");
        super.onDestroy();
    }
}
  • manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="cn.foxnickel.servicedemo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
        </service>
        <service android:name=".MyIntentService"/>

        <activity android:name=".SecondActivity">
        </activity>
    </application>
</manifest>

七. 注意

  1. 服务并不是运行在独立的进程中的,而是依赖于创建服务的应用程序进程,当创建该服务的进程被杀掉时,服务也会停止。
  2. 服务的所有代码都运行在主线程中,并不会自己开启线程。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 119,343评论 16 133
  • 本篇文章是继续上篇android图片压缩上传系列-基础篇文章的续篇。主要目的是:通过Service来执行图片压缩任...
    laogui阅读 3,888评论 5 62
  • 前言:本文所写的是博主的个人见解,如有错误或者不恰当之处,欢迎私信博主,加以改正!原文链接,demo链接 Serv...
    PassersHowe阅读 663评论 0 5
  • 本人初学Android,最近做了一个实现安卓简单音乐播放功能的播放器,收获不少,于是便记录下来自己的思路与知识总结...
    柳州刘阅读 14,860评论 2 40
  • 爱情,是经不起风雨的泡沫,轻轻一碰就会破碎。人们在爱情中动不动就山盟海誓,但承诺得再动听,最爱的还是自己。对方让自...
    阎丽律师阅读 204评论 0 0