三、基本组件(二)深入理解Service

来源:互联网 发布:荣威 知乎 编辑:程序博客网 时间:2024/06/15 22:16

  • 简介
  • 服务的基本用法
    • 1 定义一个服务
    • 2 启动和停止服务
    • 3 绑定本地Service并与之通信
  • Service的生命周期
  • Service的使用技巧
    • 1 使用前台服务
    • 2 使用IntentService
    • 3 IntentService总结

1. 简介

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

2. 服务的基本用法

2.1 定义一个服务

步骤1:创建一个MyService的类,继承自Service

public class MyService extends Service {    public static final String TAG = "MyService";    @Override    public void onCreate() {        super.onCreate();        Log.d(TAG, "onCreate() executed");    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.d(TAG, "onStartCommand() executed");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        super.onDestroy();        Log.d(TAG, "onDestroy() executed");    }    @Override    public IBinder onBind(Intent intent) {        return null;    }}

       onCreate、onStartCommand和onDestory这三个方法是每个服务中最常用的方法了。其中onCreate方法会在服务创建的时候调用,onStartCommand方法会在每次服务启动的时候调用,onDestory方法会在服务销毁的时候调用。

onCreate方法:首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或onBind() 之前)。如果服务已在运行,则不会调用此方法,该方法只调用一次

onStartCommand方法:当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果自己实现此方法,则需要在服务工作完成后,通过调用 stopSelf() 或 stopService() 来停止服务。(在绑定状态下,无需实现此方法。)

onDestory方法:当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。

onBind方法:当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。无论是启动状态还是绑定状态,此方法必须重写,但在启动状态的情况下直接返回 null。

步骤2:每一个服务都需要在AndroidManifest文件中进行注册才能生效:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android";    package="com.example.servicetest">    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >    ……        <service android:name="com.example.servicetest.MyService" >        </service>    </application></manifest>

       通过以上的两个步骤即可完成服务的定义,接下来就是启动或绑定等调用服务的操作了。

2.2 启动和停止服务

       启动与停止服务均是通过Intent来实现,核心代码如下:

Intent intent = new Intent(this,MyService.class);//启动指定服务startService(intent);//停止指定服务stopService(intent);

2.3 绑定本地Service并与之通信

       当程序通过startService和stopService启动关闭service时,service与访问者之间基本不存在太多的关联,因此当service和访问者之间需要进行方法调用或交换数据,则应该使用bindService和unBindService方式启动、关闭service。

步骤1:创建Service,并在其中实现onBind方法

public class BindService extends Service {    private int count;    private boolean quit;    //定义onBinder方法所返回的对象    private MyBinder mBinder = new MyBinder();    //通过继承Binder来实现IBinder类    public class MyBinder extends Binder{        public int getCount(){            //获取service的运行状态:count            return count;        }    }    //必须实现的方法,绑定该service时回调该方法    @Nullable    @Override    public IBinder onBind(Intent intent) {        System.out.println("service is Binded");        //返回IBinder对象        return mBinder;    }    //service被创建时回调该方法    @Override    public void onCreate() {        super.onCreate();        System.out.println("service is created");        //启动一条线程,动态地修改country的状态值        new Thread(){            @Override            public void run() {                while (!quit){                    try {                        Thread.sleep(1000);                    }catch (InterruptedException e){                    }                    count++;                }            }        }.start();    }    //service被断开连接时回调该方法    @Override    public boolean onUnbind(Intent intent) {        System.out.println("service is unbinded");        return true;    }    //service被关闭之前回调该方法    @Override    public void onDestroy() {        super.onDestroy();        this.quit = true;        System.out.println("service is Destroyed");    }}

步骤2:定义一个Activity,并在其中绑定定义好的service

public class BindServiceActivity extends Activity {    Button bindBtn, unbindBtn, getServiceStatusBtn;    //保持所启动的service的IBinder对象    BindService.MyBinder mBinder;    //定义一个serviceConnection对象    private ServiceConnection mConnection = new ServiceConnection() {        //当该Activity与Service连接成功时回调该方法        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            System.out.println("service is connected");            //获取service的onBind方法所返回的MyBinder对象            mBinder = (BindService.MyBinder) service;        }        //当Activity与Service断开连接时候回调该方法        @Override        public void onServiceDisconnected(ComponentName name) {            System.out.println("service is disconnected");        }    };    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_bind_service);        bindBtn = (Button) findViewById(R.id.btn_bind);        unbindBtn = (Button) findViewById(R.id.btn_unbind_service);        getServiceStatusBtn = (Button) findViewById(R.id.btn_get_service_status);        final Intent intent = new Intent(this, BindService.class);        bindBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //绑定指定的service                bindService(intent, mConnection, Service.BIND_AUTO_CREATE);            }        });        unbindBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //解除绑定service                unbindService(mConnection);            }        });        getServiceStatusBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //获取并显示service的count值                Toast.makeText(BindServiceActivity.this, "service count is " + mBinder.getCount()                        , Toast.LENGTH_SHORT).show();            }        });    }}

注意:
       startService( )方法在创建实例之后可以多次调用,而bindService( )方法并不会执行重复绑定。也就是说,startService( )中,系统会调用service的onStartCommand( )方法一次;但是,多次执行bindService( ),系统也只会回调service的onBind( )方法一次。

3. Service的生命周期

