Android基础知识巩固系列 Android之四大组件——Service(服务)

因为最近要面试,于是打算整理整理一下Android的基础知识,由于之前本人已经学习过大概的Android基础知识,这里主要讲这四大组件、五大存储、六大布局、网络请求等这些内容,其他一些等有时间再整理,话不多说。

应用组件(官方解释,需科学上网)

应用组件是 Android 应用的基本构建基块。每个组件都是一个不同的点,系统可以通过它进入您的应用。 并非所有组件都是用户的实际入口点,有些组件相互依赖,但每个组件都以独立实体形式存在,并发挥特定作用 — 每个组件都是唯一的构建基块,有助于定义应用的总体行为。

共有四种不同的应用组件类型。每种类型都服务于不同的目的,并且具有定义组件的创建和销毁方式的不同生命周期。

 

一、什么是Service(服务)?

官方的解释中可以看到:

Service

服务是一种在后台运行的组件,用于执行长时间运行的操作或为远程进程执行作业。 服务不提供用户界面。 例如,当用户位于其他应用中时,服务可能在后台播放音乐或者通过网络获取数据,但不会阻断用户与 Activity 的交互。 诸如 Activity 等其他组件可以启动服务,让其运行或与其绑定以便与其进行交互。

参考网站:https://www.jianshu.com/p/d963c55c3ab9       https://www.jianshu.com/p/95ec2a23f300

       Service是Android系统中的四大组件之一,主要有两个应用场景:后台运行和跨进程访问。Service可以在后台执行长时间运行操作而不提供用户界面,除非系统必须回收内存资源,否则系统不会停止或销毁服务。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍然将在后台继承运行。此外,组件还可以绑定到服务,然后与服务进行交互,甚至是执行进程间通信(IPC)。

        注:Service是在主线程里执行操作的,可能会因为执行耗时操作而导致ANR(Application Not Responding),即无响应。

  • 定义:服务是Android四大组件之一,属于计算型组件
  • 作用:提供需在后台长时间运行的服务(如:复杂计算、音乐播放、下载等等)
  • 特点:在后台运行、生命周期长、无用户界面

 

二、Service(服务)的生命周期

2.1 生命周期的常用方法

  • 官方说明图:

在Service的生命周期里,常用的有:

  • 4个手动调用的方法,如下
手动调用方法作用
startService()启动服务
stopService()关闭服务
bindService()绑定服务
unbindService()解绑服务
  • 5个自动调用的方法,如下
内部自动调用的方法作用
onCreate()创建服务
oStartCommand()开始服务
onBind()绑定服务
onUnbind()解绑服务
onDestroy()销毁服务

2.2 生命周期方法的具体介绍

2.3 常用的生命周期使用

 

三、Service(服务)的类型

3.1 具体分类

Service可按照地点运行类型功能进行分类,具体如下:

  • 地点

1.本地服务

2.远程服务

  • 运行类型

1.前台服务

2.后台服务

  • 功能

1.可通信服务

2.不可通信服务

示意图如下:

3.2 详细介绍

 

四、Service(服务)各种类型的使用

  • 本地服务(LocalService)

1.介绍

      这是最普通、最常用的后台服务Service。

2.使用步骤

         (1)新建子类继承Service类(需重写父类的onCreate()、onStartCommand()、onDestroy()、onBind()方法)

         (2)构建用来启动Service的Intent对象

         (3)调用startService()启动Service、调用stopService()停止服务

         (4)在AndroidMinifest.xml里注册Service

3.代码示例

         (1)activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="启动服务"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="停止服务"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button1" />
</android.support.constraint.ConstraintLayout>

         (2)MyService类

public class MyService extends Service {
    private static final String TAG = "MyService";

    public MyService() {
    }

    @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() {
        Log.d(TAG, "onDestroy: ");
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

         (3)MainActivity类

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

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

        Button button1 = findViewById(R.id.button1);
        Button button2 = findViewById(R.id.button2);
        button1.setOnClickListener(this);
        button2.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent(getApplicationContext(), MyService.class);
        switch (v.getId()) {
            case R.id.button1:
                startService(intent);
                break;
            case R.id.button2:
                stopService(intent);
                break;
        }
    }
}

         (4)AndroidManifest.xml清单文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.peterli.localservicetest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Androidmanifest里Service的常见属性说明

