Android四大组件之Service组件

什么是Service

Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。服务可由其他应用组件启动(如Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行,Service状态基本上分为两种形式:

    启动状态

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

    绑定状态

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

Activity与Service的对比

相似点

  • 都是单独的Android组件
  • 都拥有独立的生命周期
  • 都是Context的派生类,所以可以调用Context类定义的如getResources()、getContentResolver()等方法
  • 都拥有自己生命周期回调方法

不同点

  • Activity运行于前台有图形用户界面,负责与用户交互;Service通常位于后台运行,不需要与用户交互,也没有图形用户界面。

应用场景
如果某个程序组件需要在运行时向用户呈现界面,或者程序需要与用户交互,就需要用Activity,否则就应该考虑使用Service了。

创建一个Service后台服务

后台服务分为可交互服务和不可交互服务。其区别在于启动服务的方式StartService()bindService()。后者会返回Bind对象供Service中方法和处理结果,而前者不会

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

/**
定义一个类继承Service即可
*/
public class FirstService extends Service {
    /**
     * Service子类必须实现的方法。该方法返回一个IBinder对象,应用程序可通过IBinder对象与Service组件通信
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("===================onBind FirstService=================");
        return null;
    }

    /**
     * 当Service上绑定的所有客户端都断开连接时会回调该方法
     * @param intent
     * @return
     */
    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("===================onUnbind FirstService=================");
        return super.onUnbind(intent);
    }

    /**
     * Service第一次被创建后回调该方法
     */
    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("===================onCreate FirstService=================");
    }

    /**
     * Service被关闭之前回调该方法
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("===================onDestroy FirstService=================");
    }

    /**
     * 该方法的早期版本是onStart(Intent intent, int startId),
     * 当客户端调用startService(Intent)方法启动Service时都会回调该方法
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("===================onStartCommand FirstService=================");
        return super.onStartCommand(intent, flags, startId);

    }
}


将上面的服务注册进Manifest.xml

<service android:name="com.javayihao.demo.TestService">
            <!-- 配置intent-filter元素说明该Service可被哪些Intent启动,可选 -->
            <intent-filter>
                <!-- 为intent-filter配置action -->
                <action android:name="com.javayihao.demo.TEST_SERVICE" />
            </intent-filter>
        </service>

启动和停止Service 

第一种

通过Context的startService()方法启动Service,访问者与Service之间没有关联,Service和访问者之间也无法进行通信、数据交换。即使访问者退出,Service依然运行:

特点:每当Service被创建时会回调onCreate方法,每次Service被启动时都会回调onStartCommand方法。多次启动一个已有的Service组件将不会再回调onCreate方法,但每次启动时都会回调onStartCommand方法。

        Intent intentService = new Intent(this, FirstService.class);
        intentService.setAction("com.javayihao.demo.TEST_SERVICE");
        //启动Service
        startService(intentService);
        //停止Service
        //stopService(intentService);

第二种

TestService

public class TestService extends Service {
    //定义onBinder方法要返回的Binder对象
    private MyBinder binder = new MyBinder();
    // 通过继承Binder来实现IBinder类
    public class MyBinder extends Binder {
        public int getCount() {
            return 111;
        }
    }
    /**
     * service子类必须实现的方法。该方法返回一个IBinder对象,应用程序可通过IBinder对象与Service组件通信
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("===================onBind TestService=================");
        return binder;//返回一个对象
    }
    /**
     * 当Service上绑定的所有客户端都断开连接时会回调该方法
     * @param intent
     * @return
     */
    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("===================onUnbind TestService=================");
        return super.onUnbind(intent);
    }

    /**
     * Service第一次被创建后回调该方法.用于初始化服务,只会被调用一次
     */
    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("===================onCreate TestService=================");
    }

    /**
     * Service被关闭之前回调该方法
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("===================onDestroy TestService=================");
    }

    /**
     * 该方法的早期版本是onStart(Intent intent, int startId),
     * 当客户端调用startService(Intent)方法启动Service时都会回调该方法
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("===================onStartCommand TestService=================");
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 耗时操作
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }
}

 启动

public class MainActivity extends AppCompatActivity {
    private  ServiceConnection connection;
    //启动Service时返回的IBinder对象
    TestService.MyBinder binder;
    //绑定服务
    public void  bindTestService(){
        Intent intent = new Intent(this, TestService.class);
        if(connection==null){
            ServiceConnection connection =new ServiceConnection() {
                //当与服务连接调用
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    binder =  (TestService.MyBinder)service;
                    System.out.println("=============与TestService服务连接===========");
                }

                //当与服务断开调用
                @Override
                public void onServiceDisconnected(ComponentName name) {
                    System.out.println("============与TestService服务断开===============");
                }
            };
            //进行绑定
            bindService(intent,connection, Context.BIND_AUTO_CREATE);
        }else{
            System.out.println("============已经绑定");
        }
    }
    //解除绑定
    public void unbindTestService(){
        if(connection!=null){
            unbindService(connection);
            connection=null;
            System.out.println("============解除绑定==============");
        }else{
            System.out.println("============还没有绑定==============");
        }
    }
    //Activity
    @Override
    public void  onDestroy() {

        super.onDestroy();
        if(connection!=null) {
            unbindService(connection);//死亡之前调用
            connection = null;
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        bindTestService();
       // unbindTestService();
    }

ServiceConnection对象的onServiceConnected方法中有一个IBinder对象,该对象可实现与被绑定Service之间的通信。在开发Service类时,该Service类必须提供一个IBinder onBind(Intent intent)方法,在绑定本地Service的情况下,onBind(Intent intent)方法所返回的IBinder对象将会传给ServiceConnection对象里onServiceConnected(ComponentName name, IBinder service)方法的service参数,访问者就可通过该IBinder对象与Service进行通信。在实际开发中通常会采用继承Binder(IBinder的实现类)的方式实现自己的IBinder对象。

对于Service的onBind()方法所返回的IBinder对象来说,它可被当成该Service组件所返回的回调对象,Service允许客户端通过该IBinder对象来访问Service内部的数据,这样即可实现客户端与Service之间的通信。

执行流程

Service的生命周期

上文的启动Service的两个示例中,我们都有对Service的生命周期方法进行观测。我们发现根据启动方式的不同Service回调的生命周期方法也不同。

Service的生命周期

总结一下Service的生命周期方法回调:

startService()和bindService()是两个独立的操作,我们可以只启动不绑定;也可以通过bindService()方法的第三个参数,在绑定时启动;还可以先启动后绑定;
①被启动的服务的生命周期:如果一个Service被某个Activity调用Context.startService方法启动(Service也可以启动服务),那么不管是否有Activity使用bindService绑定或者unbindService解除绑定该Service,该Service都在后台运行。如果一个Service被startService方法多次启动,那么onCreate方法只会调用一次,但是onStartCommand将会每次都被回调,并且系统只会创建Service的一个实例(因此你只需要调用一次stopService来停止)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,Android系统也可能结束服务。

②被绑定的服务的生命周期:如果一个Service被某个Activity调用Context.bindService方法绑定启动,不管调用bindService调用几次,onCreate方法都只会调用一次,同时onStartCommand方法始终不会调用。当链接建立之后,Service将会一直运行,除非调用Context.unbindService断开连接或者之前调用bindService的Context不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。

③被启动又被绑定的服务的生命周期:如果一个Service先被启动,后又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStartCommand就会调用多少次。调用unbindService将不会停止Service,而必须调用stopService或Service自身的stopSelf来停止服务(在没有解绑的前提下使用stopService是无法停止服务的)。

④当服务被停止时的清除工作:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(通过bindService启动))时,onDestroy方法将会被回调,在这里应当做一些清除工作(如停止Service中创建并运行的线程、注册的侦听器、接收器等)。

使用Service时的注意事项

①在调用bindService绑定到Service的时候,就应当保证在某处调用unbindService解除绑定(尽管Activity被finish的时候会自动解除绑定,并且会自动停止Service);

②使用startService启动服务之后,一定要使用stopService停止服务,不管你是否使用bindService;

③同时使用startService和bindService时要注意:Service的终止,需要unbindService与stopService同时调用,才能终止Service。关于调用顺序,如果先调用unbindService此时服务不会自动终止,再调用stopService服务才会停止;如果先调用stopService此时服务也不会终止,需要再调用unbindService(或者调用bindService的Context不存在了,如Activity被finish的时候)服务才会自动停止。

④当旋转手机屏幕的时候,如果发生了Activity的重建,旋转之前的使用bindService建立的连接便会断开(Context不存在了)。

⑤在sdk 2.0之前的版本,使用的onStart方法被onStartCommand方法替换了,但是onStart方法仍然有效。

前台服务

由于后台服务优先级相对比较低,当系统出现内存不足的情况下,它就有可能会被回收掉,所以前台服务就是来弥补这个缺点的,它可以一直保持运行状态而不被系统回收。例如:墨迹天气在状态栏中的天气预报

前台服务

IntentService

服务的代码默认运行在主线程里的。如果要在服务里面执行耗时操作的代码,就需要开启一个主线程去处理这些代码。比如在启动和停止Service的例子中,IntentService是专门用来解决Service中不能执行耗时操作这一问题的,创建一个IntentService也很简单,只要继承IntentService并覆写onHandlerIntent函数,在该函数中就可以执行耗时操作了。

IntentService的使用步骤
1、继承IntentService创建自定义的IntentService类

import android.app.IntentService;
import android.content.Intent;


public class BindIntentService extends IntentService {
    private boolean quit = false;
    private int count = 0;

    public BindIntentService() {
        super("BindIntentService");
    }

    /**
     * 必须实现的抽象方法,我们的业务逻辑就是在这个方法里面去实现的
     * 方法在子线程运行,我们不用去关心ANR的问题
     * 在OnCreate方法里面创建并启动子线程,
     * 在OnStartCommand方法里面,将Intent封装成Message并传递到子线程的handler,然后回调onHandleIntentonStart
     *
     * @param intent
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        while (!quit) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count++;
            System.out.println("==================Current count:" + count + "====================");
            if (count == 10)
                quit = true;
        }
    }

    /**
     * 为了验证onHandleIntent执行后,服务会不会自动销毁,我们在这里重写onDestroy方法
     * 如果会自动销毁,那么在"IntetnService Running"出现后,应该会出现"IntetnService Stop"
     */
    @Override
    public void onDestroy() {
        System.out.println("==================BindIntentService onDestroy:" + count + "====================");
        //经测试,如果onHandleIntent里面的代码逻辑没有走完,在服务外部调用stopService来停止服务,并不会立即结束子线程
        quit = true;
        super.onDestroy();
    }
}

