Android面试一天一题(1Day)IntentService作用是什么

原创 2016年08月31日 01:39:12

写在前面

该博客思路源于在简书看到goeasyway博主写的Android面试一天一题系列,无copy之意,仅为让自己总结知识点,成长一点点。先感谢各位大神的无私分享~!

关于题目,大部分则出自AndroidInterview-Q-ALearningNotes,当然既然是Android面试,主要是Android部分,Java基础之后再写。

IntentService作用是什么 — 小米

了解IntentService,必须知道Service。

四大组件 — Service

概念

官方解释

https://developer.android.com/reference/android/app/Service.html

A Service is an application component representing either an application’s desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding ` declaration in its package's AndroidManifest.xml. Services can be started with Context.startService() and Context.bindService()`.

Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.

简单来说,如果用户希望业务出现在后台,而非主界面上,那么Activity肯定没办法满足需求,于是诞生了Service。Service解决了用户可以不在UI界面进行业务操作的问题,如当听音乐时,我们没有必要一直让界面停留在播放界面。在使用Service的时候,一定要注意Service虽然感觉在后台运行,但实际是依附主线程的。

用法

通过在MainActivity的click事件中启动Service,相关代码如下:

public class MainActivity extends AppCompatActivity {

    private boolean serviceRunning = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView serviceStates = (TextView) findViewById(R.id.tv_service_states);
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent serviceIntent = new Intent(MainActivity.this, CustomService.class);
                if (!serviceRunning) {
                    startService(serviceIntent);
                    serviceStates.setText("Service is running...");
                } else {
                    stopService(serviceIntent);
                    serviceStates.setText("Service is stop...");
                }
                serviceRunning = !serviceRunning;
            }
        });
    }
}

Service部分继承了Service,然后在需要的地方加上Log方便查看当前运行状态,代码如下:

public class CustomService extends Service {

    public static final String TAG = CustomService.class.getSimpleName();

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

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e(TAG, " onUnbind ====>  executed ");
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        Log.e(TAG, " onRebind ====>  executed ");
        super.onRebind(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, " onCreate ====>  executed ");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, " onStartCommand ====>  executed ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, " onDestroy ====>  executed ");
        super.onDestroy();
    }
}

当点击Activity中的按钮时,可以看到Service的运行状态如下,明显在stopService(Intent)方法调用后,Service执行了onDestroy,一个Service完整的生命周期就结束了。

CustomService:  onCreate ====>  executed 
CustomService:  onStartCommand ====>  executed 
CustomService:  onDestroy ====>  executed 

缺陷

IntentService替代Service处理耗时操作的作用,那么意味着Service是在主线程中而没有另开新的线程执行任务,从IntentService的源码中我们也可以看到IntentService实际也是通过Handler来进行主线程的UI更新等操作,下面是我在Service的onCreate()方法中使线程sleep了10s导致Demo报ANR

  @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, " onCreate ====>  executed ");
        Log.e(TAG, " onCreate ====>  Thread start sleeping");
        try {
            Thread.sleep(10 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.e(TAG, " onCreate ====>  Thread sleeping end");
    }

而Log提示了我们可以去anr的traces.txt日志文件中寻找问题:

E/CustomService:  onCreate ====>  Thread start sleeping
I/art: Thread[2,tid=26799,WaitingInMainSignalCatcherLoop,Thread*=0x7f9b44c000,peer=0x12c7b0a0,"Signal Catcher"]: reacting to signal 3
I/art: Wrote stack traces to '/data/anr/traces.txt'

IntentService

想知道IntentService的作用,当然需要先了解何为IntentService。

官方解释是这样说的:

IntentService是一个基于Service的子类,其能够根据需求处理异步请求(作为Intent一样来表示)。客户端通过startService(Intent)发送异步请求调用;服务作为需要而开始,控制每个Intent来转向使用一个工作线程,并且当工作完成后自己会停止。

这个工作队列处理器部分通常用于从应用的主线程卸下任务IntentService类纯在为了简化这个部分和考虑到了mechanics。为了使用它,扩展IntentService和实现onHandleIntent(Intent)。IntentService会收到Intents,启动一个工作线程和恰当地停止服务。

所有请求被耽搁工作线程控制 — 他们可能只需要(切不会阻塞应用主线程),但每次只处理一个请求。

(PS:英文翻译太烂,求别吐槽)

先抛开官方解释不说,其实我们应该想想为什么有了Service还需要一个IntentService呢?这里就引出Service与IntentService之间的区别,那么两者是否应该有联系,区别又是什么呢?考虑问题时我们应该往深度和广度去探索,当然别太深无法自拔最后自己都不知道想要了解什么问题。所以接下来的几个问题:

Service的弊端?为什么会出现IntentService,Service和IntentService的区别?

如果说Service是依附于主线程的,也就是说不能进行耗时操作,而继承于它的子类IntentService中新增了哪些代码呢,这就可以从源码入手了,下面是IntentService的几个重要元素:

public abstract class IntentService extends Service {
    // Looper可以知道需要与handler的套用,进行MessageQueue的资源存放和索取
    private volatile Looper mServiceLooper; 
    private volatile ServiceHandler mServiceHandler;
    // ... 代码省略 ...

    // 实际上也是通过Handler的机制来实现耗时操作
    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);
        }
    }

    // ... 代码省略 ...

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        // 从上面实例化的线程中获取Looper,然后再传入ServiceHandler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    // ... 代码省略 ...

   /**
     * This method is invoked on the worker thread with a request to process.
     * Only one Intent is processed at a time, but the processing happens on a
     * worker thread that runs independently from other application logic.
     * So, if this code takes a long time, it will hold up other requests to
     * the same IntentService, but it will not hold up anything else.
     * When all requests have been handled, the IntentService stops itself,
     * so you should not call {@link #stopSelf}.
     *
     * @param intent The value passed to {@link
     *               android.content.Context#startService(Intent)}.
     */
    @WorkerThread
    protected abstract void onHandleIntent(Intent intent);
}

