Android面试题之Service

Service-服务,Android四大组件之一,偷鸡摸狗必备功能之一。和Activity最大的区别就是多用户不可见。推荐阅读:Android必读之Service基础

Service系列问题

Service本地服务及生命周期详解

1.1 什么是Service,为什么使用Service

与Activity相对应的就是Service(服务)了,Activity在明,Service在暗。
Service 是一个可以在后台执行长时间运行的服务,对用户不可见。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行(注意:对于国内手机厂商来说,有可能切换到后台后立即就被kill,特别被压倒后台并且手机熄屏后)。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行

1.2 Android中跨进程的通信方式

源码:AndroidIPC

  1. 通过Binder绑定,在Binder中实现一系列的方法对Service进行操作:
      1. 添加一个继承Binder的内部类,并添加相应的逻辑方法
        /**
         * 继承自Binder
         */
        class MyBinder : Binder() {
            var bindService: BindService? = null;
            fun start() {
                bindService!!.startDownload()
            }
            fun stop() {
                bindService!!.stopDownload();
            }
        }
      2. 重写Service的onBind方法,返回我们刚刚定义的那个内部类实例
        class BindService : Service() {
            var binder: MyBinder? = null;
            override fun onCreate() {
                super.onCreate()
                binder = MyBinder();
                binder!!.bindService = this;
            }
            fun startDownload() {
                Log.d("BindService", "startDownload");
            }
            fun stopDownload() {
                Log.d("BindService", "stopDownload");
            }
            override fun onBind(intent: Intent): IBinder {
                //TODO("Return the communication channel to the service.")
                return binder!!
            }
        }
      3. 重写ServiceConnection,onServiceConnected时调用逻辑方法 绑定服务
        /**
         * Connection是Binder中Android进程间通信的桥梁,将IBinder对象在进程间传输
         */
        class BindConnection(internal var context: Context) : ServiceConnection {
            var binder: MyBinder? = null;
            var button: Button? = null;
            override fun onServiceConnected(name: ComponentName, service: IBinder) {
                this.binder = service as MyBinder
                Log.d("BindService", "onServiceConnected")
                button!!.text="绑定成功";
            }
            override fun onServiceDisconnected(name: ComponentName) {
                Toast.makeText(context, "断开链接", Toast.LENGTH_SHORT).show()
            }
        }
      4. 启动时进行绑定
        class Main3Activity : AppCompatActivity() {
            var connection: BindConnection? = null;
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main3)
                findViewById<View>(R.id.btn).setOnClickListener {
                    bindIBinderService();
                }
                findViewById<View>(R.id.btn2).setOnClickListener {
                    connection!!.binder!!.start();
                    connection!!.binder!!.stop();
                }
            }
            fun bindIBinderService() {
                connection = BindConnection(this);
                connection!!.button = findViewById<View>(R.id.btn) as Button;
                bindService(Intent(this, BindService::class.java), connection, Context.BIND_AUTO_CREATE);
            }
            override fun onDestroy() {
                super.onDestroy()
                unbindService(connection);
            }
        }
  2. BroadCastReceive Android必读之BroadcastReceive基础
  3. Messenger 在client与Server端互相绑定Messenger可以实现两端互相通信
    1. 在Activity中实现Messenger,绑定时将对象传到Service
      /**
       *
       * @param messenger Activity中的Messenger,将此Messenger传递到Service中,
       * 从而使Service可以通过Messenger向Activity发送消息
       * @param connection 链接的桥梁
       */
      class Main4Activity : AppCompatActivity() {
          var connection: MessengerConnection? = null;
          var handler = Handler(object : Handler.Callback {
              override fun handleMessage(msg: Message?): Boolean {
                  when (msg!!.what) {
                      1 -> {
                          Log.d("Main4Activity", "Receive Msg");
                      }
                      else -> {
                      }
                  }
                  return true;
              }
          });
          var messenger: Messenger = Messenger(handler);
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              setContentView(R.layout.activity_main3)
              findViewById<View>(R.id.btn).setOnClickListener {
                  bindMessengerService();
              }
              findViewById<View>(R.id.btn2).setOnClickListener {
                  var message: Message = Message.obtain();
                  message.what = 1;
                  connection!!.messenger!!.send(message);
              }
          }
          fun bindMessengerService() {
              connection = MessengerConnection(this);
              connection!!.button = findViewById<View>(R.id.btn) as Button;
              var intent = Intent(this, MessengerService::class.java);
              intent.putExtra("Messenger", messenger);
              bindService(
                  intent,
                  connection as ServiceConnection,
                  Context.BIND_AUTO_CREATE
              );
          }
          override fun onDestroy() {
              super.onDestroy()
              unbindService(connection as ServiceConnection);
          }
      }
    2. 在Service中获取Activity中的Messenger并且把自己的Messenger通过Connect传递给Activity
      class MessengerService : Service() {
          var handler = Handler(object : Handler.Callback {
              override fun handleMessage(msg: Message?): Boolean {
                  when (msg!!.what) {
                      1 -> {
                          Log.d("MessengerService", "startDownload");
                      }
                      2 -> {
                          Log.d("MessengerService", "stopDownload");
                      }
                      3, 4 -> {//同时符合3,4
                      }
                      else -> {
                      }
                  }
                  return true;
              }
          });
          var messenger: Messenger = Messenger(handler);
          var activityMessenger: Messenger? = null;
          override fun onCreate() {
              super.onCreate()
          }
          override fun onBind(intent: Intent): IBinder {
              activityMessenger = intent.getParcelableExtra("Messenger");
              var message: Message = Message.obtain();
              message.what = 1;
              activityMessenger!!.send(message);
              return messenger.binder
          }
      }
      
  4. AIDL

