Service

     Service是一个应用程序组件,它能够在后台执行一些耗时较长的操作,并且不提供用户界面。服务能被其他应用程序的组件启动,即使用户切换到另外的应用时,还能保持后台运行。此外,应用程序组件还能与服务绑定,并与服务进行交互,还能进行进程间通信(IPC)。比如,服务可以处理网络传输、音乐播放、执行文件I/O、或者与contenProvider进行交互,所有这些都是后台进行的。

     

     开启服务有2种方式,Service的生命周期也不一样:

     左图调用startService方式开启服务;右图调用bindService方式启动服务

     

 

自定义服务

public class MyService extends Service {

    @Override

    public void onCreate() {

        Log.d(Constant.TAG,"onCreate");

        super.onCreate();

    }

    @Override

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

        Log.d(Constant.TAG,"onStartCommand");

        return super.onStartCommand(intent, flags, startId);

    }

    @Override

    public IBinder onBind(Intent intent) {

        Log.d(Constant.TAG,"onBind");

        return new MyBinder();

    }

 

    @Override

    public boolean onUnbind(Intent intent) {

        Log.d(Constant.TAG,"onUnbind");

        return super.onUnbind(intent);

    }

 

    @Override

    public void onDestroy() {

        Log.d(Constant.TAG,"onDestroy");

        super.onDestroy();

    }

 

    private class MyBinder extends Binder{

 

    }

 

单一方式开启服务

  • startService方式开启服务

在Activity中调用下面的代码开启服务

Intent intent = new Intent(this,MyService.class);

startService(intent);

log日志输出

调用的方法是onCreate → onStartCommand

如果再次调用startService,日志输出

 

由此可见,onCreate方法只会调用一次,如果再次开启服务,只会调用onStartCommand()方法

 

调用stopService代码

Intent intent = new Intent(this, MyService.class);

stopService(intent);

                    日志输出:

                    执行的是onDestroy方法,服务被销毁。再次执行停止服务,没有任何日志输出,说明onDestroy方法只会执行一次,停止已经停止的服务不会报错。

 

总结:通过startService()方法开启服务,服务的生命周期 onCreate → onStartCommand →onDestroy;

PS:如果服务已经开启,再次调用startService,只会执行onStartCommand方法。

 

  • bindService方法开启服务

调用代码:

Intent intent = new Intent(this, MyService.class);

bindService(intent, new ServiceConnection() {

    @Override

    public void onServiceConnected(ComponentName name, IBinder service) {

        //当服务被连接,并且服务的onBind()方法返回的IBinder对象不为null时,执行

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

    }

 

    @Override

    public void onServiceDisconnected(ComponentName name) {

        //服务被意外销毁时执行,服务正常destroy时不执行该方法

        Log.d(Constant.TAG, "onServiceDisconnected");

    }

}, Service.BIND_AUTO_CREATE);

方法介绍

       public boolean bindService(Intent service,ServiceConnection conn,int flags);

第一个参数:意图,需要绑定的service

第二个参数:ServiceConnection,服务连接的中间类

第三个参数:flags。一般都传Service.BIND_AUTO_CREATE,意思是:绑定服务时,如果服务没有被开启,服务就会被自动开启起来

 

MyService中需要在onBind方法中返回IBinder对象。IBinder是一个接口,需要写一个类去实现该接口。由于该接口需要实现的方法较多,写起来不方便,Android提供了它的实现类Binder。只需写一个类去继承Binder即可。

 

                    调用绑定服务的日志输出

 

如果在该类中再次调用绑定该服务的方法,日志输出:

只会执行ServiceConnection中的onServiceConnected方法,并且不会报错

调用解除服务,日志输出:

先执行onUndind方法,在执行onDestroy方法

如果再次调用onUnbind方法,程序会报错

原因是服务没有注册。

PS:小技巧:调用unbindService方法时,加上try catch

 

总结:绑定服务的生命周期方法执行onCreate→ onBind→onUnbind → onDestroy ,而且每个方法只执行一次

 

如果在其他的地方绑定已经被绑定的service会出现什么结果?

测试代码的日志输出:

上面的结果中返回的IBinder对象的地址值相同,表明是同一个对象

由此可见:当服务绑定成功后,可以在其他地方再次绑定服务。不管在哪个地方再次绑定该服务,ServiceConnection中返回的IBinder对象是同一个。

