Android中IntentService知识点面试,这篇文章就够了~

好久没复习IntentService了,直到昨天面试问到了IntentService相关知识点,这里就重新总结了一下,希望对大家掌握IntentService有一臂之力。

从名字上看,很直观地可以看出它是Service的子类,Service对于我们Android开发来讲再熟悉不过了。Service是Android四大组件之一,对用户不可见,但它也是运行在主线程的,那IntentService的特点也跟Service也是一样吗?我们带着这个问题往下分析。

我们知道做Android开发,为啥会有Android初级工程师、Android中级工程师、Android高级工程师以及Android资深工程师甚至是Android专家了?这些分明别类,无非就是对Android整体知识理论及原理的掌握程序和在实际开发运用程度。

所以我从入门级开始给大家讲解一下IntentService的使用,注意了,作为开发首先你得先会用它吧,说得不好听点,你用都不会用,怎么可能知道它的原理呢?

一、IntentService的简单使用

首先,我们知道IntentService是继承自Service,而且它是abstract,这个意味着什么?很简单,我们要重写它。接下来我们通过一个简单的例子来说明IntentService的使用,超级简单,Android入门级的代码书写~

新建一个工程IntentServiceTest,创建完之后,我们新建一个类叫做MyIntentService,让它继承自IntentService,具体代码如下:

package com.example.intentservicetest;

import android.app.IntentService;
import android.content.Intent;
import android.util.Log;

import androidx.annotation.Nullable;

public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        super.onStart(intent, startId);
        Log.i("Andrew", "onStart------------");
    }

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

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.i("Andrew", "onStartCommand------------");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {

        String taskName = intent.getExtras().getString("taskName");
        switch (taskName) {
            case "task1":
                Log.i("Andrew", "do task1");
                break;
            case "task2":
                Log.i("Andrew", "do task2");
                break;
            default:
                break;
        }

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("Andrew", "onDestroy------------");
    }
}

从上面代码可以看出,我们主要重写了IntentService的onStart、onStartCommond、onCreate、onHandleIntent以及onDestroy方法,我们重点关注下onHandleIntent方法,这是核心方法,这里是做一些耗时任务的处理,当收到发送端intent后,它会取出intent相关信息,然后作相应的处理。

接下来我们应当在AndroidManifest.xml中注册MyIntentService,因为我们知道Android的四大组件创建后,都必须要在清单文件中去创建它,别忘记啦,这是常识。

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

    <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/Theme.IntentServiceTest">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <service android:name=".MyIntentService">
            <intent-filter>
                <action android:name="com.het.service" />
            </intent-filter>
        </service>
    </application>

</manifest>

MyIntentService创建并注册完成之后,我们就可以使用它了,我们在MainActivity.java中简单调用一下:

package com.example.intentservicetest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

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

        Intent intent = new Intent("com.het.service");
        Bundle bundle = new Bundle();
        bundle.putString("taskName", "task1");
        intent.putExtras(bundle);
        startService(intent);
    }
}

是不是比预想的要简单的多?别急,最终我们还是要调试运行下结果,因为结果才是最有力的证明。我们在Logcat控制台,抓取下相关日志,Oh~~No,报错了,我们错误log贴出来,具体如下:

这个报错的原因是5.0以上service不能使用隐式intent启动,那我们对intent设置一下包名即可:

Intent intentTwo = new Intent("com.het.service");
Bundle bundleTwo = new Bundle();
bundleTwo.putString("taskName", "task2");
intentTwo.putExtras(bundleTwo);
intentTwo.setPackage("com.example.intentservicetest");
startService(intentTwo);

修改完之后我们再调试运行,结果如下:

com.example.intentservicetest I/Andrew: onCreate------------
com.example.intentservicetest I/Andrew: onStartCommand------------
com.example.intentservicetest I/Andrew: onStart------------
com.example.intentservicetest I/Andrew: do task1
com.example.intentservicetest I/Andrew: onDestroy------------

从打印日志和MyIntentService的代码,我们可以看出每个方法的执行顺序,最重要的一点就是MyIntentService执行完后,自动stop了,这相对service的stopService还是不一样的,这点我们要特别记住,Android面试中很容易考到。

二、面试常考点

1、IntentService为何能执行耗时任务?它的实现原理是什么?

2、IntentService为何执行完任务会自动destroy?

三、IntentService源码解析

我们带着上面两个问题来一起看下IntentService的源码,通过源码我们就能很清楚的知道答案(针对源码我在互联网上找了相关资料,源码带有中文解析):

