Service的两种启动方式


Android学习整理 - 系列


目录:

Service定义

官方对服务的解释
通俗的解释

服务能做什么

服务的生命周期

通常服务有两种形式

创建一个最简单的服务

服务的回调函数详解

一 启动型startService
二 绑定Service

创建一个前台服务

IntentService(异步的,会自动停止的服务)

注意事项

实战

  • 音乐播放器
  • 文件下载器

Service定义

官方对服务的解释

  • Android四大组件之一
  • Service是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。
  • 服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。
  • 组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)

通俗的解释

  • 服务是Android中实现程序后台的解决方案,不依赖任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,服务还能保持运行
  • 服务非常适合那些不需要和用户交互而且还要求长期运行的任务
  • 服务依赖于创建服务是所在的应用程序进程。当宿主应用进程被杀,所有依赖该进程会马上停止运行
  • 服务像Activity那样也是默认运行在主线程中,如果有耗时任务还是要在服务内部创建子线程,不然程序会GG。

服务能做什么

  • 用于处理网络事务(下载文件)
  • 播放音乐(音乐播放器)
  • 执行文件I/O(读写文件)
  • 与内容提供器进行交互

服务的生命周期

  • 服务像Activity那样有自己的生命周期,但是没有Activity那么复杂
左图显示了使用startService()所创建的服务的生命周期, 右图显示了使用 bindService()所创建的服务的生命周期。

通常服务有两种形式

启动

应用组件(如 Activity)通过调用 startService() 启动服务,以无限期运行

  • 一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。
  • 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。(通俗的说就是逻辑控制写死在Service里了,不能用别的应用组件控制,孤独走完自己的生命周期,而且还是被父母规划好的

绑定

应用组件通过调用 bindService() 绑定到服务(其实这个时候也启动了服务)

  • 提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 (可以控制的,基本靠自己行走江湖。盲仔:我用双手成就梦想)
  • 仅当与另一个应用组件绑定时,绑定服务才会运行。
  • 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

需要注意的是服务可以同时以这两种方式运行,看上面的服务的生命周期可以看出,问题只是在于你是否实现了一组回调方法
onStartCommand()(允许组件启动服务)和 onBind()(允许绑定服务)


创建一个最简单的服务

  • ** 启动startService()形式**

新建一个类MyService

public class MyService extends Service {
   
    private static final String TAG = "MyService";
   
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

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

}

Activity中MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MainActivity";
    private Button mStartService;
    private Button mStopService;

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

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.button_start_service:
                Intent startIntent = new Intent(MainActivity.this, MyService.class);
                //startService启动形式
                startService(startIntent);
                break;
            case R.id.button_stop_service:
                Intent stopIntent = new Intent(MainActivity.this, MyService.class);  
                //“启动”服务的停止
                stopService(stopIntent);
                break;
        }
    }

    private void iniView() {
        mStartService = (Button) findViewById(R.id.button_start_service);
        mStopService = (Button) findViewById(R.id.button_stop_service);
        mStartService.setOnClickListener(this);
        mStopService.setOnClickListener(this);
    }
}

记得在AndroidManifest文件中注册服务

<service android:name=".service.MyService"/>

布局文件是2个按钮mStartService ,mStopService

这样子最简单的 “启动”服务就写好了,看下

“启动”服务

模拟器采用的是Android7.0的分屏功能,上面是我们的demo,下半部分是开发者选项里的正在运行的服务

可以看到,我们点击startService时,Activity启动了“启动”服务,开始跑“启动”服务的生命周期onCreate()-->onStartCommand(),同时正在系统多了个MyService的服务

当我们按下stopService后,生命周期走到了尽头onDestroy(),同时系统MyService服务消失


  • ** 绑定bindService()形式**

刚刚的“启动”服务里面实现的onBind方法返回的是null,
这是“启动”服务和“绑定”服务的的区别所在

有3种,分别是扩展 Binder 类(这里用的,也是常用的)
使用 Messenger,使用 AIDL(这两种以后再更新)

首先在服务MyService里面新建一个内部类MyBinder

public class MyBinder extends Binder {
        public void StartBB() {

            Log.d(TAG, "StartBB:BBBBBBBBBB");
        }
    }

然后在MyService里直接实例化