但是需要注意的是,调用解除绑定服务的方法,必须是第一次调用bindService()方法的类中,否侧绑定服务失败,程序还会crash,错误的原因是服务没有注册,需要try catch。

 

 

调用服务中的方法

 

通过上面2个开启服务的方法,可以发现如果想调用服务里面的方法,只有通过绑定服务的方式。绑定服务成功后,会返回服务里面的内部类的IBinder对象,通过内部类来调用外部类(服务)中的方法,即可实现进程间的通讯。

如果直接将Service的实现IBinder内部类暴露给外界,这样就有点不符合封装的思想。可以通过接口的方式实现。代码如下:

 

1,定义一个IService的接口

public interface IService {

 

    void callServiceMethod();

 

}

2,MyService中的MyBinder实现方式:

 

public class MyService extends Service {

    @Override

    public void onCreate() {

        Log.d(Constant.TAG, "onCreate");

        super.onCreate();

    }

 

    @Override

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

        Log.d(Constant.TAG, "onStartCommand");

        return super.onStartCommand(intent, flags, startId);

    }

 

    @Override

    public IBinder onBind(Intent intent) {

        Log.d(Constant.TAG, "onBind");

        return new MyBinder();

    }

 

    @Override

    public boolean onUnbind(Intent intent) {

        Log.d(Constant.TAG, "onUnbind");

        return super.onUnbind(intent);

    }

 

    @Override

    public void onDestroy() {

        Log.d(Constant.TAG, "onDestroy");

        super.onDestroy();

    }

 

    //实现IService接口,暴露方法给外界调用

    private class MyBinder extends Binder implements IService {

        @Override

        public void callServiceMethod() {

            serviceMethod();

        }

    }

 

    private void serviceMethod() {

        Log.d(Constant.TAG, "method in My service is called ");

    }

}

 

在Activity中的调用:

Intent intent = new Intent(this, MyService.class);

bindService(intent, new ServiceConnection() {

    @Override

    public void onServiceConnected(ComponentName name, IBinder service) {

        //当服务被连接,并且服务的onBind()方法返回的IBinder对象不为null时,执行

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

        //将IBinder对象强转成IService对象,调用Service中的方法

        IService iService = (IService) service;

        iService.callServiceMethod();

    }

 

    @Override

    public void onServiceDisconnected(ComponentName name) {

        //服务被意外销毁时执行,服务正常destroy时不执行该方法

        Log.d(Constant.TAG, "onServiceDisconnected");

    }

}, Service.BIND_AUTO_CREATE);

 

  • 服务的混合开启方式

调用顺序     startService→bindService→unbindService→stopService 日志输入:

 

                    调用顺序     bindService→startService→stopService→unbindService 日志输出:

 

                    其他情况下的生命周期方法调用比较混乱。

                    由此可见开启和关闭服务需要使用配对的方法。即通过startService方法开启的服务只能通过stopService方法开关闭服务,通过bindService方法开启的服务需要调用unbindService方法关闭服务。

 

                    如果想服务长期在后台运行,同时又想调用服务中的方法,建议使用的混合开启的方式。

                    调用顺序 startService → bindService → unbindService → stopService

 

          上述都是调用本地服务的用法。


 

使用AIDL调用服务,即调用其他应用的Service中的方法来交互。

 

  • AIDL调用本地服务

在main目录下,新建一个aidl目录,再创建一个aidl文件

 

文件中的代码:

// IService.aidl

package com.lh.android.servicedemo;

 

// Declare any non-default types here with import statements

 

interface IService {

 

    void callServiceMethod();

}

编译后会在build目录下生产一个文件

 

打开该文件可以看到下面的代码

 

即Stub这个类继承了Binder类并且实现了IService接口,在Service中可以这样写

public class MyService extends Service {

    @Override

    public void onCreate() {

        Log.d(Constant.TAG, "onCreate");

        super.onCreate();

    }

 

    @Override

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

        Log.d(Constant.TAG, "onStartCommand");