       以下是Service生命周期的执行顺序,左图显示了使用startService所创建的服务的生命周期,右图显示了使用bindService所创建的服务的生命周期。
这里写图片描述

       服务的整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束。与 Activity 类似,服务也在 onCreate() 中完成初始设置,并在 onDestroy() 中释放所有剩余资源。例如,音乐播放服务可以在 onCreate() 中创建用于播放音乐的线程,然后在 onDestroy() 中停止该线程。
       无论服务是通过 startService() 还是 bindService() 创建,都会为所有服务调用 onCreate() 和 onDestroy() 方法。
       服务的有效生命周期从调用 onStartCommand() 或 onBind() 方法开始。每种方法均有 Intent 对象,该对象分别传递到 startService() 或 bindService()。
       对于启动服务,有效生命周期与整个生命周期同时结束(即便是在 onStartCommand() 返回之后,服务仍然处于活动状态)。对于绑定服务,有效生命周期在 onUnbind() 返回时结束。

特殊情况:
       如果service已由某个客户端通过startService方法启动了,接下来其他客户端调用bindService方法绑定该service后,在调用unbindService方法解除绑定,最后有调用bindService方法再次绑定到Service,这个过程所触发的生命周期方法如下:
onCreate–>onStartCommand–>onBind–>onUnbind(重写该方法时返回了true)–>onRebind
       这个触发过程中,onCreate是创建service后立即被调用,只有当该service被创建时才会被调用;onStartCommand方法则是由客户端调用startService方法时触发的;onRebind方法触发需要该Activity的startService方法启动,并且service子类重写onUnbind方法时返回true。

       当Activity调用bindService绑定一个已启动的service时,系统只是把service内部IBinder对象传给Activity,并不会把该Service生命周期完全绑定到该Activity,因而当Activity调用unBindService方法取消与该Service的绑定的时候,也只是切断该Activity与Service之间的关联,并不能停止该Service组件。

4.Service的使用技巧

4.1 使用前台服务

       在一般情况下,Service几乎都是在后台运行,一直默默地做着辛苦的工作。但这种情况下,后台运行的Service系统优先级相对较低,当系统内存不足时,在后台运行的Service就有可能被回收。那么,如果我们希望Service可以一直保持运行状态且不会在内存不足的情况下被回收时,可以选择将需要保持运行的Service设置为前台服务。
       例如:App中的音乐播放服务应被设置在前台运行(前台服务)——在App后台运行时,便于用户明确知道它的当前操作、在状态栏中指明当前歌曲信息、提供对应操作。
       当然有时候你也可能不仅仅是为了防止Service被回收才使用前台Service,有些项目由于特殊的需求会要求必须使用前台Service,比如说墨迹天气,它的Service在后台更新天气数据的同时,还会在系统状态栏一直显示当前天气的信息。

创建一个前台服务

public class MyService extends Service {    public static final String TAG = "MyService";    private MyBinder mBinder = new MyBinder();    @Override    public void onCreate() {    super.onCreate();    Notification.Builder builder = new Notification.Builder(this.getApplicationContext());        //获取一个Notification构造器        Intent nfIntent = new Intent(this, MainActivity.class);        builder.setContentIntent(PendingIntent.getActivity(this, 0, nfIntent,                0)) // 设置PendingIntent                .setLargeIcon(BitmapFactory.decodeResource(this.getResources(),                        R.mipmap.ic_launcher_round)) // 设置下拉列表中的图标(大图标)                .setContentTitle("下拉列表中的Title") // 设置下拉列表里的标题                .setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标                .setContentText("要显示的内容") // 设置上下文内容                .setWhen(System.currentTimeMillis()); // 设置该通知发生的时间        Notification notification = builder.build(); // 获取构建好的Notification        notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音        // 参数一:唯一的通知标识;参数二:通知消息。        startForeground(110, notification);// 开始前台服务    }    .........}

4.2 使用IntentService

       IntentService 是继承自 Service 并处理异步请求的一个类,在 IntentService 内有一个工作线程来处理耗时操作,当任务执行完后,IntentService 会自动停止,不需要我们去手动结束。如果启动 IntentService 多次,那么每一个耗时操作会以工作队列的方式在 IntentService 的 onHandleIntent 回调方法中执行,依次去执行,执行完自动结束。

创建一个IntentService

public class MyIntentService extends IntentService {    public MyIntentService(String name) {        super(name);    }    @Override    protected void onHandleIntent(@Nullable Intent intent) {        //打印当前线程的id        Log.d("MyIntentService","Thread id is"+Thread.currentThread().getId());    }    @Override    public void onDestroy() {        super.onDestroy();        Log.d("MyIntentService",“onDestory executed”);    }}

       自己创建的MyIntentService实现onHandleIntent这个抽象方法,在这个方法中可以处理一些具体的逻辑,不用担心ANR问题,因为这个方法已经是在子线程中运行了。

4.3 IntentService总结

* IntentService是一个service,也是一个抽象类;* 继承IntentService需要实现其onHandlerIntent抽象方法;* onHandlerIntent在子线程中执行;* IntentService内部保存着一个HandlerThread、Looper与Handler等成员变量,维护这自身的消息队列;* 每次IntentService后台任务执行完成之后都会尝试关闭自身,但是当且仅当IntentService消息队列中最后一个消息被执行完成之后才会真正的stop自身;
阅读全文
0 0