Android四大组件之Service使用详解

Service作为安卓的四大组件之一,适合长期在后台运行而不需要用户界面的操作,Service默认运行在UI线程中,因此Service中同样不适合做耗时操作,如果需要做耗时操作需要开启子线程。
常用Service的开启包括start和bind两种启动方式。

  1. 创建自定义Service
public class ServiceTest extends Service {
    /**
     * 继承Service时必须要实现的方法
     *bind调用时,获取Intent传递的参数,或执行绑定后的操作
     * @param intent
     * @return
     */
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * Service首次运行时会执行该方法
     * 如果Service已经启动则不会调用(在onStartCommand()和onBind()之前调用)
     */
    @Override
    public void onCreate() {
        super.onCreate();
    }

    /**
     * 通过start调用时执行该方法,每调用一次,执行一次该方法
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * Service方法销毁时,执行该方法
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

以上演示代码的注释已很详细的讲解Service每个方法的含义,自定义Service其实很简单,只需要继承Service方法即可,然后根据业务需要重写在onStartCommand()或onBind()执行相应的操作。下面简述每个方法的含义。

  • onCreate():Service启动前调用的方法,在在onStartCommand()和onBind()之前调用,处理一些初始化的操作;
  • onBind():由于该方法在Service设置为abstract,因此继承Service必须实现的方法,同时也是通过bind调用时的回调方法;
  • onStartCommand():如果通过start方式启动,必须重写该方法,在该回调方法中处理相应的业务逻辑,每次调用都会执行该方法;
  • onDestroy():Service销毁时执行该方法,可处理一些资源关闭或释放的操作。

2 注册Service
作为四大组件之一,同样在使用Service的使用,需要继承Service类,重写onBind方法,并在AndroidManifest.xml中注册新创建的Servcie对象。

<service android:name=".ServiceTest"
            android:enabled="true"
            android:exported="true"
            android:isolatedProcess="true"
            android:process=":procressName"/>
  • name:表示新创建的Service的类名称;
  • enabled:是否被实例化,默认为true;
  • exported:表示该Service是否支持隐式调用,其默认值是由Service中是否包含intent-filter决定,如果包含intent-filter则默认为true,否则为false,即使设置为true也无法隐式调用,因为如果不添加intent-filter,当通过Intent调用时,安卓就无法知道具体那个控件(Service/Activity/Broadcast Receiver)来响应该请求;
  • isolatedProcess:如果设置为true,服务将会在特殊的进程中运行(独立应用的进程),通过start或bind与其进行数据通信;
  • process:进程设置,是否需要在单独的进程中运行,在设置该参数时需注意,如果设置为“:procressName”和“procressName”,前者表示“App-package:procressName”而后者表示“procressName”进程,两者表示不同的进程。

3 Service启动
Service的启动包括两种方式,一种是通过start方式,还有一种是bind方式,下面具体介绍该两种方式的不同含义。
在这里插入图片描述3.1 Bind方式

使用流程如下

  • 创建自定义Servcie类,重写onBind()对象,返回自定义的Binder类并传入自定义的Service对象;
  • 创建自定义Binder类,编写针对Service的处理方法;
  • 在bindServcie时,传入ServiceConnection对象,获取是否连接状态。

创建自定义的Service类,编写绑定成功后的方法类。

public class ServiceTest extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder(this);  //返回自定义的Binder类
    }

    /**
     * Service的处理方法
     * @param str
     */
    public void serviceMethod(String str){
        Log.i(TAG, "setMethod: str "+str);
    }

创建自定义Binder类,编写针对Service的处理方发

public class  MyBinder extends Binder {
    private static final String TAG = "MyBinder";
    private ServiceTest mServiceTest;

    public MyBinder(ServiceTest serviceTest) {
        mServiceTest = serviceTest;
    }
    //Binder操作后的方法
    public void testMethod(String str){
        Log.i(TAG, "testMethod: "+"XIAOHAN "+str);
        mServiceTest.serviceMethod(str);
        mMyBinderInterface.binderCall(str+"recall");
    }
    private MyBinderInterface mMyBinderInterface;

    public void setMyBinderInterface(MyBinderInterface myBinderInterface) {
        mMyBinderInterface = myBinderInterface;
    }

    public   interface MyBinderInterface {
        void binderCall(String msg);
    }
}

实际使用时,bindService时,需要传入一个ServiceConnection参数,在onServiceConnected可以获取到自定义的Binder类对象,通过该对象可以获取自定义Binder类中的回调处理方法,拿到自定义的Binder类后,说明bind成功,可通过binder对象,传入参数,间接调用自定义的Service中的处理方法。

ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mMyBinder = (MyBinder) service;
                mMyBinder.setMyBinderInterface(new MyBinder.MyBinderInterface() {
                    @Override
                    public void binderCall(String msg) {
                        Log.i(TAG, "XIAOHAN binderCall: "+msg);
                    }
                });
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };

        Intent intent = new Intent(MainActivity.this,ServiceTest.class);
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "XIAOHAN onClick: "+mMyBinder);
                mMyBinder.testMethod("hangzhou");
            }
        });

执行流程打印日志如下:

