安卓四大组件之服务

安卓四大组件之服务

一、服务是什么?

1.服务是什么?

用俗话说服务就是长期运行在后台的程序,如果官方一点,首先它是一个组件,用于执行长期运行的任务,并且与用户没有交互。

每一个服务都需要在配置文件AndroidManifest.xml文件下进行声明,怎么声明呢?

使用标签,其实和前面的activity和广播接收者receiver一样声明。

通过Context.startService()来开启服务,通过Context.stop()来停止服务。

还有一种启动形式就是通过Context.bindService()的方法

2.为什么要使用服务呢?

原因一:

从上面的文字说,我们可以知道服务就是执行于后台长期运行的操作。有些时候,我们没有界面,但是服务还是要工作。比如我们播放音乐,在后台播放音乐。比如,我们下载任务,在后台下载文件。这些没有界面的后台运行程序,这些都是用服务做得。

在讲第二个原因之前,先介绍几个概念:

  • 前台进程:可以理解为最顶部的,直接跟用户进行交互的,比如我们的操作界面Activity;
  • 可见进程:可以见到的,但是不进行操作。比如在一个Activity的顶部弹出一个Dialog,这个Dialog就是前台进程,但是这个Activity是可见进程;
  • 服务进程:服务可以理解为忙碌的后台进程,虽然在后台,但是很忙碌;
  • 后台进程:后台进程就是退隐到后台,不做事的进程;
  • 空进程:空进程是不做事的,没有任何东西在上面跑着,仅仅做缓存作用。

假设内存不够用了,会先杀谁呢?

首先杀空进程,要是还不够就杀后台进程,要是还不够,就杀服务,服务被杀后,等内存够用了就会跑起来了。

如果面试问到,服务用语执行耗时操作,这是对的吗?

如时服务直接执行耗时操作,也会出现anr.

在这里给大家补充一下anr的时长知识。首先ANR的意思是android no response,也就是无相应或者理解为操作超时。

在android系统中广播的ANR时长为:

 // How long we allow a receiver to run before giving up on it.  
    static final int BROADCAST_FG_TIMEOUT = 10*1000;  
    static final int BROADCAST_BG_TIMEOUT = 60*1000;  

前台广播为10秒,后台广播为60秒。

按键操作的anr时长为:

// How long we wait until we timeout on key dispatching.  
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;  

如果在服务中直接做耗时操作,也是会出现ANR异常的。服务可以长期在后台运行,所以你可以这么做:如果要做耗时操作,比如说网络的访问,数据库的读写之类的,可以开线程去做。

3.服务的生命周期

1.基本生命周期
(1)创建一个新的活动,在xml文件下面添加两个按钮,用来开启服务和结束服务
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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"
    android:orientation="vertical">

    <Button
        android:onClick="startServiceClick"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始服务"
        tools:ignore="OnClick" />


    <Button
        android:onClick="stopServiceClick"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="停止服务"
        tools:ignore="OnClick" />

</LinearLayout>

(2).新建一个服务类
package com.example.service_learning.services;

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

/**
 * @author 23737
 * @version 1.0 2021.5.17
 */
public class FirstService extends Service {
    private static final String TAG = "FirstService";

    public FirstService() {
    }

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

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


    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestory:·····");
    }
}

如上所示:oncreate()用来开启服务;onDestroy()用来停止服务;

onStart()现在已经过时,替换的是onStartCommand(),每次开启服务,都会开启此方法

(3)在主活动的.java文件内添加代码
public class MainActivity extends AppCompatActivity {

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

    //开启服务
    public void startServiceClick(View view){
        //第一步呢还是要创建一个intent(意图)
        Intent intent = new Intent();
        //第二步开启服务,setClass()里面传入两个参数,分别是当前活动和服务类
        intent.setClass(this, FirstService.class);
        //第三步就是把意图作为参数传给startService()方法,这样服务就开启了
        startService(intent);
    }

    //停止服务
    public void stopServiceClick(View view){
        Intent intent = new Intent();
        intent.setClass(this, FirstService.class);
        stopService(intent);
    }
}

点击发送和停止服务后,log显示首先调用oncreate,然后是onStartCommand()。停止服务时调用了ondestory()

可以看出,我们在startService之后,就执行了onCreate方法,接着是onStartCommand方法。当我们执行stopService的时候,就会走onDestroy方法了。

这就是一个服务的基本生命周期

4.服务的绑定和解绑

1.绑定服务

在服务类里面编辑代码,创建一个内部类和内部方法

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

    public FirstService() {
    }

    //内部类调用服务方法
    public class InnerBinder extends Binder {
        public void callServiceInnerMethod(){
            sayHello();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
       return  new InnerBinder();
    }

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


    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestory:·····");
    }

    public void sayHello(){
        Toast.makeText(this,"hello",Toast.LENGTH_LONG).show();
    }
}

