资讯详情

Android四大组件之——Service


文章目录

    • 程序、程序、线程
      • 概念
      • 线程的生命周期
      • 线程的创建
    • 初识service
      • Service概念
      • Service生命周期
      • Service种类
      • Service重要的方法
      • Service 的声明
      • Service的启动
        • StartService启动Service
        • BindService启动Service
        • StartService启动Service后bindService绑定
    • Service 进阶
      • IntentService的使用
      • Activity与Service通信
      • 实现前台服务
      • 实现定时后台线程
    • Service 再进阶
      • Binder机制
        • IBinder和Binder
        • Binder机制和工作流程
        • 为何Android使用Binder实现过程间通信的机制
      • AIDL(重要)
        • AIDL定义
        • AIDL实现两个过程之间的简单通信
        • 复杂数据的传输AIDL Service
        • 通过Binder的onTransact完成跨过程通信
      • 记录使用高版本的记录AIDL大坑


程序、程序、线程

在了解service在了解相关知识之前,先了解多线程知识。际开发中,Service它通常与多线程相关。

概念

  • 程序:用某种语言编写的一组指令集(一组静态代码),以完成特定任务
  • 过程:操作系统将是运行中的程序、系统调度和资源分配的独立单位 为每个过程分配一个内存空间!依次动态执行程序,加载和执行代码, 完成完完整过程!
  • 线程:比进程更小的执行单元,每个进程可能有多条线程,线程需要放在一个 线程由程序管理,过程由系统调度!
  • 多线程理解:并行执行多个指令,将CPU按照调度算法分配时间片 线程实际上是分时执行的,但切换时间很短,用户感觉到"同时"而已

唠叨两句,有很多学生会混淆过程和线程的概念,这里教你一种方法,过程作为火车;线程作为火车,然后我们看看线程和过程的概念是否非常符合这个概念?

流程是最小的资源分配单位;线程是最小的程序执行单位

线程的生命周期

共五个阶段 新建、就绪、运行、阻塞、死亡

在这里插入图片描述

线程的创建

  1. 继承Thread类
  2. 实现Runnable接口
//创建并启动 new Thread(myThread).start(); 
//使用匿名内部类 new Thread(new Runnable(){ 
              public void run();          }).start(); 
  1. 实现Callable接口

初识service

Service概念

什么是Servie?

Service (服务)是一个应用程序组件,可以在后台长时间运行,不提供用户界面。即使用户切换到另一个应用程序,服务也可以在后台运行。此外,组件可以绑定到服务并与之互动,甚至在执行过程中进行通信(IPC) 。例如,服务可以在后台处理网络事务、播放音乐和执行文件IO或者与ContentProvider通信。

Service生命周期

Service种类

从上图可以看出,Service主要分为两种

  • Started:当应用程序组件(如Activity )通过调用startService()方法启动服务时,服务处于started状态。 即使启动的组件已经被销毁,服务也可以在后台无限期运行。通常,启动服务执行单个操作并且不会向调用者返回结果。例如,它可以通过网络下载或上传文件。如果操作完成,服务需要停止。
  • Bound :应用程序组件通过调用bindService()方法绑定到服务时,服务在bound状态。绑定服务提供客户端-服务器接口,允许组件与服务互动,发送请求,甚至在使用过程中获得结果(IPC) 跨过程完成这些操作。只有当其他应用程序组件被绑定时,绑定服务才能运行。多个组件可以使用一次绑定到 一个服务上,当它们都解绑定时,服务被销毁。

其实还有一种就是启动Service后,绑定Service,也就是两种兼有的

不管应用程序是否为启动状态、绑定状态或者两者兼有,都能通过Intent使用服务,就像使用Activity那样。然而,开发人员可以在配置文件中将服务声明为私有的,从而阻止其他应用程序访问。 服务运行于管理它的进程的主线程,服务不会创建自己的线程,也不会运行于独立的进程(除非开发人员定义)。这意味着,如果服务要完成CPU密集工作或者阻塞操作(如MP3回放或者联网),开发人员需要在服务中创建新线程来完成这些工作。通过使用独立的线程,能减少应用程序不响应(ANR)错误的风险,并且应用程序主线程仍然能用于用户与Activity的交互。