属性说明备注
android:nameService的类名 
android:labelService的名字若不设置,默认为Service类名
android:iconService的图标 
android:permission申明此Service的权限有提供了该权限的应用才能控制或连接此服务
android:process表示该服务是否在另一个进程中运行(远程服务)不设置默认为本地服务;remote则设置成远程服务
android:enabled系统默认启动true:Service 将会默认被系统启动;不设置则默认为false
android:exported该服务是否能够被其他应用程序所控制或连接不设置默认此项为 false

4.运行结果

05-01 17:18:00.777 22569-22569/com.peterli.localservicetest D/MyService: onCreate: 
05-01 17:18:00.777 22569-22569/com.peterli.localservicetest D/MyService: onStartCommand: 
05-01 17:18:11.827 22569-22569/com.peterli.localservicetest D/MyService: onDestroy: 

  • 远程服务(RemoteService)

1.介绍(远程服务与本地服务的区别)

     远程服务与本地服务最大的区别是:远程服务与调用者不再同一个进程里(也就是说远程服务是运行在另外一个进程);                                                                            而本地服务则是与调用者运行在同一个进程里

     示意图如下:

2.使用场景

    多个应用程序共享同一个后台远程服务。(也就是说一个远程服务与多个应用程序的组件(四大组件)进行跨进程通信)

    示意图:

3.具体使用

3.1 前言

    为了让远程Service与多个应用程序的组件(四大组件)进行IPC(跨进程通信),需要使用AIDL(Android接口定义语言)

    (1)IPC:Inter-Process Communication,即跨进程通信。

    (2)AIDL:Android Interface Definition Language,即Android接口定义语言;用于让某个Service与多个应用程序组件之                                      间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。

    在多进程通信中,存在两个进程角色(以最简单的为例):服务器端和客户端

3.2 使用步骤

     服务器端(Service)

          (1)新建定义AIDL文件,并声明该服务需要向客户端提供的接口

          (2)在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCrate、onBind、onDestroy等等)

          (3)在AndroidManifest.xml中注册服务和声明为远程服务

     客户端(Client)

          (1)拷贝服务器端的AIDL文件到目录下

          (2)使用Stud.asInterface接口获取服务器的Binder,根据需要调用服务提供的接口方法

          (3)通过Intent指定服务端的服务名称和所在包,绑定远程Service

3.3 代码示例

     服务器端(Service)工程项目:

步骤1: 新建一个AIDL文件(懒得截图,使用原网站的图)

步骤2:在新建AIDL文件里定义Service需要与Activity进行通信的内容(方法),并进行编译(Make Project)

// 在新建的AIDL_Service1.aidl里声明需要与Activity进行通信的方法
package scut.carson_ho.demo_service;

interface AIDL_Service1 {
    void AIDL_Service();
}
//AIDL中支持以下的数据类型
//1. 基本数据类型
//2. String 和CharSequence
//3. List 和 Map ,List和Map 对象的元素必须是AIDL支持的数据类型;
//4. AIDL自动生成的接口(需要导入-import)
//5. 实现android.os.Parcelable 接口的类(需要导入-import)

步骤3:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCreat、onBind()等等)

public class MyService extends Service {
    private static final String TAG = "MyService";
    private AIDL_Service1.Stub mBinder = new AIDL_Service1.Stub() {
        @Override
        public void AIDL_Service() throws RemoteException {
            Log.d(TAG, "AIDL_Service: 客户端通过AIDL与远程后台成功通信");
        }
    };

    public MyService() {
    }

    @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() {
        Log.d(TAG, "onDestroy: ");
        super.onDestroy();
    }

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

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

步骤4:在AndroidMainfest.xml中注册服务 & 声明为远程服务

<service
            android:name=".MyService"
            android:process=":remote"  //将本地服务设置成远程服务
            android:exported="true"      //设置可被其他进程调用
            >
            //该Service可以响应带有scut.carson_ho.service_server.AIDL_Service1这个action的Intent。
            //此处Intent的action必须写成“服务器端包名.aidl文件名”
            <intent-filter>
                <action android:name="scut.carson_ho.service_server.AIDL_Service1"/>
            </intent-filter>