然后在.java文件下编辑代码

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 startServiceClick(View view){
        //第一步呢还是要创建一个intent(意图)
        Intent intent = new Intent();
        //第二步开启服务,setClass()里面传入两个参数,分别是当前活动和服务类
        intent.setClass(this, FirstService.class);
        //第三步就是把意图作为参数传给startService()方法,这样服务就开启了
        startService(intent);
    }

    //停止服务
    public void stopServiceClick(View view){
        Intent intent = new Intent();
        intent.setClass(this, FirstService.class);
        stopService(intent);
    }

    //调用函数内部方法
    public void CallServiceMethod(View view){
   if (mCommunicateBinder != null) {
            //调用服务内部的方法
            mCommunicateBinder.callInnerMethod();
        }
    }

    //绑定服务
    public void bindServiceClick(View view){
        Intent intent = new Intent();
        intent.setClass(this, FirstService.class);
        boolean bindService = bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        private FirstService.InnerBinder mRemoteBinder;

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

}

我理解的绑定流程:

  • 首先在服务类里边创建一个内部类,使之继承Binder类,然后在里面创一个方法用来调用内部方法,记得在onBind()方法里边return内部类的对象
 //内部类调用服务方法
    public class InnerBinder extends Binder {
        public void callServiceInnerMethod(){
            sayHello();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
       return  new InnerBinder();
    }
  • 在activity中,设置意图,然后实例化ServiceConnection类
//绑定服务    public void bindServiceClick(View view){        Intent intent = new Intent();        intent.setClass(this, FirstService.class);        boolean bindService = bindService(intent, mConnection, BIND_AUTO_CREATE);    }    private ServiceConnection mConnection = new ServiceConnection() {        private FirstService.InnerBinder mRemoteBinder;        @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: ");        }    };
2.解绑服务
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 startServiceClick(View view){
        //第一步呢还是要创建一个intent(意图)
        Intent intent = new Intent();
        //第二步开启服务,setClass()里面传入两个参数,分别是当前活动和服务类
        intent.setClass(this, FirstService.class);
        //第三步就是把意图作为参数传给startService()方法,这样服务就开启了
        startService(intent);
    }

    //停止服务
    public void stopServiceClick(View view){
        Intent intent = new Intent();
        intent.setClass(this, FirstService.class);
        stopService(intent);
    }

    //调用函数内部方法
    public void CallServiceMethod(View view){

    }

    //绑定服务
    public void bindServiceClick(View view){
        Intent intent = new Intent();
        intent.setClass(this, FirstService.class);
        boolean bindService = bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        private FirstService.InnerBinder mRemoteBinder;

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

    //解绑服务
    public void unbindServiceClick(View view){
        if (mConnection != null) {
            unbindService(mConnection);
        }
    }
}

和绑定服务基本一致的流程,少了调用内部方法和内部类

二、服务的两种启动方式及其优缺点

1.startService

(1).创建一个类,继承Service
public class FirstService extends Service{

    private static final String TAG = "FirstService";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate...");
        super.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();
    }
}
2.接着写一个类去控制服务
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) {
        Log.d(TAG, "start service ... ");
        startService(new Intent(this, FirstService.class));
    }

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

startService(new Intent(this, FirstService.class)) 服务就启动完成了哈

2.bindService

(1).创建一个服务
public class SecondService extends Service {