Service中重要的方法

为了创建服务,开发人员需要创建Service类(或其子类)的子类。在实现类中,需要重写一些处理服务生命周期重要方面的回调方法,并根据需要提供组件绑定到服务的机制。

回调 描述
onCreate() 当服务通过onStartCommand()和onBind()被第一次创建的时候,系统调用该方法。该方法在整个生命周期 中只会调用一次,如果服务已经运行,该方法不被调用
onDestory() 当服务不再有用或者被销毁时,系统调用该方法。你的服务需要实现该方法来清理任何资源,如线程,已注册的监听器,接收器等。该方法只会回调一次!
onStartCommand() 其他组件(如活动)通过调用startService()来请求启动服务时,系统调用该方法。如果你实现该方法,你有责任在工作完成时通过stopSelf()或者stopService()方法来停止服务。当客户端调用startService(Intent)方法时会回调,可多次调用StartService方法, 但不会再创建新的Service对象,而是继续复用前面产生的Service对象,但会继续回调 onStartCommand()方法
IBinder onOnbind() 该方法是Service都必须实现的方法,当其他组件想要通过bindService()来绑定服务时,系统调用该方法。如果你实现该方法,你需要返回IBinder对象来提供一个接口,以便客户来与服务通信。你必须实现该方法,如果你不允许绑定,则直接返回null。
onUnbind() 当该Service上绑定的所有客户端都断开时会回调该方法
onRebind() 当新的客户端与服务连接,且此前它已经通过onUnbind(Intent)通知断开连接时,系统调用该方法。

需要重写的重要回调方法有onStartCommand()onBind()onCreate()onDestroy()

Service 的声明

<service android: enabled= ["true" | " false"]
	android:exported= ["true" |"false"]
	android:icon="drawable resource'
	android:label= "string resource'
	android:name= "string "
	android:permission="string"
	android:process="string" >
</service>

  • android:enabled 服务能否被系统实例化,true表示可以,false 表示不可以,默认值是true。标签也有自己的enabled属性,用于包括服务的全部应用程序组件。 和enabled属性必须同时设置成true (两者的默认值都是true)才能让服务可用。如果任何一个是false,服务被禁用并且不能实例化。
  • android:exported 其他应用程序组件能否调用服务或者与其交互,true 表示可以,false 表示不可以。当该值是false时,只有同一个应用程序的组件或者具有相同用户ID的应用程序能启动或者绑定到服务。 默认值依赖于服务是否包含Intent 过滤器。若没有过滤器,说明服务仅能通过精确类名调用,这意味着服务仅用于应用程序内部( 因为其他程序可能不知道类名)。此时,默认值是false;若存在至少一个过滤器,暗示服务可以用于外部,因此默认值是true.该属性不是限制其他应用程序使用服务的唯一方式。 还可以使用permission屈性限制外部实体与服务交互。
  • android:icon 表示服务的图标。该属性必须设置成包含图片定义的可绘制资源引用。如果没有设置,使用应用程序图标取代。服务图标不管在此设置还是在标签设置,都是所有服务的Intent过滤器默认图标。
  • android:label 显示给用户的服务名称。如果没有设置,使用应用程序标签取代。服务标签不管在此设置还是在标签设置,都是所有服务的Intent过滤器默认标签。标签应该设置为字符串资源引用,这样可以像用户界面的其他字符串那样本地化。然而,为了开发时方便,也可以设置成原始字符串。
  • android:name 实现服务的Service子类名称,应该是-一个完整的类名,如com.googl.RoomService.然而,为了简便,如果名称的第一个符号是点号(如.RoomService) ,则会增加在标签中定义的包名。一旦发布了应用程序,不应该再修改子类名称。该属性没有默认值并且必须指定。
  • android:permission 实体必须包含的权限名称,以便启动或者绑定到服务。如果startService()、bindService()或 stopService()方法调用者没有被授权,方法调用无效,并且Intent对象也不会发送给服务。 如果没有设置该属性,使用标签的permission 属性设置给服务。如果 和标签的permission属性都未设置,服务不受权限保护.
  • android:process 服务运行的进程名称。通常,应用程序的全部组件运行于为应用程序创建的默认进程。进程名称与应用程序包名相同。标签 的process 属性能为全部组件设置一个相同的默认值。但是组件能用自己的process属性重写默认值,从而允许应用程序跨越多个进程。如果分配给该属性的名称以冒号(:)开头,仅属于应用程序的新进程会在需要时创建,服务能在该进程中运行; 如果进程名称以小写字母开头,服务会运行在以此为名的全局进程,但需要提供相应的权限。这允许不同应用程序组件共享进程,减少资源使用。