        </service>

     客户端(Client)工程项目:

              

步骤1:将服务端的AIDL文件所在的包复制到客户端目录下(Project/app/src/main),并进行编译。(懒得截图,使用原网站的图)

PS:记得要原封不动地复制!!什么都不要改!

步骤2:在主布局文件定义按钮

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定服务"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="解绑服务"
        app:layout_constraintEnd_toEndOf="@+id/button1"
        app:layout_constraintStart_toStartOf="@+id/button1"
        app:layout_constraintTop_toBottomOf="@+id/button1" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="启动服务"
        app:layout_constraintEnd_toEndOf="@+id/button2"
        app:layout_constraintStart_toStartOf="@+id/button2"
        app:layout_constraintTop_toBottomOf="@+id/button2" />

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="停止服务"
        app:layout_constraintEnd_toEndOf="@+id/button3"
        app:layout_constraintStart_toStartOf="@+id/button3"
        app:layout_constraintTop_toBottomOf="@+id/button3" />
</android.support.constraint.ConstraintLayout>

步骤3:在MainActivity.java里

  • 使用Stub.asInterface接口获取服务器的Binder;
  • 通过Intent指定服务端的服务名称和所在包,进行Service绑定;
  • 根据需要调用服务提供的接口方法。
public class MainActivity extends AppCompatActivity {
    //定义aidl接口变量
    private AIDL_Service1 mAIDL_service;
    //创建ServiceConnection的匿名类
    private ServiceConnection mServiceConnection = new ServiceConnection() {

        //在Activity与Service建立关联时调用
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //使用AIDLService1.Stub.asInterface()方法获取服务器端返回的IBinder对象
            //将IBinder对象传换成了mAIDL_Service接口对象
            mAIDL_service = AIDL_Service1.Stub.asInterface(service);

            try {
                //通过该对象调用在MyAIDLService.aidl文件中定义的接口方法,从而实现跨进程通信
                mAIDL_service.AIDL_Service();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        //重写onServiceConnected()方法和onServiceDisconnected()方法
        //在Activity与Service建立关联和解除关联的时候调用
        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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

        Button button1 = findViewById(R.id.button1);
        Button button2 = findViewById(R.id.button2);
        Button button3 = findViewById(R.id.button3);
        Button button4 = findViewById(R.id.button4);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //通过Intent指定服务端的服务名称和所在包,与远程Service进行绑定
                //参数与服务器端的action要一致,即"服务器包名.aidl接口文件名"
                Intent intent = new Intent("com.peterli.remoteserviceserver.AIDL_Service1");

                //Android5.0后无法只通过隐式Intent绑定远程Service
                //需要通过setPackage()方法指定包名
                intent.setPackage("com.peterli.remoteserviceserver");

                //绑定服务,传入intent和ServiceConnection对象
                bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
            }
        });

        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(mServiceConnection);
            }
        });

        button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.peterli.remoteserviceserver.AIDL_Service1");
                intent.setPackage("com.peterli.remoteserviceserver");
                startService(intent);
            }
        });

        button4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.peterli.remoteserviceserver.AIDL_Service1");
                intent.setPackage("com.peterli.remoteserviceserver");
                stopService(intent);
            }
        });
    }
}

3.4 运行结果(按的顺序:开启服务-->绑定服务-->解绑服务-->停止服务)

05-01 20:51:10.329 29345-29345/com.peterli.remoteserviceserver:remote D/MyService: onCreate: 
05-01 20:51:10.329 29345-29345/com.peterli.remoteserviceserver:remote D/MyService: onStartCommand: 
05-01 20:51:12.879 29345-29345/com.peterli.remoteserviceserver:remote D/MyService: onBind: 
05-01 20:51:12.879 29345-29357/com.peterli.remoteserviceserver:remote D/MyService: AIDL_Service: 客户端通过AIDL与远程后台成功通信
05-01 20:51:17.239 29345-29345/com.peterli.remoteserviceserver:remote D/MyService: onUnbind: 
05-01 20:51:19.269 29345-29345/com.peterli.remoteserviceserver:remote D/MyService: onDestroy: 