注意onHandleIntent方法的注释:

该方法可以为唤起一个工作线程,而每次只能处理一个Intent,但是这个过程发生在工作线程中,并且运行在一个独立于其他的应用逻辑中,因此如果这部分代码会花一些时间的话,就会先拦截别的请求对于同一个IntentService,但不会拦截除此之外的事件。一旦所有请求都被处理了以后,IntentService会自动停止,你不需要调用stopSelf。

啰嗦一大堆,说白了IntentService就是为了实现让Service能够进行耗时操作的功能。

IntentService的用法

光知道概念和原理,但是不会用怎么行呢,我们以一个简单的Demo来进行示例,首先创建一个MainActivity负责事件开始,点击onClick的时候启动IntentService:

public class MainActivity extends AppCompatActivity {

    private boolean serviceRunning = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView serviceStates = (TextView) findViewById(R.id.tv_service_states);
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent serviceIntent = new Intent(MainActivity.this, CustomIntentService.class);
                serviceIntent.putExtra("sleep_flag", true);
                if (!serviceRunning) {
                    startService(serviceIntent);
                    serviceStates.setText("Service is running...");
                } else {
                    stopService(serviceIntent);
                    serviceStates.setText("Service is stop...");
                }
                serviceRunning = !serviceRunning;
            }
        });
    }
}
public class CustomIntentService extends IntentService {
    public static final String TAG = CustomIntentService.class.getSimpleName();

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

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.e(TAG, " onHandleIntent====> ");
        boolean flag = intent.getBooleanExtra("sleep_flag", false);
        Log.e(TAG, " onHandleIntent flag  ====> " + flag);
    }

    @Override
    public void onCreate() {
        Log.e(TAG, " onCreate ====>  executed ");
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, " onDestroy ====>  executed ");
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, " onStartCommand ====>  executed ");
        boolean flag = intent.getBooleanExtra("sleep_flag", false);
        Log.e(TAG, " onStartCommand flag  ====> " + flag);
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, " onBind ====>  executed ");
        return super.onBind(intent);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e(TAG, " onUnbind ====>  executed ");
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        Log.e(TAG, " onRebind ====>  executed ");

        super.onRebind(intent);
    }
}

因为我在Intent中传递了一个boolean值作为标记,可以在下面的执行结果中看到IntentService方法的执行顺序:

E/CustomIntentService:  onCreate ====>  executed 
E/CustomIntentService:  onStartCommand ====>  executed 
E/CustomIntentService:  onStartCommand flag  ====> true
E/CustomIntentService:  onHandleIntent====> 
E/CustomIntentService:  onHandleIntent flag  ====> true
E/CustomIntentService:  onDestroy ====>  executed 

当我在onHandleIntent()中使线程睡眠10s时,连续点击两次启动Button,可以看到并没有出现Service中ANR的问题,过了一会sleeping end才结束,因为IntentService通过新的子线程来进行耗时操作,从而不会影响主线程的业务逻辑。