1.3 介绍源码中Binder机制

答:Binder是进程间通信的具体实现,实现了IBinder接口,在BindService中接收Service中onBind(Intent intent)返回的Binder对象,通过这个Binder对象就可以对Service进行操作。这个Binder对象可以是继承自Binder类型的new出来的实例,也可以是aidl生成的AIDL.Stub对象

1.4 IntentService与Service的区别

答:IntentService是继承并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统的Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们手动去控制或stopSelf()。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。推荐阅读:Android必读之IntentService

1.5 Service 是否在 main thread 中执行, service 里面是否 能执行耗时的操作?

默认情况,如果没有显示的指 service 所运行的进程, Service 和 activity 是运 行在当前 app 所在进程的 main thread(UI 主线程)里面。
service 里面不能执行耗时的操作(网络请求,拷贝数据库,大文件 )
特殊情况 ,可以在清单文件配置 service 执行所在的进程 ,让 service 在另 外的进程中执行

        <service
                android:name=".aidl.AidlService"
                android:enabled="true"
                android:process=":AidlService"
                android:exported="true">
  • android:enabled service能否被系统实例化,默认为true
  • android:exported service能否被隐式调用 默认为true
  • android:process 是否在单独的进程中,
    • 如果没有设置值,则表明此service与UI线程在同一个进程中,并且是在主线程中。
    • 如果设置值以冒号“:”开头,则表明此service是App私有的,不能被共享;
    • 如果设置的值以小写字母开头,则表明此service是可以被系统共享的。

1.6 Service的生命周期

Service 有绑定模式和非绑定模式,以及这两种模式的混合使用方式。不同 的使用方法生命周期方法也不同。

  • 非绑定模式:当第一次调用 startService 的时候执行的方法依次为 onCreate()、onStartCommand(),当 Service 关闭的时候调用 onDestory 方 法。
  • 绑定模式:第一次 bindService()的时候,执行的方法为 onCreate()、 onBind()解除绑定的时候会执行 onUnbind()、onDestory()。

上面的两种生命周期是在相对单纯的模式下的情形。我们在开发的过程中还 必须注意 Service 实例只会有一个,也就是说如果当前要启动的 Service 已经存 在了那么就不会再次创建该 Service 当然也不会调用 onCreate()方法。
《Android面试题之Service》

1.7 如何提高service的优先级?

参考文章

  • 1、在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = “1000”这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时实用于广播。没啥用
  • 2、在onStartCommand里面调用 startForeground()方法把Service提升为前台进程级别,然后再onDestroy里面要记得调用stopForeground ()方法。没啥用
  • 3、onStartCommand方法,手动返回START_STICKY。有点用
  • 4、 在onDestroy方法里发广播重启service。
    service +broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service。(第三方应用或是在setting里-应用-强制停止时,APP进程就直接被干掉了,onDestroy方法都进不来,所以无法保证会执行)没啥用
  • 5、监听系统广播判断Service状态。
    通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service是否还存活。没啥用
  • 6、Application加上Persistent属性。推荐阅读:说说Android应用的persistent属性 没啥用

1.8 Service 的 onStartCommand 方法有几种返回值?各代表什么意思?

有四种返回值,不同值代表的意思如下:

  • START_STICKY:如果 service 进程被 kill 掉,保留 service 的状态为开始状态,但不保留递送的 intent 对象。随 后系统会尝试重新创建 service,由于服务状态为开始状态,所以创建服务后一定会调用 onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到 service,那么参数 Intent 将为 null。
  • START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异常 kill 掉,系统不会自动重启该服务。
  • START_REDELIVER_INTENT:重传 Intent。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异 常 kill 掉,系统会自动重启该服务,并将 Intent 的值传入。
  • START_STICKY_COMPATIBILITY: START_STICKY 的兼容版本,但不保证服务被 kill 后一定能重启。

1.9 Service 的 onRebind(Intent)方法在什么情况下会执行?

如果在 onUnbind()方法返回 true 的情况下会执行,否则不执行。

    /**
     * Called when all clients have disconnected from a particular interface
     * published by the service.  The default implementation does nothing and
     * returns false.
     *
     * @param intent The Intent that was used to bind to this service,
     * as given to {@link android.content.Context#bindService
     * Context.bindService}.  Note that any extras that were included with
     * the Intent at that point will <em>not</em> be seen here.
     *
     * @return 注意这里:Return true if you would like to have the service's
     * {@link #onRebind} method later called when new clients bind to it.
     */
    public boolean onUnbind(Intent intent) {
        return false;
    }

2.0 Activity 调用 Service 中的方法都有哪些方式?

源码:AndroidIPC

  • Binder:
    通过 Binder 接口的形式实现,当 Activity 绑定 Service 成功的时候 Activity 会在 ServiceConnection 的类 的 onServiceConnected()回调方法中获取到 Service 的 onBind()方法 return 过来的 Binder 的子类,然后通过对象调用方法。
  • Aidl:
    aidl 比较适合当客户端和服务端不在同一个应用下的场景。
  • Messenger:
    它引用了一个Handler对象,以便others能够向它发送消息(使用mMessenger.send(Message msg)方法)。该类允许跨进程间基于Message的通信(即两个进程间可以通过Message进行通信),在服务端使用Handler创建一个Messenger,客户端持有这个Messenger就可以与服务端通信了。一个Messeger不能同时双向发送,两个就就能双向发送了
点赞

发表评论