从上面测试结果可以看出:

  • 打印的语句分别运行在不同进程(看语句前面的包名);
  • 客户端调用了服务端Service的方法

    (即客户端和服务端进行了跨进程通信)


  • 后台服务

几乎服务都是在后台运行的,以本地后台为例,那个就是普通的后台服务。


  • 前台服务

1. 介绍

      服务几乎都是在后台运行的,但后台服务的系统优先级还是比较低的,但系统出现内存不足的情况时,就有可能会回收掉正在后台运行的服务。如果想要服务一直保持运行状态,不被系统回收,就可以考虑使用前台服务。前台服务和后台服务最大的区别就在于,它会有一个正在运行的图标在系统状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。或者说可能你并不是希望服务不被系统回收,而是有其他特殊的需求(比如天气APP数据的服务)。

2. 代码示例

      用法很简单,只需要在原有的Service类对onCreate()方法进行稍微修改即可,本人亲测与是否可通信服务无关,如下:

8.0以下:

@Override
    public void onCreate() {
        super.onCreate();
        System.out.println("执行了onCreat()");

        //添加下列代码将后台Service变成前台Service
        //构建"点击通知后打开MainActivity"的Intent对象
        Intent notificationIntent = new Intent(this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notificationIntent,0);

        //新建Builer对象
        Notification.Builder builer = new Notification.Builder(this);
        builer.setContentTitle("前台服务通知的标题");//设置通知的标题
        builer.setContentText("前台服务通知的内容");//设置通知的内容
        builer.setSmallIcon(R.mipmap.ic_launcher);//设置通知的图标
        builer.setContentIntent(pendingIntent);//设置点击通知后的操作

        Notification notification = builer.getNotification();//将Builder对象转变成普通的notification
        startForeground(1, notification);//让Service变成前台Service,并在系统的状态栏显示出来

    }

8.0以上:

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

        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        String channelId = getString(R.string.app_name);
        NotificationChannel notificationChannel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_DEFAULT);
        notificationChannel.setDescription(channelId);
        notificationChannel.setSound(null, null);

        notificationManager.createNotificationChannel(notificationChannel);
        Intent intent = new Intent(getApplicationContext(), MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
        Notification.Builder builder = new Notification.Builder(this, channelId);
        builder.setContentTitle("这是一个标题");
        builder.setContentText("这是一个内容");
        builder.setSmallIcon(R.mipmap.ic_launcher);
        builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
        builder.setContentIntent(pendingIntent);
        Notification notification = builder.build();
        startForeground(1, notification);
    }

3. 测试结果(本人还是懒得截图,贴上原网站的图片)

     运行后,当点击Start Service或Bind Service按钮,Service就会以前台Service的模式启动(通知栏上有通知),如下图


  • 可通信的后台服务

1. 介绍

      普通的本地后台服务是最基础的,但只能单机使用,即无法与Activity通接下来将在上面的基础用法上,增设“与Activity通                    信”的功能,即使用绑定Service服务(Binder类、bindService()、onBind()、unbindService()、onUnbind())

           2. 使用步骤

                (1)在新建子类继承Service类,并新建一个子类继承自Binder类、写入与Activity关联需要的方法、创建实例。

                (2)在主布局文件再设置两个Button分别用于绑定和解绑Service。

                (3)在Activity通过调用MyBinder类中的public方法来实现Activity与Service的联系。

                               (即实现了Activity指挥Service干什么Service就去干什么的功能)

           3. 代码示例

MyService类

public class MyService extends Service {
    private static final String TAG = "MyService";
    private MyBinder mBinder = new MyBinder();

    public MyService() {
    }

    class MyBinder extends Binder{

        public void serviceConnectActivity(){
            Log.d(TAG, "Service关联了Activity,并在Activity执行了Service的方法");
        }

    }

    @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() {
        Log.d(TAG, "onDestroy: ");
        super.onDestroy();
    }

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

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

 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <Button
        android:id="@+id/button1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="启动服务"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="停止服务"
        app:layout_constraintEnd_toEndOf="@+id/button1"
        app:layout_constraintStart_toStartOf="@+id/button1"
        app:layout_constraintTop_toBottomOf="@+id/button1" />