@Override
protected void onHandleIntent(Intent intent) {
    Log.e(TAG, " onHandleIntent====> ");
    boolean flag = intent.getBooleanExtra("sleep_flag", false);
    Log.e(TAG, " onHandleIntent flag  ====> " + flag);
    Log.e(TAG, " onHandleIntent====> sleeping start");
    try {
        Thread.sleep(10 * 1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Log.e(TAG, " onHandleIntent====> sleeping end");
}
// 第一次点击
E/CustomIntentService:  onCreate ====>  executed 
E/CustomIntentService:  onStartCommand ====>  executed 
E/CustomIntentService:  onStartCommand flag  ====> true
E/CustomIntentService:  onHandleIntent====> 
E/CustomIntentService:  onHandleIntent flag  ====> true
E/CustomIntentService:  onHandleIntent====> sleeping start
E/CustomIntentService:  onDestroy ====>  executed 
// 第二次点击  
E/CustomIntentService:  onCreate ====>  executed 
E/CustomIntentService:  onStartCommand ====>  executed 
E/CustomIntentService:  onStartCommand flag  ====> true
E/CustomIntentService:  onHandleIntent====> 
E/CustomIntentService:  onHandleIntent flag  ====> true
E/CustomIntentService:  onHandleIntent====> sleeping start
E/CustomIntentService:  onDestroy ====>  executed 
// sleep结束
E/CustomIntentService:  onHandleIntent====> sleeping end
E/CustomIntentService:  onHandleIntent====> sleeping end
版权声明:本文为博主原创文章,未经博主允许不得转载。

如何查看Android的ANR(无响应)异常

在Android开发中,当程序发生异常时会抛出异常信息,可以通过如下代码来获取异常信息,然而在ANR时我们在Logcat中并看不到异常信息。 Thread.setDefaultUncaughtExc...
  • u010134293
  • u010134293
  • 2016年11月14日 10:28
  • 2790

Android IntentService完全解析 当Service遇到Handler

Android 基于Message的进程间通信 Messenger完全解析 转载请标明出处: http://blog.csdn.net/lmj623565791/article/deta...
  • lmj623565791
  • lmj623565791
  • 2015年07月30日 08:49
  • 62361

Android 多线程之IntentService 完全详解

转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/52334098 出自【zejian的博客】 关联文章: A...
  • javazejian
  • javazejian
  • 2016年09月04日 07:58
  • 7950

IntentService,用完即走

IntentService     IntentService,可以看做是Service和HandlerThread的结合体,在完成了使命之后会自动停止,适合需要在工作线程处理UI无关任务的场景。...
  • iromkoear
  • iromkoear
  • 2017年03月18日 12:36
  • 576

一个例子带IntentService入门

之前写过一篇文章讲AsyncTask入门的,http://blog.csdn.net/lincyang/article/details/6617802。 今天要说的IntentService提供的功能...
  • lincyang
  • lincyang
  • 2011年08月09日 18:06
  • 20786

Android源码解析之(五)-->IntentService

什么是IntentService?简单来说IntentService就是一个含有自身消息循环的Service,首先它是一个service,所以service相关具有的特性他都有,同时他还有一些自身的属...
  • qq_23547831
  • qq_23547831
  • 2016年03月22日 21:39
  • 4657

Android学习笔记(五一):服务Service(上)- IntentService

对于需要长期运行,例如播放音乐、长期和服务器的连接,即使已不是屏幕当前的activity仍需要运行的情况,采用服务方式。服务将通过API触发启动或者通过IPC(Interprocess Communi...
  • flowingflying
  • flowingflying
  • 2012年05月30日 14:47
  • 25153

IntentService简介

英文原文:http://developer.android.com/reference/android/app/IntentService.html 参照文章:http://android.tg...
  • hudashi
  • hudashi
  • 2012年09月17日 07:41
  • 61639

IntentService源码详解

IntentService可以做什么: 如果你有一个任务,分成n个子任务,需要它们按照顺序完成。如果需要放到一个服务中完成,那么IntentService就会使最好的选择。 IntentS...
  • murphykwu
  • murphykwu
  • 2014年07月07日 14:44
  • 3098

Android中IntentService与Service的区别

Android中的Service是用于后台服务的,当应用程序被挂到后台的时候,问了保证应用某些组件仍然可以工作而引入了Service这个概念,那么这里面要强调的是Service不是独立的进程,也不是独...
  • matrix_xu
  • matrix_xu
  • 2012年09月13日 12:02
  • 42336
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android面试一天一题(1Day)IntentService作用是什么
举报原因:
原因补充:

(最多只允许输入30个字)