    private static final String TAG = "SecondService";

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

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

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.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();
    }
}
(2).写一个主界面
public class BindServiceActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bind_service);
    }

    public void bindServiceClick(View view) {
        //创建意图对象
        Intent intent = new Intent(this, SecondService.class);
        //第一个是参数是意图对象,第二个参数是回调,第三个参数是标记,这个是自动创建的意,如果服务没有start,那么会自己创建。
        //automatically create the service as long as the binding exists
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    public void unBindServiceClick(View view) {
        //解绑服务
        if (mServiceConnection != null) {
            unbindService(mServiceConnection);
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:onClick="bindServiceClick"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="绑定服务"/>


    <Button
        android:onClick="unBindServiceClick"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="解绑服务"/>

</LinearLayout>

bindService(intent, mServiceConnection, BIND_AUTO_CREATE) 也能启动服务

绑定启动服务的特点:

  • 绑定服务在系统设置里是没有显进服务正在跑着的;
  • 如果onBind方法返回的是null,那么onServiceConnected方法不会被调用;
  • 绑定服务的生命周期跟Activity是不求同时生,但求同时死,Activity没了,服务也要解绑;
  • 服务在解除绑定以后会停止运行,执行unBind方法—>onDestroy方法;
  • 绑定服务开启的服务,只可以解绑一次,多次解绑会抛异常;
  • 绑定的connection要跟解绑的connection要对应着,否则没法解绑。

3.两种启动方式的区别是什么?

1、startService这个方法来启动服务的话,是长期运行的,只有stopService才会停止服务。而bindService来启动服务,不用的时候,需要调用unBindService,否则会导致context泄漏,所以bindService不是长期运行的。当context销毁的时候,则会停止服务运行。

2、startService来启动服务可以长期运行,但是不可以通讯,而bindService的方式来启动服务则可以通讯,两者都有优缺点,所以我们就有了混合起来使用的方法。

三、混合开发服务

1.创建一个活动,设置UI布局
<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Mixed_life_cycle.MyActivity"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="startService"
        android:onClick="startServiceClick"
        tools:ignore="OnClick" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="bindService"
        android:onClick="bindServiceClick"
        tools:ignore="OnClick" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="callServiceInnerMethod"
        android:onClick="callServiceClick"
        tools:ignore="OnClick" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="unbindService"
        android:onClick="unbindServiceClick"
        tools:ignore="OnClick" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="stopService"
        android:onClick="stopServiceClick"
        tools:ignore="OnClick" />

</LinearLayout>
2.在activity中完成点击事件
public class MyActivity extends AppCompatActivity {

    private boolean bindService;
    public static final String TAG = "MyActivity";
    private ICommunication iCommunication;

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

    /**
     * 开启服务
     *
     * @param view
     */
    public void startServiceClick(View view) {
        //开启服务
        Intent intent = new Intent(this, SecondService.class);
        startService(intent);
    }

    /**
     * 绑定服务
     *
     * @param view
     */
    public void bindServiceClick(View view) {
        //如果服务已经启动,不会再启动服务。如果没有启动,则启动服务.
        bindService = bindService(new Intent(this, SecondService.class), serviceConnection, BIND_AUTO_CREATE);
    }

    private ServiceConnection serviceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected: ");
            iCommunication = (ICommunication)service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iCommunication  = null;
        }
    };

    /**
     * 调用服务内部方法
     *
     * @param view
     */
    public void callServiceClick(View view) {
        if (iCommunication != null) {
            iCommunication.callServiceInnerMethod();
        }
    }

    /**
     * 解绑服务
     *
     * @param view
     */
    public void unbindServiceClick(View view) {
        if (bindService && serviceConnection != null) {
            unbindService(serviceConnection);
        }
    }

    /**
     * 关闭服务
     *
     * @param view
     */
    public void stopServiceClick(View view) {
        //停止服务
        stopService(new Intent(this, SecondService.class));
    }

}
3.创建一个服务
public class SecondService extends Service {

    public static final String TAG = "SecondService";
    public SecondService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        Log.d(TAG, "onBind: ");
        InnerBinder innerBinder = new InnerBinder();
        return innerBinder;
    }

    //调用内部类方法
    private class InnerBinder extends Binder implements ICommunication {
        @Override
        public void callServiceInnerMethod() {
            serviceInnerMethod();
        }
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

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

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

    /**
     * 服务内部方法
     */
    private void serviceInnerMethod(){
        Toast.makeText(this,"内部方法被调用",Toast.LENGTH_LONG).show();
    }
}

是不是根本没get到怎么进行混合开发服务的点呢?别着急,大菜马上就来!

4.精心大菜——怎么进行混合开发服务?
(1)首先得有个接口,里边定义一个抽象方法
public interface ICommunication {
    void callServiceInnerMethod();
}
(2)使用startService开启服务
/**
     * 开启服务
     *
     * @param view
     */
    public void startServiceClick(View view) {
        //开启服务
        Intent intent = new Intent(this, SecondService.class);
        startService(intent);
    }
(3)绑定启动服务bindService
 /**
     * 绑定服务
     *
     * @param view
     */
    public void bindServiceClick(View view) {
        //如果服务已经启动,不会再启动服务。如果没有启动,则启动服务.
        bindService = bindService(new Intent(this, SecondService.class), serviceConnection, BIND_AUTO_CREATE);
    }

**你且看看,第二个参数里边是不是需要传一个serviceConnection?**所以下一步就是创建一个serviceConnection

(4个serviceConnection出来
private ServiceConnection serviceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected: ");
            iCommunication = (ICommunication)service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iCommunication  = null;
        }
    };

(5)在服务里边设一个内部类,使之继承Binder类,并且实现我们创建的那个接口,以完成活动和服务之间的通信

实现接口的那个方法里边要实现自定义的那个方法

 //调用内部类方法
    private class InnerBinder extends Binder implements ICommunication {
        @Override
        public void callServiceInnerMethod() {
            serviceInnerMethod();
        }
    }
(6)在服务里边创一个自定义方法,作为服务内部方法
 /**
     * 服务内部方法
     */
    private void serviceInnerMethod(){
        Toast.makeText(this,"内部方法被调用",Toast.LENGTH_LONG).show();
    }
(7)在activity中实现“实现服务内部类”的方法
/**
     * 调用服务内部方法
     *
     * @param view
     */
    public void callServiceClick(View view) {
        if (iCommunication != null) {
            iCommunication.callServiceInnerMethod();
        }
    }

巨详细笔记地址:
https://www.sunofbeach.net/a/1186588611802320896

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值