    <Button
        android:id="@+id/button3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="绑定服务"
        app:layout_constraintEnd_toEndOf="@+id/button2"
        app:layout_constraintStart_toStartOf="@+id/button2"
        app:layout_constraintTop_toBottomOf="@+id/button2" />

    <Button
        android:id="@+id/button4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="解绑服务"
        app:layout_constraintEnd_toEndOf="@+id/button3"
        app:layout_constraintStart_toStartOf="@+id/button3"
        app:layout_constraintTop_toBottomOf="@+id/button3" />
</android.support.constraint.ConstraintLayout>

MainActivity类

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private MyService.MyBinder mMyBinder;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMyBinder = (MyService.MyBinder) service;
            mMyBinder.serviceConnectActivity();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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

        Button button1 = findViewById(R.id.button1);
        Button button2 = findViewById(R.id.button2);
        Button button3 = findViewById(R.id.button3);
        Button button4 = findViewById(R.id.button4);

        button1.setOnClickListener(this);
        button2.setOnClickListener(this);
        button3.setOnClickListener(this);
        button4.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent(getApplicationContext(), MyService.class);
        switch (v.getId()) {
            case R.id.button1:
                startService(intent);
                break;
            case R.id.button2:
                stopService(intent);
                break;
            case R.id.button3:
                bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
                break;
            case R.id.button4:
                unbindService(mServiceConnection);
                break;
        }
    }
}

           3.4 运行结果(按的顺序:开启服务-->绑定服务-->解绑服务-->停止服务)

05-01 23:29:07.025 2373-2373/com.peterli.communicationservice D/MyService: onCreate: 
05-01 23:29:07.025 2373-2373/com.peterli.communicationservice D/MyService: onStartCommand: 
05-01 23:29:10.075 2373-2373/com.peterli.communicationservice D/MyService: onBind: 
05-01 23:29:10.105 2373-2373/com.peterli.communicationservice D/MyService: Service关联了Activity,并在Activity执行了Service的方法
05-01 23:29:13.845 2373-2373/com.peterli.communicationservice D/MyService: onUnbind: 
05-01 23:29:14.875 2373-2373/com.peterli.communicationservice D/MyService: onDestroy: 

  • 不可通信的后台服务

以本地后台为例,那个就是普通的不可通信后台服务,它并没有与其他组件进行通信。


 

五、Service(服务)各种类型的使用场景

  • 各种Service的使用场景请看下图:

 

六、IntentService的使用及其工作原理

1.定义

IntentServiceAndroid里的一个封装类,继承四大组件之一的Service

2.作用

处理异步请求和实现多线程,它可以简单地创建一个异步的、会自动停止的服务。

3.使用场景

线程任务需按顺序在后台执行

1.最常见的场景:离线下载

2.不符合多个数据同时请求的场景:所有的任务都在同一个Thread looper里执行

4.工作原理

4.1 流程示意图

     IntentService的工作原理和源码工作流程如下:

4.2 特别注意

       如果启动IntentsService多次,那么每个耗时操作则是以队列的方式在IntentsServiceonHandleIntent回调方法中依                      次执行,执行完自动结束。

5.源码分析

问题1:IntentService如何单独开启1个新的工作线程(主要从源码中的onCreate方法入手)

@Override
public void onCreate() {
    super.onCreate();
    
    // 1. 通过实例化HandlerThread新建线程 & 启动;故 使用IntentService时,不需额外新建线程
    // HandlerThread继承自Thread,内部封装了 Looper
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
  
    // 2. 获得工作线程的 Looper & 维护自己的工作队列
    mServiceLooper = thread.getLooper();

    // 3. 新建mServiceHandler & 绑定上述获得Looper
    // 新建的Handler 属于工作线程 ->>分析1
    mServiceHandler = new ServiceHandler(mServiceLooper); 
}


   /** 
     * 分析1:ServiceHandler源码分析
     **/ 
     private final class ServiceHandler extends Handler {

         // 构造函数
         public ServiceHandler(Looper looper) {
         super(looper);
       }

        // IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
        @Override
         public void handleMessage(Message msg) {
  
          // onHandleIntent 方法在工作线程中执行
          // onHandleIntent() = 抽象方法,使用时需重写 ->>分析2
          onHandleIntent((Intent)msg.obj);
          // 执行完调用 stopSelf() 结束服务
          stopSelf(msg.arg1);

    }
}

   /** 
     * 分析2: onHandleIntent()源码分析
     * onHandleIntent() = 抽象方法,使用时需重写
     **/ 
      @WorkerThread
      protected abstract void onHandleIntent(Intent intent);

