[Android应用]Android开发基础之服务Service(一、Service的基础知识)

Service简介

Service是Android程序中四大基础组件之一,它和Activity一样都是Context的子类,只不过它没有UI界面,是在后台运行的组件。
Service是Android中实现程序后台运行的解决方案,它非常适用于去执行那些不需要和用户交互而且还要求长期运行的任务。Service默认并不会运行在子线程中,它也不运行在一个独立的进程中,它同样执行在UI线程中,因此,不要在Service中执行耗时的操作,除非你在Service中创建了子线程来完成耗时操作。

Android为什么需要服务?

原因一:

导入进程的概念:Android进程的五个等级

1、前台进程:可以理解为是最顶部的,直接跟用户交互的。比如说我们操作的Activity界面.

2、可见进程:可以见的,但是不操作的,比如说我们在一个Activity的顶部弹出一个Dialog,这个Dialog就是前台进程,但是这个Activity则是可见进程。

3、服务进程:服务可以理解为是忙碌的后台进程,虽然是在后台,但是它很忙碌。

4、后台进程:后台进程就是退隐到后台,不做事的进程。

5、空进程:空进程是不做事的,没有任何东西在上面跑着,仅作缓存作用。

Android进程管理的特殊设计:

        Linux系统对进程的管理方式是一旦进程活动停止,系统就会结束该进程。尽管Android基于Linux Kernel,但在进程管理上,却采取了另外一种独特的设计:当进程活动停止时,系统并不会立刻结束它,而是会尽可能地将该进程保存在内存中,在以后的某个时间,一旦需要该进程,系统就会立即打开它,而不用再做一些初始化操作。只有当剩余内存不够用了,为了维持新开启的进程或者比较重要的进程的正常运行,系统才会选择性地杀掉一些不重要的内存,腾出内存空间来,所以Android系统永远不会有内存不足的提示。

引用:【朝花夕拾】Android性能篇之(六)Android进程管理机制

当内存不足时,首先杀的是空进程,其次时后台进程,再其次时服务进程,但是等内存够用了,服务又会被跑起来了;所以当我们需要长期在后台操作的任务,使用服务即可,Framework里多数都是服务;

原因二:

从前面的简介可知,Service是Android中实现程序后台运行的解决方案;没有界面的时候程序仍然在工作,比如播放音乐,比如下载任务;

Service的生命周期

同样的Service也有生命周期方法:onCreate,onStartCommand,onDestroy;

编写一个类继承自Service

package com.example.servicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class FirstService extends Service {
    private static final String TAG = "FirstService";
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

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

    @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---");
    }
}

再编写一个Activity开启和停止服务,采用显示意图的方式;

package com.example.servicedemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

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

    public void startService(View view){
        Intent intent = new Intent(this,FirstService.class);
        startService(intent);
        Log.d(TAG, "startService: ");
    }

    public void stopService(View view){
        Intent intent = new Intent(this,FirstService.class);
        stopService(intent);
        Log.d(TAG, "stopService: ");
    }

}

第一次开启服务:

1970-01-01 08:01:58.276 2571-2571/com.example.servicedemo D/MainActivity: startService: 
1970-01-01 08:01:58.279 2571-2571/com.example.servicedemo D/FirstService: ---onCreate---
1970-01-01 08:01:58.282 2571-2571/com.example.servicedemo D/FirstService: ---onStartCommand---

重复开启服务:

1970-01-01 08:02:31.467 2571-2571/com.example.servicedemo D/MainActivity: startService: 
1970-01-01 08:02:31.469 2571-2571/com.example.servicedemo D/FirstService: ---onStartCommand---

停止服务:

1970-01-01 08:02:38.419 2571-2571/com.example.servicedemo D/MainActivity: stopService: 
1970-01-01 08:02:38.419 2571-2571/com.example.servicedemo D/FirstService: ---onDestroy---

开启服务之后销毁Activity:

1970-01-01 08:25:10.722 2707-2707/com.example.servicedemo D/MainActivity: startService: 
1970-01-01 08:25:10.723 2707-2707/com.example.servicedemo D/FirstService: ---onCreate---
1970-01-01 08:25:10.733 2707-2707/com.example.servicedemo D/FirstService: ---onStartCommand---

 结论:销毁Activity之后,服务并没有被销毁,仍然在后台运行中;

相对于Activity的生命周期,Service相对简单得多;

绑定服务

为什么需要绑定服务?

绑定服务是客户端-服务器接口中的服务器。绑定服务可让组件(例如 Activity)绑定到服务、发送请求、接收响应,甚至执行进程间通信 (IPC)。 绑定服务通常只在为其他应用组件服务时处于活动状态,不会无限期在后台运行。

总而言之,不绑定服务没办法与其他组件进行通信。

如何绑定服务和解绑服务?

我们在上文的基础下编写一个例子调用服务的内部方法,该例子用到了绑定服务和解绑服务;

