zoukankan      html  css  js  c++  java
  • Android基础(五) Service全解析----看不见的Activity

    一、服务的介绍:

    作为Android四大组件之中的一个,Service(服务)也常常运用于我们的日常使用中,它与Activity的差别在于:Service一直在后台执行。没实用户界面。所以绝不会到前台来。但Service被启动起来之后。它就和Activity一样。全然具有自己的生命周期。

    在关于程序中是选择用Activity还是Service的一个选择标准就是:假设某个程序组件须要执行时向用户呈现某种用户界面。或者该程序须要与用户交互,就须要使用Activity,否则就该考虑使用Service

    就像开发Activity须要两个步骤:①开发Activity子类;②在AndroidManifest文件里配置Activity。开发Service也须要两个步骤:①定义一个继承Service的子类;②在AndroidManifest文件里配置该Service

    public class FirstService extends Service {
        @Nullable
        @Override
        /**
         * Service中唯一的一个抽象方法。子类必须实现,
         * 该方法返回一个IBinder对象。应用程序可通过该对象与Service通信
         */
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        /**
         * 在Service第一次被创建后将马上回调该方法
         */
        @Override
        public void onCreate() {
            super.onCreate();
            Log.i("TAG","Service is Created");
        }
    
        /**
         * 每次client调用startService(Intent)方法启动该Service时都会回调该方法
         * @return
         */
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("TAG","Service is Started");
            return super.onStartCommand(intent, flags, startId);
        }
    
        /**
         * 在该Service被关闭之前将会回调该方法
         */
        @Override
        public void onDestroy() {
            Log.i("TAG","Service is Destroyed!");
            super.onDestroy();
        }
    }
      

       这个Service类重写了Service组件的onCreate()onStartCommand()onDestroy()onBind()方法,重写这些方法时仅仅是简单地输出了一条字符串,除此之外什么也没干。

    当然,为了启动这个服务我们还是要在Activity中写明的:

    package com.bigmoney.www.servicetest;
    
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.Button;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        private Button serviceStart;
        private Button serviceStop;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            serviceStart = (Button) findViewById(R.id.button1);
            serviceStop = (Button) findViewById(R.id.button2);
            serviceStart.setOnClickListener(this);
            serviceStop.setOnClickListener(this);
    
        }
    
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(this,FirstService.class);
            switch (v.getId()){
                case R.id.button1:
                    startService(intent) ;//用意图启动服务
                case R.id.button2:
                    stopService(intent);    //用意图停止服务
                default:
                    break;
            }
        }
    }

    activity中的代码可知。点击第一个button后,后台将打印出例如以下log


    再点击第二个button后。后台会有例如以下log:


    本程序中启动Service的方法属于“通过ContextstartService()方法”,通过此方法启动Service,訪问者与Service之间没有关联。即使訪问者退出了,Service也仍然执行。

    (有人可能注意到了这里使用了显式Intent(直接指定要启动的目标组件的实现类)来启动Service,而并没有使用隐式。并非我偷懒。而是从Android5.0開始,Google要求必须使用显式Intent启动Service组件)

    Service也是context中的一份子,能够把Service看成是一个长期执行在后台的,没有界面的Activity

    是的。或许你看到这里会想服务难道还有第二种启动方式?是的,在讲第二种启动方式之前,我们先来明白下为什么须要使用服务:

    普通情况下,假设我们想让某一块逻辑长期执行在后台,那么能够把它放到服务里面去执行。这也决定了服务的用途:①长期在后台监听设备接入;②在后台轮巡server获取数据(股票类软件等须要频繁刷新操作);③在后台播放音乐(音乐播放器)……等等。

      

    二、Service的两种执行方式:

      ①通过ContextstartService()方法:通过该方法启动Service。訪问者与Service之间没有关联。即使訪问者退出了。Service也仍然执行。

      ②通过ContextBindService()方法:使用该方法启动Service,訪问者与Service绑定在一起。訪问者一旦退出,Service也就终止了。

      用第①种方式启动的代码上面已经给出,启动、关闭Service十分简单,调用Context里定义的startService()stopService()方法就可以启动、关闭Service

    假设在不关闭Service的情况下。连续三次单击“开启Service”。程序将连续三次启动Service,能够看到logcat中连续三次打印出“Service is Started”的log信息。能够由此看出。每当Service被创建时会回调onCreate()方法。每次Service被启动时会回调onStartCommand()方法——多次启动一个已有的Service组件将不会再回调onCreate()方法。但每次启动时都会回调onStartCommand()方法。

      当程序通过startService()stopService()启动、关闭Service时,Service与訪问者之间基本不存在太多的关联,因此Service和訪问者之间也无法进行通信、交换数据。

    假设Service和訪问者之间须要进行方法调用或者交换数据,则应该使用bindService()或者unbindService()方法启动、关闭Service。这里就须要用到我们的第②中启动方式了,也叫“绑定Service”。

    实例代码例如以下:

    ServiceDemo类:

    package com.bigmoney.www.sevvvvv;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.Binder;
    import android.os.IBinder;
    import android.support.annotation.Nullable;
    import android.util.Log;
    import android.widget.Toast;
    
    /**
     * Created by hasee on 2016/3/19.
     * @param
     */
    public class ServiceDemo extends Service {
    
        /**
         * 这是服务内部的代理。用于当activity与服务绑定之后,返回给activity
         */
        class MyBinder extends Binder {
            /**
             * 内部类就通过自己的方法去调用服务的方法,这个内部类方法实际上就是给外部訪问的
             * 也就是给activity訪问的
             * @param name
             * @param money
             */
            public void callMethodInService(String name, int money){
                methodInService(name, money);
            };
        }
    
        private void methodInService(String name, int money) {
            Toast.makeText(this,name+"有"+money+"钱",Toast.LENGTH_LONG).show();
        }
    
        /**
         * 当服务成功绑定之后。就返回这里的内部对象
         * 所谓的“绑定”就是把activity和Service放到一个Map中去形成映射关系
         * @param intent
         * @return
         */
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return new MyBinder();
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.i("vim","onCreate---");
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("vim","onStartCommand---");
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.i("vim", "onDestroy---");
        }
    }

    MainActivity类:

    package com.bigmoney.www.sevvvvv;
    
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    
    public class MainActivity extends AppCompatActivity {
    
        ServiceDemo.MyBinder binder;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        class Myconn implements ServiceConnection {
            /**
             * 当服务成功绑定上之后调用,里面有一个參数很重要就是第二个參数。
             * 这个參数就是服务里面的onBind方法返回的内部对象
             */
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //这里的service实际上就是ServiceDemo类
                binder = (ServiceDemo.MyBinder) service;
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        }
    
        Myconn myconn = new Myconn();
    
        //绑定服务
        public void bind(View v) {
            //启动服务---startService
            //绑定服务---bindService
            Intent intent = new Intent(this, ServiceDemo.class);
            //參数二:用于接收当前服务的一些状态,是启动还是停止了
            //參数三:服务不存在,就创建然后绑定,存在则直接绑定
            bindService(intent, myconn, BIND_AUTO_CREATE);
        }
    
        //调用服务中的方法
        public void call(View v) {
            //调用了Service里面的呼叫服务的方法
            binder.callMethodInService("张三", 1000);
        }
    
        //解除绑定
        public void unbind(View v) {
            Intent intent = new Intent(this, ServiceDemo.class);
            unbindService(myconn);
        }
    }

    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:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.bigmoney.www.sevvvvv.MainActivity"> <Button android:onClick="bind" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="绑定服务" /> <Button android:onClick="call" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="调用服务中的方法" /> <Button android:onClick="unbind" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="解除绑定服务" /> </LinearLayout>

      最后执行起来如图:

    当我们点击“绑定服务”后会打印例如以下log

    当我们点击“调用服务中的方法”时。会有例如以下效果:



    当我们点击“解出绑定服务”时,

    上面的代码示范了怎样在Activity中绑定本地Service,该程序的Service类须要“真正”实现onBind()方法。并让该方法返回一个有效的IBinder对象。

    public IBinder onBind(Intent intent) {
            return new MyBinder();
        }

      这段代码返回了一个可訪问该Service状态数据的IBinder对象,该对象将被传给该Service的訪问者。

      Service中的内部类MyBinder通过继承Binder类实现了一个IBinder对象,这对于绑定本地Service并与之通信是一种常见场景。

      对于ServiceonBinder()方法所返回的IBinder对象来说。它可被当成该Service组件所返回的代理对象,Service同意client通过该IBinder对象来訪问Service内部的数据,这样就可以实现client与Service之间的通信。与多次调用startService()方法启动Service不同的是,多次调用bindService()方法并不会运行反复绑定。对于前一个实例程序。用户每单击“启动服务”button一次,系统就会回调ServiceonStartCommand()方法一样。对于这个演示样例程序,无论用户单击“绑定服务”多少次,系统仅仅会回调用ServiceonBind()方法一次。

     这里,我们能够总结下绑定服务的步骤:

     1、定义服务类、声明服务中的方法

    public class ServiceDemo extends Service{...}

     2、在AndroidManifest文件里注冊服务

     3、定义一个内部类。继承Binder这样的类型

    class MyBinder extends Binder{...}

     4、在onBind方法里面返回内部类对象

    public IBinder onBind(Intent intent) {
                    return new MyBinder();
                }

     5、在activity里面绑定服务

    public void bind(View v){...}

     6、在onServiceConnected方法里面获取到内部对象

    class MyConn implements ServiceConnection{

    public void onServiceConnected(ComponentName name, IBinder service) {// IBinder service =  new MyBinder();
        binder = (MyBinder) service;

    }

     7、依据内部对象调用内部类中的方法。由它去调用服务中的方法

    public void call(View v){
                binder.callMethodInService("张三", 10000);
            }

    三、服务的两种生命周期

      在官方文档中,服务的生命周期如上:

      常规的service总体的生命周期是从onCreate()被调用開始,到onDestroy()方法返回为止。

      和activity一样,serviceonCreate()中进行它的初始化工作。在onDestroy()中释放残留的资源。

      比方。一个音乐播放service能够在onCreate()中创建播放音乐的线程,在onDestory()中停止这个线程。

     onCreate()  onDestroy()会被全部的service调用,不论service是通过startService()还是bindService()建立。

    绑定service积极活动的生命周期是从onStartCommand() onBind()被调用開始,它们各自处理由startService() bindService()方法传过来的Intent对象。

      假设service是被开启的。那么它的活动生命周期和整个生命周期一同结束。

      假设service是被绑定的,它们它的活动生命周期是在onUnbind()方法返回后结束。

      注意:虽然一个被开启的service是通过调用 stopSelf()  stopService()来停止的,没有一个相应的回调函数与之相应,即没有onStop()回调方法。

    所以。当调用了停止的方法,除非这个service和客户组件绑定。否则系统将会直接销毁它。onDestory()方法会被调用,而且是这个时候唯一会被调用的回调方法。


    四、两种服务的差别

    生命周期不同

    > startService : onCreate -- onStartCommand -- onDestroy

    > bindService  : onCreate - onBind -- onUnBind -- onDestroy 

    ② 与服务通讯方式不同

    > startService : 通过intent传递code来区分。(无法与服务进行通讯)

    > bindService : 通过内部代理对象操作服务中的方法

    ③ 与开启者的关系不同

    > startService :即使开启者(activity)已经被销毁了, 服务依旧执行着

    > bindService : 当开启销毁的时候,服务也跟着销毁。

     

    ④ 在设置应用显示不同

    > startService: 在设置应用里面有显示当前正在执行的服务

    > bindService : 在设置应用里面没有显示。

    五、混合绑定服务

    假设使用startService启动服务,是能够让服务在后台一直执行,可是没有办法与服务进行通讯。 假设是用的是bindService启动服务。能够与服务进行通讯,可是没有办法让服务一直在后台执行。能不能让服务长期执行在后台,而且还能与服务进行通讯?答案是能够的。

    详细操作过程例如以下:

    1. 启动服务
    2. 绑定服务
    3. 调用服务方法
    4. 解除绑定
    5. 停止服务

    以上两个服务类代码都是模板代码。直接套用就可以,这里不做赘述。

    注意: 每次与服务进行通讯完成,都记得解除绑定服务。否则服务有可能在退出界面的时候停止。

    另一种远程服务,我将在下片博文里为大家具体介绍。



  • 相关阅读:
    用脚本实现对pm2.5 数据的获取
    虚拟机下的快盘空间分配方案
    C++中new的用法
    只在堆或栈上生成对象
    Virtualbox识别USB设备
    最长公共字序列
    gdb 打印vector 等stl数据
    LC_CTYPE: cannot change locale
    字符串压缩
    对象的概念
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7380578.html
Copyright © 2011-2022 走看看