02-24 02:13:53.165 4172-4172/com.example.xiaohan.test22 I/MainActivity: onCreate: XIAOHAN serviceConnection
02-24 02:13:53.175 4172-4172/com.example.xiaohan.test22 I/MainActivity: onCreate: XIAOHAN bindService 
02-24 02:13:53.205 4172-4172/com.example.xiaohan.test22 I/ServiceTest: XIAOHAN onBind: 
02-24 02:13:53.205 4172-4172/com.example.xiaohan.test22 I/MyBinder: XIAOHAN MyBinder: 

执行顺序:创建serviceConnection对象–bindService—调用onBind方法----创建MyBinder对象

当点击onclick时,即执行mMyBinder.testMethod(“hangzhou”)时的执行日志如下

02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/MainActivity: XIAOHAN onClick: com.example.xk.test22.MyBinder@4a77e7f4
02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/MyBinder: testMethod: XIAOHAN hangzhou
02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/ServiceTest: XIAOHAN setMethod: hangzhou
02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/MainActivity: XIAOHAN binderCall: hangzhourecall

执行顺序:自定义Biner的执行方法–Service的绑定处理方法—ServiceConnection的onServiceConnected()回调

注意:如果在使用bind调用Service时,如果在AndroidManifest.xml中对绑定的组件(activity或servcie)设置了进程名,但未设置自定义的Service的相同的进程,则会报如下错误:

java.lang.ClassCastException:android.os.BinderProxy

处理的方法见该文章

3.2 Start方式
可通过其他组件(如Activity)调用startServce方法来开启Service,每次调用一次start方法,都会回调一次 onStartCommand()方法,但只要执行一次stopService方法,就会关闭service方法,因此该执行的顺序是:

onCreate()—onStartCommand() (n次)—onDestroy()

对于该流程的操作相信大家都已熟悉,但实际使用中我们更多的是关注onStartCommand()方法的使用。

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

在查看该方法前,我们先通过查看源代码,获取该方法的参数类型和返回值类型,点击源码,可以查看如下代码块,通过该代码块可以看到我们需要的参数类型。

public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
        onStart(intent, startId);
        return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;

方法参数的设置:

  • Intent:启动Service传递过来的Intent对象,也可通过Intent传递数值;
  • flags:包括两个参数:START_FLAG_REDELIVERY和START_FLAG_RETRY,其中START_FLAG_REDELIVERY:表示之前已经传递过Intent信息,但是由于Service被kill,再次重传时保留之前Intent的值,表示之前的传递还未结束,START_FLAG_RETRY:如果onStartCommand()回调一直没有返回值会重新调用onStartCommand()方法;
  • startId:指明当前Service的唯一对象,与stopSelfResult (int startId)方法配合使用,可更安全的停止服务。

返回值的类型:

  • START_STICKY_COMPATIBILITY:与START_STICKY类似,主要是兼容低版本的作用;
  • START_STICKY:线程被杀死,会尝试再次创建该服务,并会回调onStartCommand()方法,但Intent参数可能为空,因此回调该方法时需要做非空判断;
  • START_NOT_STICKY:线程被杀死时,不会尝试重启该服务,除非程序检测被杀后,重新开启,但已经不是被杀调之前的状态了(创建新的服务对象);
  • START_REDELIVER_INTENT:如果线程被杀,重新创建,并传递最后一个服务传递的Intent的值调用onStartCommand()方法,与START_STICKY不同,该传递的Intent值是非空的。

startService启动Service的方式包括两种,一种是通过显示启动,一种是通过隐式启动,两种启动方式分析如下
1.显示启动:

在需要的地方启动该服务。

Intent intent = new Intent(MainActivity.this, ServiceTest.class);
//        intent.putExtra("xiaohan","杭州");//添加数据
        Bundle bundle = new Bundle();   //通过Bundle方式传递
        bundle.putString("xiaohan","杭州");
        intent.putExtras(bundle);
        startService(intent);

在自定义的Service的onStartCommand方法中处理回调。

//回调中处理该数据
 @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
       /* String xiaohan = intent.getStringExtra("xiaohan");
        Log.i(TAG, "onStartCommand: "+xiaohan);*/
        Bundle extras = intent.getExtras();
        String xiaohan = extras.getString("xiaohan");
        Log.i(TAG, "onStartCommand: "+xiaohan);
        return START_STICKY;
    }

该方式是我们比较常见的启动方式,启动时可通过Intent传递参数(支持基本数据类型(及其对应的数组)、String和序列化后的对象),如果包含多个参数,可通过Bundle传递,但需要注意通过该种方式传递的数据不能太大(不超过1M)。

2.隐式启动:
在AndroidManifest.xml中声明自定义的Service,添加action和category。

 <service android:name=".ServiceTest">
            <intent-filter >
                <action android:name="com.example.xiaohan.service"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>

采用Bind的方式

ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mMyBinder = (MyBinder) service;
                mMyBinder.setMyBinderInterface(new MyBinder.MyBinderInterface() {
                    @Override
                    public void binderCall(String msg) {
                        Log.i(TAG, "XIAOHAN binderCall: "+msg);
                    }
                });
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };
		 Intent service = new Intent();
        service.setAction("com.example.xiaohan.service"); //service 的 action 值
        service.setPackage("com.example.xiaohan.test22"); //远程服务所在包名
        //绑定服务
        bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
		 //启动服务
     // startService(service);

猜你喜欢

转载自blog.csdn.net/xk7298/article/details/88146408
今日推荐