android学习笔记——Service服务初探

文章转载自:http://android.jobbole.com/80853/    Service服务只是本篇博文的一小部分。
也有一些笔者自己的理解。

service是四大组件之一比较重要的一部分,它一般在后台运行,不予用户交互,主要执行一些耗时操作。当然,如果你需要的话,也可以让它变成前台运行。

服务的种类(按启动方式来分)

根据服务的启动方式来分,可分为两种
1.通过startService启动
2.通过bindService启动

简单的服务StartService

通过这种方式启动的服务属于比较标准的一类。一般来说,从定义一个服务到使用一个服务,要遵循一下几个步骤:

  1. 自己实现一个类,该类继承service类
  2. 在清单文件中注册自己的服务(实际上,四大组件的使用,都需要在清单文件中进行注册)
  3. 调用startservice来初始化并且启动自己的服务
  4. 调用stopservice来停止我们已经运行的服务。

通过上面的图,我们也可以观察到,这种服务的生命周期是怎样的:


首先,通过startservice来启动服务,如果我们是第一次启动服务的话,需要调用oncreate函数来做一些初始化的操作,并且调用 start方法来开始执行服务的核心代码(新版本中start已经被替换成onstartcommand函数)。如果我们多次调用 startservice方法的话,那么oncreate也不会再被调用,重复调用的只有onstart方法,它会处理传递给startvice的 intent.如果服务中有大量的耗时操作,比如在网络上下载图片等,则可以在Onstart函数里面单独开一个线程来执行这些操作,这样做可以不让我们 的主线程阻塞。最后,如果我们想停止这个服务,可以调用stopservice,这个函数第一次被调用的时候,会执行ondestory函数来释放程序在 oncreate函数中所创建的资源等等。当然,你也可以在服务的内部调用stopself函数来停止服务的运行。
下面来看看代码:
MyService.java
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}

activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/start_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Service" />
<Button
android:id="@+id/stop_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop Service" />
</LinearLayout>

MainActivity.java
public class MainActivity extends Activity implements OnClickListener {
private Button startService;
private Button stopService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService = (Button) findViewById(R.id.start_service);
stopService = (Button) findViewById(R.id.stop_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent); // 启动服务
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent); // 停止服务
break;
default:
break;
}
}
}

千万记得在注册清单中对服务进行注册
<service android:name=".MyService" >
</service>

BindService与Binder
之前的服务,只要在活动中调用startservice之后,服务就自己去运行了,而服务 具体做了什么,在活动内是不知道的,在活动内,除了调用两个API来启动和停止服务之外,就不能再对服务有所干涉了。如果我们想获取到服务执行过程中的一 些情况的话,就需要服务和活动进行通信,这也就需要用到我们进程间通讯机制的一种—Binder.

在之前实现service类的时候,我们可以看到有一个抽象方法被重新实现—onBind()。service和activity的通信大致是通过这样的一种过程来实现的:

  1. 首先在service的类中,实现一个类,这个类继承于Binder,这个类中你可以定义一些你自己的方法,这些方法就是以后要在服务中执行的方 法,这个类是服务类的子类。然后,创建一个这个binder类的实例,在Onbind函数中返回这个binder对象。这个对象就像是一个开着的接口,向 外提供了子类内部将要执行的操作。
  2. 服务对外通信的接口做好之后,想让活动和服务建立连接的话,就要在两者之间创建一个连接点。这个连接点的工作由 ServiceConnection类的对象来实现,我们要在activity中创建一个ServiceConnection的匿名类,类中要重写两个方 法: onServiceDisconnected,onServiceConnected。之后在创建一个这个类的对象,到此为止,连接点也做好了,那么如何 让服务和活动通过这个连接点连接起来进行通讯工作呢?
  3. 因为在实现服务的时候,给我们返回了一个Binder的对象作为接口,可以看见在重写onServiceConnected方法的时候,里面有一 个参数就是Binder类型的参数,通过把这个Binder对象强制类型转换成我们在服务中自己实现binder类的那个对象的类类型之后,就可以通过它 来调用我们服务内部的一些操作。这就为活动来指挥服务内部的运行,提供了良好的接口和条件。
  4. 而对于acitivity来说的话,我们可以通过bindservice这个启动服务的函数,在参数中把要启动服务的intent和创建的连接点 ServiceConnection对象传递过去,如此一来,在绑定成功之后就可以创建服务并且连接在一起,然后自动调用连接点中 onServiceconnected方法,在这个方法的内部就可以通过参数中的binder对象来指挥服务。在服务和活动解除绑定的时候,自然也会调用 连接点处的onServiceDisconnected方法。
  5. 我们在活动中,现在通过Binder对象所能执行的是service类的子类所提供的方法,如果想执行service类内部的方法,可以在这个接口中再定义一个函数,返回服务类的一个对象,便可以执行服务类中的成员方法了。