Service的启动

要想声明并启动Service,开发人员需要创建Service类(或其子类)的子类 Android提供了两个类供开发人员继承以创建启动服务。

  • Service: 这是所有服务的基类。当继承该类时,创建新线程来执行服务的全部工作是非常重要的。因为服务默认使用应用程序主线程,这可能降低应用程序Activity的运行性能。
  • IntentService: 这是Service类的子类,它每次使用一个工作线程来处理全部启动请求。在不必同时处理多个请求时,这是最佳选择。开发人员仅需要实现onHandleIntent()方法,该方法接收每次启动请求的Intent 以便完成后台任务。

StartService启动Service

①首次启动会创建一个Service实例,依次调用onCreate()和onStartCommand()方法,此时Service 进入运行状态,如果再次调用StartService启动Service,将不会再创建新的Service对象, 系统会直接复用前面创建的Service对象,调用它的onStartCommand()方法! ②但这样的Service与它的调用者无必然的联系,就是说当调用者结束了自己的生命周期, 但是只要不调用stopService,那么Service还是会继续运行的! ③无论启动了多少次Service,只需调用一次StopService即可停掉Service

FirstService.java

package com.thundersoft.myblogdemo;

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

import androidx.annotation.Nullable;

/** * @ClassName FirstService * @Description TODO * @Author Yu * @Date 2022/6/30 17:19 * @Version 1.0 **/
public class FirstService extends Service { 
        
    private  final static  String TAG="Service1";
    //必须要实现的方法 
    @Nullable
    @Override
    public IBinder onBind(Intent intent) { 
        
        Log.i(TAG,"onBind()被调用");
        return null;
    }
	 //Service被启动时调用 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) { 
        
        Log.i(TAG,"onStartCommand()被调用");
        return super.onStartCommand(intent, flags, startId);
    }
	
	//Service被创建时调用 
    @Override
    public void onCreate() { 
        
        Log.i(TAG,"onCreate()被调用");
        super.onCreate();
    }
	//Service被关闭之前回调 
    @Override
    public void onDestroy() { 
        
        Log.i(TAG,"onDestroy()被调用");
        super.onDestroy();
    }
}

AndroidManifest.xml

 <service android:name=".FirstService" android:enabled="true" >
 </service>

MainActivity.java

public class MainActivity extends AppCompatActivity { 
        

    private Button btn1,btn2;

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

        btn1 = findViewById(R.id.btn1);
        btn2 = findViewById(R.id.btn2);
        Intent intent = new Intent(MainActivity.this, FirstService.class);

        btn1.setOnClickListener(v->{ 
        
            startService(intent);
        });

        btn2.setOnClickListener(v->{ 
        
            stopService(intent);
        });
    }


}

点击开始服务 再次点击 点击关闭服务

这里补充一些知识点 onStartCommand()方法必须返回一个 整数。该值用来描述系统停止服务后如何继续服务onStartCommand()方法返回值必 须是下列常量之一。

  • START_ NOT STICKY 如果系统在onStartCommand()方法返回后停止服务,不重新创建服务,除非有PendingIntent要发送。为避免不在不需要的时候运行服务,这是最佳选择。
  • START_ STICKY 如果系统在onStartCommand()方法返回后停止服务,重新创建服务并调用onStartCommand()方法,但是不重新发送最后的Intent;相反,系统使用空Intent调用onStartCommand0方法,除非有PendingIntent来启动服务,此时,这些Intent会被发送。这适合多媒体播放器(或者类似服务),它们不执行命令但是无限期运行并等待工作。
  • START_REDELIVER_ INTENT 如果系统在onStartCommand0方法返回后停止服务,重新创建服务并使用发送给服务的最后Intent调用onStrtCommand0方法,全部PendingIntent依次发送。这适合积极执行应该立即恢复工作的服务,如下载文件。

