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面试一天一题(1 Day)

前言我从2009年开始接触Android开发(SDK1.6),然后一直在Android这条路上痛并快乐地颠簸顽抗至今。一算也有6年光景了,6年中参加过不同公司的面试,也同样面试过不同公司跳槽来的人,想...

Android面试一天一题(12 Day)

昨天组里的同事遇到一些切换多语言的细节问题,发现在Android N版本上配置应用内多语言没有生效,正好拿出来做为一个面试题讲解一下。面试题:如何实现应用内多语言切换?我们知道Android的多语言实...

Android面试一天一题(15 Day:ContentProvider)

有一次HR给我了一份简历,说是一个资深的工程师,比较特别的是翻译过一本《Andorid XXXX》的书,基本涵盖了Android开发的要点,而且还是有深度的。正好我看过此书的一些章节,面试了一下之后...

Android面试一天一题(4 Day)

我一般面试技术分两方面了解面试者,一是测重问面试者细节的地方,看对方是否真如简历上所说对XX“精通”、“熟悉”、“有一定的见解”,有实践经验的积累。别一种是侧重考察对方对问题(可以是未知问题)的理解和...

Android面试一天一题(3 Day)

面试题: 怎么理解Activity的生命周期?这是一道几乎必问的Android面试题,当听到这题时大家的神经都会麻木,你问我背。说实话,如果你问我怎么理解,我也很难回答,因为这个问题太宽泛了,答什么都...

Android面试一天一题(8 Day)

Android应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队例(MessageQueue),应用程序的主线程不断地从这个消息队例中获取消息(Looper),然后对这些消息进行处理(Ha...

Android面试一天一题(Day 37:一套高级工程师的面试题)

非常感谢这么多的读者一直关注“Android面试一天一题”这个系列,看了这么多篇面试题,不知道有没有效果呢?读者不妨用这一套题来检验一下自己。 虽然针对不同的情况会有不同的面试题,不同技术方...

Android面试一天一题(11 Day)

遇到一个从快播出来的Android开发,有11年的开发经验,咋一看不管是资历还是经历都挺吓人的。但和他共处一段时间后,发现他完全没有体现出11年工作经验的优势,相反还常常犯一些低级的错误,如在List...

Android面试一天一题(14 Day:SharedPreferences)

如果说程序可以简单理解成“指令和数据的集合”,那么你在任何平台上编程都难以离开数据存储,在Android平台上自然也不会例外。说到数据的存储,对于Key-Value对应的数据存取,Android提供S...

Android面试一天一题(10 Day)

有些东西,大家天天都能看到,但并不一定了解和在意它。在Android开发中,加载资源,启动一个新的Activity,获取系统服务,获取数据库路径,创建一个View等都会使用到Context。Conte...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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