问题2:IntentService如何通过onStartCommand() 将Intent传递给服务还有依次插入到工作队列中

/** 
  * onStartCommand()源码分析
  * onHandleIntent() = 抽象方法,使用时需重写
  **/ 
  public int onStartCommand(Intent intent, int flags, int startId) {

    // 调用onStart()->>分析1
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

/** 
  * 分析1:onStart(intent, startId)
  **/ 
  public void onStart(Intent intent, int startId) {

    // 1. 获得ServiceHandler消息的引用
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;

    // 2. 把 Intent参数 包装到 message 的 obj 发送消息中,
    //这里的Intent  = 启动服务时startService(Intent) 里传入的 Intent
    msg.obj = intent;

    // 3. 发送消息,即 添加到消息队列里
    mServiceHandler.sendMessage(msg);
}

6.源码总结

从上面源码可以看出:IntentService本质 = Handler + HandlerThread

1. 通过HandlerThread单独开启1个工作线程(子线程):IntentService

2. 创建1个内部HandlerServiceHandler

3. 绑定ServiceHandlerIntentService

4. 通过onStartCommand()传递服务intentServiceHandler、依次插入Intent到工作队列中并且逐个发送给                                         onHandleIntent()

5. 通过onHandleIntent()依次处理所有Intent对象所对应的任务 

  (因此我们通过重写onHandleIntent()在里面根据Intent的不同进行不同线程操作即可)

7.注意事项

此处,有两个注意事项需要关注的:

     1. 工作任务队列 = 顺序执行

     2. 不建议通过bindService()启动IntentService

注意事项1:工作任务队列 = 顺序执行

解释:

即如果一个任务正在IntentService中执行,此时你再发送1个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕后才开始执行。

原因:

1. 由于onCreate()只会调用一次,所以它只会创建1个工作线程(子线程);

2. 当多次调用startService()时(即onStartCommand()也会调用多次),其实不会创建新的工作线程,只是把消息加入消                     息队列中等待执行;

3. 所以,多次启动IntentService会按照顺序执行事件(若服务停止,则会清除消息队列中的消息,后续的事件不再执行)

注意事项2:不建议通过bindService()启动IntentService

原因:

// 在IntentService中,onBind()默认返回null
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
  • 采用bindService()启动IntentService的生命周期如下:

onCreate() -->onBind() --> onUnbind() --> onDestory()

  • 由上面生命周期可以得知不会调用onStart()onStartCommand(),故不会将消息发送到消息队列,那么也就是说onHandleIntent()也将不会回调,即无法实现多线程的操作(所以,此时你应该使用Service,而不是IntentService

 

七、IntentService与Service(服务)、Thread(普通线程)的区别

此处主要讲解IntentServiceService(服务)Thread(普通线程)的区别。

1.  IntentService与Service(服务)的区别

2.  IntentService与Thread(普通线程)的区别

 

八、Service(服务)与Thread(线程)的区别

  • 结论:Service与Thread无任何关系!!!
  • 之所以会有不少人把他们联系起来,主要是因为Service的后台概念(后台:后台任务运行完全不依赖UI,即时活动被销毁或者说程序被关闭,只要进程还在,后台任务就可以继续运行)
  • 关于二者的异同,具体如下图:

  • 注:一般会将Service和Thread联合着用,即在Service中再创建一个子线程(工作线程)去处理耗时操作逻辑,如下代码:
@Override  
public int onStartCommand(Intent intent, int flags, int startId) {  
//新建工作线程
    new Thread(new Runnable() {  
        @Override  
        public void run() {  
            // 开始执行后台任务  
        }  
    }).start();  
    return super.onStartCommand(intent, flags, startId);  
}  
  
class MyBinder extends Binder {  
    public void service_connect_Activity() {  
  //新建工作线程
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                // 执行具体的下载任务  
            }  
        }).start();  
    }  
  
}  

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值