那么BinderService的生命周期就由以下几个部分构成:


onbindservice方法来创建一个持久的连接,它会在第一次调用的时候,调用oncreate来做一些服务的初始化的操作。之后自动调用 onbind函数,向服务的外部返回Binder对象,这算是一个服务面向外部的接口。之后,通过serviceconnection对象的作用,活动和 服务便能够成功绑定并且可以执行相应的操作,如果想要停止服务,可以调用unbindservice函数来解除之前建立的连接,这会直接调用服务内部的 ondestory函数。

下面来看一下代码:
MainActivity.java
public class MainActivity  extends AppCompatActivity  implements View.OnClickListener {

    public static final int  UPDATE_TEXT 1;

    private Button  start_Service;
    private Button  stopService;
    private Button  bindService;
    private Button  unbindService;
    private Button  changeUI;
    private TextView  tvContent;
    private MyService.DownloadBinder  downloadBinder;

    private ServiceConnection  connection new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder = (MyService.DownloadBinder) service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    start_Service = (Button) findViewById(R.id.start_service);
    stopService = (Button) findViewById(R.id.stop_service);
    bindService = (Button) findViewById(R.id.bind_service);
    unbindService = (Button) findViewById(R.id.unbind_service);
    changeUI = (Button) findViewById(R.id.changeUI);
    tvContent = (TextView) findViewById(R.id.tv_content);

    start_Service.setOnClickListener(this);
    stopService.setOnClickListener(this);
    bindService.setOnClickListener(this);
    unbindService.setOnClickListener(this);
    changeUI.setOnClickListener(this);
}
  @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id. start_service:
                Intent startIntent =  new Intent( this, MyService. class);
                startService(startIntent);
                break;
            case R.id. stop_service:
                Intent stopIntent =  new Intent( this, MyService. class);
                stopService(stopIntent);
                break;
            case R.id. bind_service:
                Intent bindIntent =  new Intent( this, MyService. class);
                bindService(bindIntent,  connectionBIND_AUTO_CREATE); //绑定服务
                break;
            case R.id. unbind_service:
                unbindService( connection); //解绑服务
                break;
            default:
                break;
        }
    }
}

MyService.java
public class MyService  extends Service {

    private DownloadBinder  mBinder new DownloadBinder();

    class DownloadBinder  extends Binder {
        public void startDownload() {
            Log. d( "MyService""startDownload executed");
        }

        public void stopDownload() {
            Log. d( "MyService""stopDownload executed");
        }

        public int getProgress() {
            Log. d( "MyService""getProgress executed");
            return  0;
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return  mBinder;
    }

    @TargetApi(Build.VERSION_CODES. JELLY_BEAN)
    @Override
    public void onCreate() {
        super.onCreate();
        Log. d( "MyService""onCreate executed");
        Notification notification =  new Notification.Builder( this)
                .setContentTitle( "This is Title")
                .setContentText( "This is content")
                .setSmallIcon(R.mipmap. ic_launcher)
                .build();
        Intent notificationIntent =  new Intent( this, MainActivity. class);
        PendingIntent pendingIntent = PendingIntent. getActivity( this0, notificationIntent,  0);
        startForeground( 1, notification);
    }

    @Override
    public int onStartCommand(Intent intent,  int flags,  int startId) {
        Log. d( "MyService""onStartCommand executed");

        new Thread( new Runnable() {
            @Override
            public void run() {
                //线程逻辑
                Log. d( "MyService Thread", "Thread executed");
                stopSelf(); //终止自己的线程
            }
        }).start(); //开启线程
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log. d( "MyService""onDestory executed");
    }
}

布局文件activity_main.xml
<? xml version= "1.0"  encoding= "utf-8" ?>
< LinearLayout  xmlns: android = "http://schemas.android.com/apk/res/android"
    xmlns: tools = "http://schemas.android.com/tools"
    android :layout_width= "match_parent"
    android :layout_height= "match_parent"
    android :orientation= "vertical"
    android :paddingBottom= "@dimen/activity_vertical_margin"
    android :paddingLeft= "@dimen/activity_horizontal_margin"
    android :paddingRight= "@dimen/activity_horizontal_margin"
    android :paddingTop= "@dimen/activity_vertical_margin"
    tools :context= ".MainActivity">