说明:这些常 量都定义在Service类中。

BindService启动Service

①当首次使用bindService绑定一个Service时,系统会实例化一个Service实例,并调用其onCreate()和onBind()方法,然后调用者就可以通过IBinder和Service进行交互了,此后如果再次使用bindService绑定Service,系统不会创建新的Sevice实例,也不会再调用onBind()方法,只会直接把IBinder对象传递给其他后来增加的客户端! ②如果我们解除与服务的绑定,只需调用unbindService(),此时onUnbind和onDestory方法将会被调用!这是一个客户端的情况,假如是多个客户端绑定同一个Service的话,情况如下 当一个客户完成和service之间的互动后,它调用 unbindService() 方法来解除绑定。当所有的客户端都和service解除绑定后,系统会销毁service。(除非service也被startService()方法开启) ③另外,和上面那张情况不同,bindService模式下的Service是与调用者相互关联的,可以理解为 “一条绳子上的蚂蚱”,要死一起死,在bindService后,一旦调用者销毁,那么Service也立即终止!

通过BindService调用Service时调用的Context的bindService的解析 bindService(Intent Service,ServiceConnection conn,int flags)

service:通过该intent指定要启动的Service conn:ServiceConnection对象,用户监听访问者与Service间的连接情况, 连接成功回调该对象中的onServiceConnected(ComponentName,IBinder)方法; 如果Service所在的宿主由于异常终止或者其他原因终止,导致Service与访问者间断开 连接时调用onServiceDisconnected(CompanentName)方法,主动通过unBindService() 方法断开并不会调用上述方法! flags:指定绑定时是否自动创建Service(如果Service还未创建), 参数可以是0(不自动创建),BIND_AUTO_CREATE(自动创建)

Context中的bindService方法:

  • ServiceConnection对象:监听访问者与Service间的连接情况,如果成功连接,回调 onServiceConnected(),如果异常终止或者其他原因终止导致Service与访问者断开 连接则回调onServiceDisconnected方法,调用unBindService()不会调用该方法!
  • onServiceConnected方法中有一个IBinder对象,该对象即可实现与被绑定Service 之间的通信!我们再开发Service类时,默认需要实现IBinder onBind()方法,该方法返回的 IBinder对象会传到ServiceConnection对象中的onServiceConnected的参数,我们就可以 在这里通过这个IBinder与Service进行通信!

SecondService.java

package com.thundersoft.myblogdemo;

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

import androidx.annotation.Nullable;

/** * @ClassName SecondService * @Description TODO * @Author Yu * @Date 2022/6/30 18:04 * @Version 1.0 **/
public class SecondService extends Service { 
        
    private  static final String TAG="Service2";

    private boolean quit;
    private int count;
    //onBind()返回的对象
    private MyBinder binder=new MyBinder();
    public class MyBinder extends Binder{ 
        
        SecondService getService(){ 
        
            return SecondService.this;
        }
    }


    //必须实现的方法,绑定改Service时回调该方法
    @Nullable
    @Override
    public IBinder onBind(Intent intent) { 
        
        Log.i(TAG,"onBind()被调用");
        return binder;
    }

    //Service被创建时回调
    @Override
    public void onCreate() { 
        
        Log.i(TAG,"onCreate()被调用");
        new Thread(new Runnable() { 
        
            @Override
            public void run() { 
        
                //当服务还在就一直累加
                while (!quit){ 
        
                    try { 
        
                        Thread.sleep(1000);
                    } catch (InterruptedException e) { 
        
                        e.printStackTrace();
                    }
                    count++;
                }
            }
        }).start();
        super.onCreate();
    }

    //Service断开连接时回调
    @Override
    public boolean onUnbind(Intent intent) { 
        
        Log.i(TAG, "onUnbind方法被调用!");
        return true;
    }

    //Service被关闭前回调
    @Override
    public void onDestroy() { 
        
        super.onDestroy();
        this.quit = true;
        Log.i(TAG, "onDestroyed方法被调用!");
    }

    @Override
    public void onRebind(Intent intent) { 
        
        Log.i(TAG, "onRebind方法被调用!");
        super.onRebind(intent);
    }

