Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

Service 基本介绍

$
0
0
一、前言
引用官方一句话:
A service is an application component that can perform long-running operations in the background and does not provide a user interface。
大概翻译就是:服务是一个应用程序组件,可以在后台执行长时间运行的操作,不提供用户界面。(英语不好)

Service作为Android四大组件之一,必定有它独特的作用。Service与Activity最为相似,但是又有不同。它们的区别是:
1)Activity可以与用户交互,而Service是不能与用户交互,但是长期潜伏在后台执行一些比较耗时的工作(开启新的线程)。
2)Service和Activity都有自己的生命周期,但是Service生命周期比较简单。
3)Service可以实现进程间通信,而Activity只能在同一个进程中调用。
4)Activity在进程被杀死之后就会结束,而Service在后台继续保持运行状态。

相信很多开发者都知道Service如何使用,但是并不是每一位都能理解透彻,希望这篇文章会让大家有所收获。

二、Service基础
说到service,首先要先知道它的生命周期,如下图所示:

根据这张图,我们有必要对这些回调方法进行分析一下:
onStartCommand()当其他组件调用startService()方法请求启动Service时,该方法被回调。一旦Service启动,它会在后台独立运行。当Service执行完以后,需调用stopSelf() 或 stopService()方法停止Service。(若您只希望bind Service,则无需调用这些方法)
onBind()当其他组件调用bindService()方法请求绑定Service时,该方法被回调。该方法返回一个IBinder接口,该接口是Service与绑定的组件进行交互的桥梁。若Service未绑定其他组件,该方法应返回null。
onCreate()当Service第一次创建时,回调该方法。该方法只被回调一次,并在onStartCommand() 或 onBind()方法被回调之前执行。若Service处于运行状态,该方法不会回调。
onDestroy()当Service被销毁时回调,在该方法中应清除一些占用的资源,如停止线程、接触绑定注册的监听器或broadcast receiver 等。该方法是Service中的最后一个回调。

通过上面,相信大家对Service生命周期有一定的认识了,在这里我做一些个人总结:
1)可以看出Service启动有两种方式,第一种用startService启动stopService停止,另外一种是用绑定 bindService和unbindService解绑。但是无论有什么方式,对于生命周期中的onCreate和onDestroy来说,都会被调用。
2)如果service是被开启的,那么它的活动生命周期和整个生命周期一同结束。如果service是被绑定的,那么它的活动生命周期是在onUnbind()方法返回后结束。
3)无论是那种启动方式,onCreate都只会调用一次。

三、Service基本使用
下面通过一个项目来看看Service是怎么执行的?
新建一个TestService继承Service,并重写以下方法:
public class TestService extends Service {

    private static final String TAG = "test";

    @Override
    public void onCreate() {
        super.onCreate();
        //打印线程ID
        Log.d(TAG,"TestService Thread:"+Thread.currentThread().getId());
        Log.d(TAG,"onCreate");
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind");
        return new MyBinder();
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"onDestroy");
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG,"onUnbind");
        return super.onUnbind(intent);
    }

    public class MyBinder extends Binder
    {
        public void load()
        {
            Log.d(TAG,"执行下载了!");
        }
    }
}
然后在ServiceActivity中做启动、停止、绑定、解绑动作。
public class ServiceActivity extends AppCompatActivity {

    private Button start,stop,bind,unbind;
    private TestService.MyBinder myBinder;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_service);
        initUI();
        //打印线程ID
        Log.d("test","activity Thread:"+Thread.currentThread().getId());
    }

    /**
     * 界面初始化
     * @author hjy
     * created at 2016/12/15 13:46
     */
    private void initUI()
    {
        start = (Button) findViewById(R.id.start);
        stop = (Button) findViewById(R.id.stop);
        bind = (Button) findViewById(R.id.bind);
        unbind = (Button) findViewById(R.id.unbind);
        //开启服务
        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(ServiceActivity.this, TestService.class);
                startService(intent);
            }
        });
        //停止服务
        stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(ServiceActivity.this, TestService.class);
                stopService(intent);
            }
        });
        //绑定服务
        bind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(ServiceActivity.this, TestService.class);
                //BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service,
                // 这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。
                bindService(intent,connection,BIND_AUTO_CREATE);
            }
        });
        //解绑服务
        unbind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //Intent intent = new Intent(ServiceActivity.this, TestService.class);
                unbindService(connection);
            }
        });
    }

    //Activity与Service建立关联与接触关联时候被调用
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            myBinder = (TestService.MyBinder)iBinder;
            myBinder.load();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
}
在布局文件中,布局4个按钮,分别是startService,stopService,bindService,unbindService。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_service"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:text="start Service"/>

    <Button
        android:id="@+id/stop"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:text="stop Service"/>

    <Button
        android:id="@+id/bind"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:text="bind Service"/>

    <Button
        android:id="@+id/unbind"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:text="unbind Service"/>

</LinearLayout>
别忘记四大组件在AndroidManifest.xml配置:
<service android:name="com.hjyProject.service.TestService" />

好了,通过上面代码的编写,我们来一步步来看Service执行打印。

1、点击startService按钮,观察一下log打印。

从上面可以看出,点击startService按钮,会执行onCreate-->onStartCommand。眼尖的小伙伴会观察出,activity的线程id与service线程id一样,都是1。顺便说明一点,Service是执行在主线程UI线程中,具体我们下面会做讨论。

2、接着上面一步,我们在继续多点几次startService按钮,观察一下log打印。

从上面可以看出,只要启动了Service服务,不管你以后执行多少次startService都只会执行onStartCommand方法,也能说明,onCreate只执行一次。

3、执行stopService按钮,观察一下log打印。

从上面可以看出,点击stopService,执行了onDestroy方法,说明服务结束。

4、执行bindService按钮,观察一下log打印。

从上面可以看出,点击了bindService按钮,会执行onCreate-->onBind-->Bind类中的下载方法。

5、绑定过得Service,继续点击bindService按钮,观察一下log打印。
无打印现象。。。

从这个现象说明,服务仅能绑定一次。

6、执行unBindService按钮,观察一下log打印。

从上面可以看出,点击了unBindService按钮,会执行onUnbind-->onDestroy,服务解绑。

7执行startService按钮,在执行bindService按钮,在执行stopService按钮或者执行unBindService按钮,观察一下log打印。

从上面可以看出,不管你执行了stopService按钮还是执行了unBindService按钮,都不会打印出onUnBind方法。

奇怪的是,同时点击了这两个stopService按钮和unBindService按钮,就会出现onUnBind方法和onDestroy方法。

从上面可以得出,onBindService和startService同时执行的时候,必须通过unBindService解绑和onstopService停止服务,服务才能真正的结束。

通过上面的例子,相信大家应该知道服务有两种启动方式,服务结束,以及服务的与Activity之前的通信调用。接下来,来讲一下服务与线程之间关系。

四、服务与线程
通过上面我们也先知道了服务所执行的线程与UI主线程是同一个ID。这大概也能说明一点,service想执行比较耗时的事件,必须开启新的子线程,不然会导致UI线程的堵塞。

既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?
这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

在MyBinder类中开启一条新的线程去执行比较耗时的工作。
public class MyBinder extends Binder
{
    public void load()
    {
        Log.d(TAG,"执行下载了!");

        new Thread(new Runnable() {
            @Override
            public void run() {
               ......
            }
        }).start();
    }
}

好了!Service基本使用大概就讲到这,后边我会讲Service进程间的通信(AIDL)。如果有出错或者需要改进的地方,欢迎指点或者交流。



作者:qq_21126979 发表于2016/12/15 17:10:09 原文链接
阅读:23 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>