在FirstService类中自定义内部类InnerBinder,继承Binder类,在内部类中,根据需要交互的数据,创建一些方法,以便Activity可通过这些方法得到Service中的一些数据,或者干脆通过一个方法返回Service实例;在FirstService类中new 一个InnerBinder私有成员,并在onBind()方法中直接return一个InnerBinder实例,之所以这样做,是因为Service一旦绑定,就会回调onBind()方法,并返回一个Binder对象给Activity;

    public class InnerBinder extends Binder{
        public void callServiceInnerMethod(){
            sayHello();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new InnerBinder();
    }

    private void sayHello() {
        Log.d(TAG, "---Hello---");
    }

在Activity中覆写ServiceConnection接口中的onServiceConnected(ComponentName name, IBinder service)方法以及onServiceDisconnected(ComponentName name);使用public boolean bindService(Intent service, ServiceConnection conn, int flags)来进行绑定服务;其中flags我们采用BIND_AUTO_CREATE,自动创建服务;

    private boolean mIsBindService;
    private FirstService.InnerBinder mRemoteBinder;

    public void bindService(View view){
        Intent intent = new Intent(this,FirstService.class);
        mIsBindService = bindService(intent,mConnection,BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "---onServiceConnected---");
            mRemoteBinder = (FirstService.InnerBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "---onServiceDisconnected---");
            mRemoteBinder = null;
        }
    };

    public void unBindService(View view){
        if (mConnection != null && mIsBindService) {
            Log.d(TAG, "---unBindService---");
            unbindService(mConnection);
        }
    }


    public void callServiceMethod(View view){
        mRemoteBinder.callServiceInnerMethod();
    }

绑定服务-调用服务内部方法-解绑服务-调用服务内部方法打印日志如下

1970-01-01 10:01:50.308 3837-3837/com.example.servicedemo D/FirstService: ---onCreate---
1970-01-01 10:01:50.308 3837-3837/com.example.servicedemo D/FirstService: ---onBind---
1970-01-01 10:01:50.310 3837-3837/com.example.servicedemo D/MainActivity: ---onServiceConnected---
1970-01-01 10:01:52.186 3837-3837/com.example.servicedemo D/FirstService: ---Hello---
1970-01-01 10:01:54.194 3837-3837/com.example.servicedemo D/MainActivity: ---unBindService---
1970-01-01 10:01:54.195 3837-3837/com.example.servicedemo D/FirstService: ---onDestroy---
1970-01-01 10:01:56.897 3837-3837/com.example.servicedemo D/FirstService: ---Hello---
1970-01-01 10:01:57.290 3837-3837/com.example.servicedemo D/FirstService: ---Hello---
1970-01-01 10:01:57.561 3837-3837/com.example.servicedemo D/FirstService: ---Hello---
1970-01-01 10:01:58.049 3837-3837/com.example.servicedemo D/FirstService: ---Hello---

奇怪了,为什么明明解绑服务之后,服务已经销毁了,仍然可以调用内部方法呢?

百度查阅了一番:

首先要明确一点,unbindService()起作用了。你之所以还是可以调用Service里的方法是因为你持有mRemoteBinder这个对象,所以Service的生命周期虽然结束了,但是它还没有被垃圾回收机制回收,这个Service对象还在内存中。

不过当Acvtivity被销毁后,该对象肯定也消失,需要重新绑定服务才可以调用内部方法;

另外,我们还发现如果绑定服务之后直接销毁Activity会有如下报错:

1970-01-01 11:01:41.890 5026-5026/? E/ActivityThread: Activity com.example.servicedemo.MainActivity has leaked ServiceConnection com.example.servicedemo.MainActivity$1@8a6200c that was originally bound here
    android.app.ServiceConnectionLeaked: Activity com.example.servicedemo.MainActivity has leaked ServiceConnection com.example.servicedemo.MainActivity$1@8a6200c that was originally bound here
        at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:1336)
        at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:1231)
        at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1450)
        at android.app.ContextImpl.bindService(ContextImpl.java:1422)
        at android.content.ContextWrapper.bindService(ContextWrapper.java:636)
        at com.example.servicedemo.MainActivity.bindService(MainActivity.java:41)
        at java.lang.reflect.Method.invoke(Native Method)
        at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385)
        at android.view.View.performClick(View.java:5637)
        at android.view.View$PerformClick.run(View.java:22445)
        at android.os.Handler.handleCallback(Handler.java:755)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6141)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:912)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:802)
1970-01-01 11:01:41.890 430-2831/? W/ActivityManager: Unbind failed: could not find connection for android.os.BinderProxy@5dbf313
1970-01-01 11:01:41.891 5026-5026/? D/FirstService: ---onDestroy---

因为销毁Activity没有解绑服务,导致内存泄漏了;这也引出了一个要求,销毁Activity时必须要解绑服务; 

混合使用服务

如果我们采用先开启服务,再绑定服务这种方式,可以保证应用退出到后台之后,服务仍然可以与Activity进行通信,值得注意的是,采用这种方式(start+bind)开启服务,如果要退出服务,必须得先解绑(unBind),再stop,仅仅使用一种方式是无效得。

隐藏服务内部的实现,暴露接口

1、私有内部方法并实现接口;

    private class InnerBinder extends Binder implements IComunication {
        public void callServiceInnerMethod(){
            sayHello();
        }
    }

2、新建IComunication接口,public暴露接口

public interface IComunication {
    void callServiceInnerMethod();
}

3、私有化之后,Activity中的InnerBinder都报红了;调用接口

private IComunication mIComunication;
mIComunication = (IComunication) service;

参考

android四大组件之服务(Service)

Android--绑定服务调用服务的方法

【朝花夕拾】Android性能篇之(六)Android进程管理机制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值