    < Button
        android :id= "@+id/start_service"
        android :layout_width= "wrap_content"
        android :layout_height= "wrap_content"
        android :text= "Staet Service" />

    < Button
        android :id= "@+id/stop_service"
        android :layout_width= "wrap_content"
        android :layout_height= "wrap_content"
        android :text= "Stop Service" />

    < Button
        android :id= "@+id/bind_service"
        android :layout_width= "wrap_content"
        android :layout_height= "wrap_content"
        android :text= "Bind Service" />

    < Button
        android :id= "@+id/unbind_service"
        android :layout_width= "wrap_content"
        android :layout_height= "wrap_content"
        android :text= "UnBind Service" />
    < Button
        android :id= "@+id/changeUI"
        android :layout_width= "wrap_content"
        android :layout_height= "wrap_content"
        android :text= "Change UI"/>
    < TextView
        android :id= "@+id/tv_content"
        android :layout_width= "match_parent"
        android :layout_height= "wrap_content"
        android :textSize= "30sp"
        android :text= "Hello World"/>

</ LinearLayout>



还是要记得在注册文件中对服务进行注册
< service  android :name= ".MyService" />


关于服务的生命周期
一旦在项目的任何位置调用了 Context startService()方法,相应的服务就会启动起来, 并回调 onStartCommand()方法。如果这个服务之前还没有创建过,onCreate()方法会先于 onStartCommand()方法执行。服务启动了之后会一直保持运行状态,直到 stopService()或 stopSelf()方法被调用。注意虽然每调用一次 startService()方法,onStartCommand()就会执行 一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次 startService()方法, 只需调用一次 stopService() stopSelf()方法,服务就会停止下来了。
另外,还可以调用 Context bindService()来获取一个服务的持久连接,这时就会回调 服务中的 onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于 onBind()方法执行。之后,调用方可以获取到 onBind()方法里返回的 IBinder 对象的实例,这 样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保
持运行状态。
当调用了 startService()方法后,又去调用 stopService()方法,这时服务中的 onDestroy() 方法就会执行,表示服务已经销毁了。类似地,当调用了 bindService()方法后,又去调用 unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。但是需要注意 我们是完全有可能对一个服务既调用了 startService()方法,又调用了 bindService()方法的,这种情况下该如何才能让服务销毁掉呢?根据 Android 系统的机制,一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,这种情况下要同时调用 stopService()unbindService()方法,onDestroy()方法才会执行。


服务的更多技巧
可以将服务放到前台来(像墨迹天气、音乐播放器在通知栏的用例)
(在服务开启时onCreate()方法中创建一个通知到前台)

也可以将服务设置为开机自启(其实是使用了广播方式,利用广播接收器来开启服务or应用)

IntentService自动开启和关闭服务

服务的运行一般来说关心两个问题:

  1. 如果处理服务中的耗时操作
  2. 如果在服务中的逻辑运行完毕之后,自动关闭该服务。
对于第一个问题来说,我们可以在服务的逻辑方法中新建一个子线程,让它来完成耗时操作。
对于第二个问题来说,我们可以在服务的运行逻辑的末尾,加上stopself以便让它在逻辑完成时,结束服务。

那么能否有一种service可以不用我们自己手动单独开辟线程,自动的来完成我们的耗时操作呢,并且在服务的逻辑处理完成之后,可以自动的帮我们结束服务,不用我们自己再调用stopself。这种服务就是intentservice。

intentservice的使用方法和普通的service是一样的,但是在intentservice的实现中,我们需要继承自intentservice的类,并且要为这个类重载一个没有参数的构造方法。具体的实现步骤有以下几个比较重要的:

  1. 继承Intentservice并提供一个无参数的构造函数,在方法内部调用父类的构造函数。
  2. 重写父类当中onhandleintent这个方法,把我们要在服务当中处理的逻辑过程都放在这个方法内完成
  3. 启动这个service。 这个服务的特殊之处就在于onhandleintent这个方法,这个方法能够代替我们手动进行的两个工作:这个方法内部的代码都是在子线程中运行的,并 且在这个方法执行到末尾的时候,会自动的帮我们关闭service。这样一来,就不用我们再手动的开子线程和关闭服务了。其实在 intentservice中也是通过开启子线程来完成服务内的逻辑过程的执行的。
来看一下示例代码:
MyIntentService.java
public class MyIntentService  extends IntentService {

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

