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

推荐阅读更多精彩内容