2、在Manifest里面注册IntentService,注册方式跟Service一样

3、启动IntentService
我们推荐使用startService(intent)的方式来启动服务。

注意!!!也可以使用bindService的方式来启动服务。但是在使用bindService的启动的时候,即使onHandleIntent里面的逻辑执行完毕,也不会自动销毁服务。原因应该是Service还是被绑定状态,调用stopSelf无法停止。所以如果使用bindService启动服务将会失去IntentService的一大特点,使用时请谨慎.

4、销毁IntentService
IntentService的一大特点就是onHandleIntent里面的代码逻辑执行完之后,自动销毁Service。所以我们可以不用专门做停止IntentService的操作。

注意!!!我们也可以在客户端手动调用stopService来销毁服务,但是用这种方式不会停止IntentService里面启动的子线程。如果要采用这种方式销毁服务,一定要注意子线程无法停止,从而导致内存泄漏的问题。

系统服务

系统服务提供了很多便捷服务,可以查询Wifi、网络状态、查询电量、查询音量、查询包名、查询Application信息等等等相关多的服务,具体大家可以自信查询文档,这里举例2个常见的服务。

wifi

WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
boolean enabled = wm.isWifiEnabled();

最大音量

AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
int max = am.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);

参考连接:

https://blog.csdn.net/javazejian/article/details/52709857

https://www.jianshu.com/p/d48b386225fc

https://www.jianshu.com/p/d4d147fd8549

https://blog.csdn.net/geyunfei_/article/details/78851024

发布了260 篇原创文章 · 获赞 112 · 访问量 26万+

猜你喜欢

转载自blog.csdn.net/qq_34491508/article/details/103961678