/**
* IntentService是一种特殊的Service,它继承于Service并且还是个抽象类
* 所以我们必须创建它的子类才能使用IntentService
* IntentService可以用来执行后台任务,当任务执行完后就会“自杀”
* 因为它自己也是个服务,所以优先级高于普通的线程,不容易被系统所干掉
* 所以IntentService比较适合执行优先级较高的后台任务
*/
public abstract class IntentService extends Service {
    //HandlerThread的looper
    private volatile Looper mServiceLooper;
    //通过looper创建的一个Handler对象,用于处理消息
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    /**
    * 通过HandlerThread线程中的looper对象构造的一个Handler对象
    * 可以看到这个handler中主要就是处理消息
    * 并且将我们的onHandlerIntent方法回调出去
    * 然后停止任务,销毁自己
    */
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            /**
            * 收到消息后,会将Intent对象传递给onHandlerIntent方法去处理
            * 注意这个Intent对象的内容和外界Activity中startService(intent)
            * 中的intent的内容是完全一致的
            * 通过这个intent对象可以得到外界启动IntentService时所传递的参数
            * 通过这些参数我们就可以区分不同的业务逻辑
            * 这样onHandlerIntent就可以对不同的逻辑做出不同的操作了
            * 当onHandlerIntent方法执行结束后,IntentService会通过
            * stopSelf(int startId)方法尝试停止服务
            * 之所以使用stopSelf(int startId)而不是stopSelf()来停止
            * 是因为stopSelf()会马上停止服务,但是有可能还有消息未处理
            * stopSelf(int startId)则会等所有的消息都处理完后才销毁自己
            * 一般来说的话,stopSelf(int startId)在停止之前会判断
            * 最近启动服务的次数和startId是不是相等的,如果相等就立刻停止
            * 如果不相等说明还有别的消息没处理,就不停止服务
            * 具体的要看AMS中的stopServiceToken方法的实现
            */
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    /**
    * 构造函数,可以传递一个name参数
    */
    public IntentService(String name) {
        super();
        mName = name;
    }

    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    /**
    * 当我们的IntentService第一次启动的时候,onCreate方法会执行一次
    * 可以看到方法里创建了一个HandlerThread
    * HandlerThread继承Thread,它是一种可以使用Handler的Thread
    * 它的run方法里通过Looper.prepare()来创建消息队列
    * 并通过Looper.loop()来开启消息循环,所以就可以在其中创建Handler了
    * 这里我们通过HandlerThread得到一个looper对象
    * 并且使用它的looper对象来构造一个Handler对象,就是我们上面看到的那个
    * 这样做的好处就是通过mServiceHandler发出的消息都是在HandlerThread中执行
    * 所以从这个角度来看,IntentService是可以执行后台任务的
    */
    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    /**
    * 这个方法里的实现其实就很简单了,就是通过handler发送了一个消息
    * 把我们的intent对象和startId发送出去
    * 在我们上面的handleMessage()会接收到消息
    * 并且通过onHandlerIntent()方法将对象回调给子类
    */
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    /**
    * 每次启动IntentService,onStartCommand()就会被调用一次
    * 在这个方法里处理每个后台任务的intent
    * 可以看到在这个方法里调用的是上方的onStart()方法
    */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    /**
    * 因为looper是无限循环轮询消息的一个机制,所以当我们明确不需要继续使用的话
    * 那么我们就应该通过它的quit()方法来终止它的执行
    * 这是个编程的好习惯,要记住哦!
    */
    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    /**
    * 这个方法的注释写了:除非你为这个service提供了绑定,否则不需要实现这个方法
    * 因为这个方法默认是返回null的
    * 所以咱们不用太关注这个方法
    */
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
    * 这就是IntentService中定义的抽象方法
    * 具体交由它自己的子类来实现
    */ 
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

源码已经讲得很详细了,这里简单总结下:IntentService是个抽象类,继承自Service,类中有一个mServiceLooper、一个mServiceHandler。在onCreate方法中创建了一个HandlerThread,并且通过HandlerThread获取到了mServiceLooper,最后通过这个mServiceLooper创建了mServiceHandler。mServiceHandler通过消息回调来处理多任务,在handlerMessage方法中有核心代码块:

private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

源码看到这里,我们已经能答出上面两个问题了。

针对IntentService为何能执行耗时任务?它的实现原理是什么?我们的回答是:IntentService内部是采用Thread+Handler去处理任务的,也即是开启了新的线程,所以它是能执行耗时任务的。

针对IntentService为何执行完任务会自动destroy?我们的回答是:在ServiceHandler的handleMessage方法中,我们看到了stopSelf方法,也就是当任务处理完成时,这里自动调用了stopSelf方法,所以不用开发者主动去stop。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值