private MyBinder mBinder = new MyBinder();

然后将onBind的返回值改为MyBinder的对象mBinder

接着在Activity中添加ServiceConnection 的实现,其实就是Activity与Service合作的纽带,后面会细说

private ServiceConnection mConnection = new ServiceConnection() {

        //bind服务,onCreate之后
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            
            mBinder = (MyService.MyBinder) binder;
            mBinder.StartBB();

            Log.d(TAG, "onServiceConnected");
        }
        //unBind服务时,在onDestroy之前
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected");
        }
    };

初始化一下MyBinder
private MyService.MyBinder mBinder;

添加俩个按钮去控制“绑定服务”

            case R.id.bind_service:
                Intent bindIntent = new Intent(MainActivity.this, MyService.class);
                //这里的三个参数后面会细说
                bindService(bindIntent, mConnection, BIND_AUTO_CREATE);
                break;
            case R.id.unbind_sservice:
                    //多次取消绑定程序会crash
                    unbindService(mConnection);
                break;

然后看

bindService生命周期

可以看到生命周期 为onCreate-->onBind-->onDestroy
onServiceConnected是ServiceConnection纽带的回调


服务的回调函数

一 启动型startService

  • onCreate与onDestroy和Activity一样,
onStartCommand

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

1 intent

这个intent是startService(intent)中的intent,在创建启动的服务所在的组件中如果需要传递给Service中数据,可以将要传递的数据放放到这个Intent里面

2 flags

服务被系统杀死后的重新启动方式,有3种

  • START_NOT_STICKY(非粘性启动,系统杀死后不再重新创建该Service)

