关闭

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

标签: android面试
631人阅读 评论(0) 收藏 举报
分类:

写在前面

该博客思路源于在简书看到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
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:69743次
    • 积分:927
    • 等级:
    • 排名:千里之外
    • 原创:52篇
    • 转载:13篇
    • 译文:0篇
    • 评论:72条
    博客专栏
    最新评论