    @Override
    protected void onHandleIntent(Intent intent) {
        Log. d( "MyIntentService", "onHandleIntent executed ,Thread id is "+Thread. currentThread());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log. d( "MyIntentService", "onDestroy executed");
    }
}
MainActivity.java
case R.id. start_intent_service:
    Log. d( "MainActivity", "MainActivity Thread id is "+Thread. currentThread());
    Intent intentService =  new Intent( this,MyIntentService. class);
    startService(intentService);
    break;
增加了一个Button,点击时触发MyIntentService服务。
别忘记在注册表中注册服务。

StartService和BindService的区别
  1. 如果是使用startservice的方式来启动服务的话,即使启动这个服务的activity可能处于destory状态,但是服务本身还是在继续运行的,除非这个服务所在进程被销毁,那么这个服务也会停止。
  2. 如果是使用bindservice来启动服务的话,一旦启动这个服务的activity变为destory的状态的时候,那么这个服务也随之会销毁,因为这个服务是和特定的activity进行绑定的。
  3. startservice一旦启动之后,activity就不能再插手服务运行过程中的事情,比如我想了解服务运行的状态等等,都不能够实现,也就是说,activity只能是给服务发一个信号来启动他,但是并不能和它进行交互。
  4. bindservice就不一样了,它是依靠binder进程间通讯的机制,在服务本身内部向外提供接口,以便让activity能够和服务进行交互操作,这样一来,activity就可以在服务运行的过程当中来查看服务运行的情况。
  5. bindservice虽然向外部提供了一个binder的对象,通过这个对象可以调用service内部的一些方法,但是在activity 中,通过binder调用的函数并不是直接一步到位调用了服务中的相关成员函数,而只是得到一个类似指针和映射的东西,通过这些再去找在服务内部真正存在 的方法。总的来说把,bindservice向外部提供的,只是内部信息的一个映射,并不是真正的把内部的函数地址等等信息提供出去。
  6. 如果有一个service和多个activity同时绑定的话,那么即使一个activity解除了service的绑定,但是并不会回调 unbind和ondestory的方法,只不过在绑定多个actiity的时候要注意绑定的顺序。> 其实bind和start两种服务最本质的区别就是,Bind是一种服务器/客户端模型,能够让服务的启动者和服务的执行者交互,但是start不行。

Service到底是什么?进程 ?线程?

这个问题也是自己刚刚遇到的,一开始有点模糊不清,包括不明白service为什么和调用他的activity是在同一个主线程中的。网上的资料, 大家还是最好有一点甄别能力,尤其是论坛上面的,有些明显的错误来误导提问者。当然,我写的东西可能也是有错的,如果有不同意见,大家可以一起讨论。 我对这个问题的理解是这样的:<br> 首先,service不是一线程,也不是一个进程,它是一个组件。它的生存运行,和线程的生命周期没有一点关系。也就是说,线程的死活和它是没有关系的。 但是,service的运行,毕竟需要一些条件,比如内存,CPU这些资源。然而,这些资源,操作系统只会分配给进程,这也就是为什么,不管我们用什么种 类的service,只要我们把这个进程干掉,服务神马的也就都是浮云了。所以,服务虽然是一个组件,但是它的运行还是要依赖于进程。

服务分为远程服务和本地服务,本地服务就是在我们这个应用进程之内启动并运行的。一般来说,服务的运行并不会新开一个线程,无论是哪一种service他们都是运行在进程中的主线程中。这也就是为什么不能在服务中直接执行大量的耗时操作,要单独新开一个子线程完成的原因。

还有一个问题:startservice在启动之后,其实activity销毁了,但是它还是继续在运行,直到我们显式的调用 stopservice或者stopself,他才会停止。而且,即使我们在服务运行的过程中stopservice,但是服务还是会完成这一次的逻辑, 也就是说,我们在一个服务执行的过程中,不能中断本次执行的这个过程。这一点对于Bindservice和startservice都是一样。


但是bindeservice在activity销毁之后,它自己也会跟着销毁,我们可能会觉得这是因为进程或者线程的生命周期的关系,其实,这是 不正确的一个是,按下back键使activity销毁,进程还有被杀死,还有一个就是,线程的生命周期和服务压根一点关系没有。之所以 bindservice被销毁了,是因为它绑定的是activity,而不是什线程,activity销毁了,那么绑定它上面的service也就会销毁 了。startservice没有销毁是因为它没有和人和activity绑定。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值