        return super.onStartCommand(intent, flags, startId);

    }

 

    @Override

    public IBinder onBind(Intent intent) {

        Log.d(Constant.TAG, "onBind");

  //返回的是继承了IService.Stub类的对象

        return new MyBinder();

    }

 

    @Override

    public boolean onUnbind(Intent intent) {

        Log.d(Constant.TAG, "onUnbind");

        return super.onUnbind(intent);

    }

 

    @Override

    public void onDestroy() {

        Log.d(Constant.TAG, "onDestroy");

        super.onDestroy();

    }

 

    /**

     * MyBinder继承IService.Stub(抽象类),实现抽象的方法

     * IService.Stub类实现了IService接口,即需要实现该接口中的方法

     *

    private class MyBinder extends IService.Stub {

        @Override

        public void callServiceMethod() throws RemoteException {

            serviceMethod();

        }

    }

 

    private void serviceMethod() {

        Log.d(Constant.TAG, "method in My service is called ");

    }

}

 

     在Activity中的调用就变成了

Intent intent = new Intent(this, MyService.class);

bindService(intent, new ServiceConnection() {

    @Override

    public void onServiceConnected(ComponentName name, IBinder service) {

        //当服务被连接,并且服务的onBind()方法返回的IBinder对象不为null时,执行

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

        IService iService = IService.Stub.asInterface(service);

        try {

            iService.callServiceMethod();

        } catch (RemoteException e) {

            e.printStackTrace();

        }

    }

 

    @Override

    public void onServiceDisconnected(ComponentName name) {

        //服务被意外销毁时执行,服务正常destroy时不执行该方法

        Log.d(Constant.TAG, "onServiceDisconnected");

    }

}, Service.BIND_AUTO_CREATE);

     

     当然,如果在一个app里面想要调用另外一个app里面的服务,即调用远程服务,实现方式跟上面一模一样。

 

     注意:通过aidl调用远程服务时,aidl文件的包名要跟调用的服务的包名一致。

 

     aidl文件中的代码规则:不能有public等权限修饰符

     方法中如果有参数

  • 参数是基本数据类型(四类八种)的写法:直接表明数据类型即可。

interface IService {

    /**

     * Demonstrates some basic types that you can use as parameters

     * and return values in AIDL.

     */

    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,

            double aDouble, String aString);

}

  • 参数是引用数据类型(对象)

     需要该类实现 Parcelable 接口,示例如下:

 

Person类的定义如下:可以在该类中添加无参和带参的构造方法

public class Person implements Parcelable {

 

    private String name;

 

    public String getName() {

        return name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

    @Override

    public String toString() {

        return "Person{" +

                "name='" + name + '\'' +

                '}';

    }

 

    @Override

    public int describeContents() {

        return 0;

    }

 

    @Override

    public void writeToParcel(Parcel dest, int flags) {

        dest.writeString(name);

    }

 

    public void readFromParcel(Parcel in){

        setName(in.readString());

    }

 

    protected Person(Parcel in) {

        readFromParcel(in);

    }

 

    public static final Creator<Person> CREATOR = new Creator<Person>() {

        @Override

        public Person createFromParcel(Parcel in) {

            return new Person(in);

        }

 

        @Override

        public Person[] newArray(int size) {

            return new Person[size];

        }

    };

}

 

Person.aidl中的代码:

// Person.aidl

package com.lh.android.servicedemo;

 

parcelable Person;

注意:parcelable小写

 

IService中的代码

// IService.aidl

package com.lh.android.servicedemo;

import com.lh.android.servicedemo.Person;//注意一定要导包

// Declare any non-default types here with import statements

 

interface IService{

    /**

     * Demonstrates some basic types that you can use as parameters

     * and return values in AIDL.

     */

    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,

            double aDouble, String aString);

 

    //注意添加inout

    void setPerson(inout Person p);

 

    void setPersonList(inout List<Person> persons);

}

注意:

  1. 参数是引用数据类型的需要添加 inout;如果是集合也是一样的。
  2. 一定要导包(手动导包)。

 

特别注意:

  • 如果在同一个项目中使用aidl,并且定义的参数有引用数据类型,那么实体bean不能放在aidl目录下面,要放在java代码的目录下面,但是要保证与aidl文件的包名一致。
  • 如果不是在另外一个项目中调用本项目的服务,那么在另外一个项目中就可以把实体bean,bean的aidl文件以及要实现的aidl文件放在同一个包下,同时也要保证该包的包名与调用的服务中定义的aidl的包名是一致的。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值