    public  int getCount(){ 
        
        return count;
    }
}

AndroidManifest.xml

<service android:name=".SecondService" android:enabled="true" android:exported="true" >
</service>

MainActivity.java

package com.thundersoft.myblogdemo;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity { 
        

    private Button btn1,btn2,btn3;
    SecondService  second_service;

    ServiceConnection connection = new ServiceConnection(){ 
        

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) { 
        
          SecondService.MyBinder  binder=( SecondService.MyBinder) service;
            second_service=binder.getService();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) { 
        

        }
    };

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

        btn1 = findViewById(R.id.btn1);
        btn2 = findViewById(R.id.btn2);
        btn3= findViewById(R.id.btn3);
        Intent intent = new Intent(this,SecondService.class);




        btn1.setOnClickListener(v->{ 
        
            bindService(intent,connection, Service.BIND_AUTO_CREATE);
        });

        btn2.setOnClickListener(v->{ 
        
            unbindService(connection);
        });

        btn3.setOnClickListener(v->{ 
        
            Toast.makeText(this,second_service.getCount()+"",Toast.LENGTH_SHORT).show();
        });
    }


}

点击绑定服务 点击服务状态 点击解绑服务

StartService启动Service后bindService绑定

如果Service已经由某个客户端通过StartService()启动,接下来由其他客户端 再调用bindService()绑定到该Service后调用unbindService()解除绑定最后在 调用bindService()绑定到Service的话,此时所触发的生命周期方法如下: onCreate( )->onStartCommand( )->onBind( )->onUnbind( )->onRebind( ) PS:前提是:onUnbind()方法返回true!!! 这里或许部分读者有疑惑了,调用了unbindService后Service不是应该调用 onDistory()方法么!其实这是因为这个Service是由我们的StartService来启动的 ,所以你调用onUnbind()方法取消绑定,Service也是不会终止的! 得出的结论: 假如我们使用bindService来绑定一个启动的Service,注意是已经启动的Service!!! 系统只是将Service的内部IBinder对象传递给Activity,并不会将Service的生命周期 与Activity绑定,因此调用unBindService( )方法取消绑定时,Service也不会被销毁!

Service 进阶

IntentService的使用

在前面我们提到了可以继承Service或者IntentService来创建并启动自己的Service,前面只对继承Service类的方式做了介绍,下面来详细介绍一下IntentService

既然继承Service类就可以实现自己的Service,为什么还需要IntentService类呢?

事实上,如果我们直接把耗时线程放到Service中的onStart()方法中,很容易 会引起ANR异常(Application Not Responding),虽然可以这样做,但是官方不推荐在Service中进行一些耗时操作。

1.Service不是一个单独的进程,它和它的应用程序在同一个进程中 2.Service不是一个线程,这样就意味着我们应该避免在Service中进行耗时操作

于是,Android给我们提供了解决上述问题的替代品——IntentService; IntentService是继承与Service并处理异步请求的一个类,在IntentService中有 一个工作线程来处理耗时操作,请求的Intent记录会加入队列 IntentService工作流程为:

客户端通过startService(Intent)来启动IntentService; 我们并不需要手动地去控制IntentService,当任务执行完后,IntentService会自动停止; 可以启动IntentService多次,每个耗时操作会以工作队列的方式在IntentService的 onHandleIntent回调方法中执行,并且每次只会执行一个工作线程,执行完一,再到二这样

ThirdService.java

/** * @ClassName ThirdService * @Description TODO * @Author Yu * @Date 2022/7/1 11:18 * @Version 1.0 **/
public class ThirdService extends IntentService { 
        
    private  static final String TAG="ThirdService";


    //必须实现父类的构造方法
    public ThirdService() { 
        
        super("ThirdService");
    }


    //必须重写的核心方法
    @Override
    protected void onHandleIntent(@Nullable Intent intent) { 
        
        String param = intent.getExtras().getString("PARAM");
        if (param.equals("s1")) Log.i(TAG,"service1启动");
        else if (param.equals("s2")) Log.i(TAG,"service2启动");
        else if (param.equals("s3")) Log.i(TAG,"service3启动");

        try { 
        
            
        标签: exact传感器991b传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台