如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务

  • START_STICKY(粘性启动,默认的返回值

    如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent(意思就是说此时intent的数据已经是null了)。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。、

    适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。

  • START_REDELIVER_INTENT(再交付)

    如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务(就是需要重启,需要保留Intent的服务

3 startId

每次创建启动Service的唯一身份ID,每次每次startService,这个startId均不相同

  • 用于处理多个onStartCommand请求时,关闭Service时使用

创建启动的Service实际使用业务逻辑
  • 在onStartCommand中开启子线程进行耗时操作
  • 子线程结束用stopSelf(startId)(在本Service中)杀死自己这个Service
  • 或者另外在别的组件中用stopService结束Service

二 绑定Service

1 onBind

Return the communication channel to the service. May return null if clients can not bind to the service.
返回通信的类给服务,如果Binder没有绑定到Service可以返回null

2 Binder的子类
public class MyBinder extends Binder {
       // 内部业务逻辑,别的组件(Activity)可以调用
      }
3 ServiceConnection类的实现
  • ServiceConnection的对象其实就是应用组件(这里是Activity)与Service的纽带,实现方法有俩个
onServiceConnected()//bind服务时,onCreate之后
onServiceDisconnected()//unBind服务时,在onDestroy之前
4 bindService(Intent service, ServiceConnection conn, int flags)

-flags

flags种类

BIND_AUTO_CREATE:表示在Activity和Service建立关联后自动创建Service,这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行

其他的基本用不上。


创建一个前台服务

只要在onCreate方法中添加

manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        Notification notification = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("标题")
                .setContentText("内容")
                .build();
        manager.notify(1, notification);
        //启动前台服务
        startForeground(1, notification);```

3.gif

全部代码挂一下
  • MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MainActivity";
    private Button mStartService;
    private Button mStopService;
    private Button BindService;
    private Button unBindService;
    private MyService.MyBinder mBinder;

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


    }


    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            mBinder = (MyService.MyBinder) binder;
            mBinder.StartBB();

            Log.d(TAG, "onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected");
        }
    };


    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.button_start_service:
                Intent startIntent = new Intent(MainActivity.this, MyService.class);
                startService(startIntent);
                break;
            case R.id.button_stop_service:
                Intent stopIntent = new Intent(MainActivity.this, MyService.class);
                stopService(stopIntent);
                break;
            case R.id.bind_service:
                Intent bindIntent = new Intent(MainActivity.this, MyService.class);
                bindService(bindIntent, mConnection, BIND_AUTO_CREATE);
                break;
            case R.id.unbind_sservice:
                    unbindService(mConnection);
                break;
        }
    }


    private void iniView() {
        mStartService = (Button) findViewById(R.id.button_start_service);
        mStopService = (Button) findViewById(R.id.button_stop_service);
        BindService = (Button) findViewById(R.id.bind_service);
        unBindService = (Button) findViewById(R.id.unbind_sservice);
        mStartService.setOnClickListener(this);
        mStopService.setOnClickListener(this);
        BindService.setOnClickListener(this);
        unBindService.setOnClickListener(this);
    }
}

  • MyService
public class MyService extends Service {
    private MyBinder mBinder = new MyBinder();
    private static final String TAG = "MyService";
    NotificationManager manager;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
//        return null;
        Log.d(TAG, "onBind");
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        Notification notification = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("标题")
                .setContentText("内容")
                .build();
        manager.notify(1, notification);
        startForeground(1, notification);
        Log.d(TAG, "onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        manager.cancel(1);
        Log.d(TAG, "onDestroy");
    }


    public class MyBinder extends Binder {
        public void StartBB() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //处理具体的逻辑
                    stopSelf();
                }
            }).start();
            Log.d(TAG, "StartBB:BBBBBBBBBB");
        }
    }
}

这里要导入v4包


IntentService(异步的,会自动停止的服务)

从上面知道,服务默认是运行在主线程中的,如果直接在服务中处理一些耗时的逻辑,就可能会出现ANR,所以我们一般都在服务的具体方法里开启一个新的线程去处理具体的逻辑,然后,这种类型的服务一旦启动后,就会一直运行,要想停止服务就得调用stopSeft()

然后Android专门提供了IntentService来简单的创建异步,自动停止的服务

然后我们写个简单的demo,
新建一个IntentServiceDemo类

public class IntentServiceDemo extends IntentService {
    private static final String TAG = "IntentServiceDemo";
    /**
     *  必要的,这里不写manifest文件会报错
     *
     * */
    public IntentServiceDemo() {
        super(TAG);//调用有参构造方法
    }


    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public IntentServiceDemo(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "IntentServiceDemo当前线程:" + Thread.currentThread().getId());
    }

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

MainActivity中直接:

         Log.d(TAG, "MainActivity当前线程:" + Thread.currentThread().getId());
        Intent intentService = new Intent(this, IntentServiceDemo.class);
        startService(intentService);
debug

可以看到,Activity线程与服务不一样,并且逻辑完服务就GG了


注意事项

  • 服务是一种即使用户未与应用交互也可在后台运行的组件。 因此,您应仅在必要时才创建服务

  • 如需在主线程外部执行工作,不过只是在用户正在与应用交互时才有此需要,则应创建新线程而非服务。 例如,如果您只是想在 Activity 运行的同时播放一些音乐,则可在 onCreate() 中创建线程,在 onStart() 中启动线程,然后在 onStop() 中停止线程。您还可以考虑使用 AsyncTask 或 HandlerThread,而非传统的 Thread 类。

  • 为了确保应用的安全性,请始终使用显式 Intent 启动或绑定 Service,且不要为服务声明 Intent 过滤器。

  • 长时间运行服务老被杀死(耗电)

1.提高进程优先级,使用 setForeground(true)
2.onStartCommand返回START_STICKY
3.设置属性android:persistent=true(设置为永久性应用,系统启动的时候就会启动)
4.在onDestory中重新启动service
5.开启单独进程,也就是指定android:process

  • 小米手机针对onStartCommand的flags启动模式

小米,魅族,华为等手机针对此处做了一定的修改。在“自启动管理”中有一个自启动应用列表,默认情况下,只有少应用(如微信、QQ、YY、360等)默认是可以自启动的,其他应用默认都是禁止的。用户可以手动添加自启动应用,添加后的应用中如果Started Service onStartCommand(...)回调返回值是START_STICKY或START_REDELIVER_INTENT,当用户在手机上长按Home键结束App后,接下来未来的某个时间内,当系统内存足够可用时,Service依然可以按照上述规定重启。当然,如果用户在 设置 >> 应用 >> 强制kill掉App进程,此时Service是不会重启的。


实战

Service 与 MediaPlayer学习后音乐播放器的实现


参考 自

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

推荐阅读更多精彩内容