IntentService中的耗时操作

本文介绍Android中的IntentService组件,探讨其如何通过内部的HandlerThread处理耗时任务,并在完成后自动关闭自身,有效避免主线程阻塞。

相关文章
Android消息队列机制
IntentService中的耗时操作

文章目录

C]
由于Service和Broadcast都是运行在主线程中,所以在这两个里面我们无法做一些长时间的耗时操作,要执行耗时操作可以用异步线程AsyncTask或者IntentServie。IntentServie的好处是调用玩后会自己关闭service。

用法

调用基本是重写IntentService,然后生成intent,startService。

public class MyIntentService extends IntentService {
	//必须创建构造方法
    //因为IntentService里面已经有个一个构造方法了,构建的时候jvm不会再构造一个构造方法了。
    //你继承IntentService如果不添加构造方法将没有构造方法。jvm会报错。
    //java.lang.InstantiationException: java.lang.Class<***.MyService> has no zero argument constructor
    public MyIntentService () {
        super("");
    }  

    //实现你要做的内容
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        System.out.println("工作线程是: "+Thread.currentThread().getName());
        String task = intent.getStringExtra("task");
        System.out.println("任务是 :"+task);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
	//在Manifest.xml中注册服务
	//调用方式。
        Intent intent = new Intent(this,MyIntentService .class);
        intent.putExtra("task","播放音乐");
        startService(intent);

原理

IntentService里面可以执行耗时操作,下面看下它是怎么实现的。

package android.app;

import android.annotation.WorkerThread;
import android.annotation.Nullable;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    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);
        }
    }


    public IntentService(String name) {
        super();
        mName = name;
    }


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

    @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

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

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    /**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }


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


    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

IntentService 继承Service 。它里面主要是开了一个HandlerThread,执行耗时的操作的内容就在工作线程里面操作。

在onCreate的时候执时候,新建了一个HandlerThread

    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        //新建线程
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
			
		//建立线程的Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

HandlerThread继承了Thread,实现的run方法是建立一套安卓的异步消息队列的。

//HandlerThread的run方法。
    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    

在工作的时候,HandlerThread的onStart放就新建一个message放到handler里面。

    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

Handler是另外定义的,对应到内部类是ServiceHandler 。

    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);
        }
    }

这里可以看出,执行handleMessage时候先执行onHandleIntent((Intent)msg.obj);,然后在执行stopSelf(msg.arg1);onHandleIntent是你自己定义的要执行的内容,stopSelf是结束service。也是就是说,执行完耗时操作后,会关掉service。

//Service 里面的
    /**
     * Stop the service, if it was previously started.  This is the same as
     * calling {@link android.content.Context#stopService} for this particular service.
     *  
     * @see #stopSelfResult(int)
     */
    public final void stopSelf() {
        stopSelf(-1);
    }

    /**
     * Old version of {@link #stopSelfResult} that doesn't return a result.
     *  
     * @see #stopSelfResult
     */
    public final void stopSelf(int startId) {
        if (mActivityManager == null) {
            return;
        }
        try {
            mActivityManager.stopServiceToken(
                    new ComponentName(this, mClassName), mToken, startId);
        } catch (RemoteException ex) {
        }
    }
### IntentService 的特性及 `onStartCommand` 方法超时与 ANR 处理机制 #### 一、IntentService 的特性 `IntentService` 是一个继承自 `Service` 的类,专门用于处理异步请求。其核心特性包括以下几点: - 内部实现了一个工作线程来处理耗时操作,避免阻塞主线程[^1]。 - 每次启动 `IntentService` 时,任务会被添加到工作队列中,并在 `onHandleIntent` 方法中依次执行。 - 当所有任务处理完毕后,`IntentService` 会自动停止,无需手动调用 `stopSelf()`。 以下是 `IntentService` 的典型代码示例: ```java public class MyIntentService extends IntentService { public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { // 在工作线程中执行耗时任务 if (intent != null) { String action = intent.getAction(); if ("ACTION_TASK".equals(action)) { performTask(); } } } private void performTask() { try { Thread.sleep(5000); // 模拟耗时操作 } catch (InterruptedException e) { e.printStackTrace(); } } } ``` #### 二、`onStartCommand` 方法运行超时是否会导致 ANR `IntentService` 的 `onStartCommand` 方法会在主线程中被调用。尽管 `IntentService` 内部使用了工作线程来处理耗时任务,但如果 `onStartCommand` 方法本身执行时间过长(例如超过 5 秒),可能会导致主线程阻塞,从而触发 ANR[^4]。 以下是一个可能导致 ANR 的示例代码: ```java public class ThreadService extends IntentService { public ThreadService() { super("ThreadService"); } @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { Log.i("ThreadService", "onStartCommand"); try { Thread.sleep(60_000); // 主线程长时间阻塞 } catch (InterruptedException e) { e.printStackTrace(); } return super.onStartCommand(intent, flags, startId); } @Override protected void onHandleIntent(Intent intent) { // 工作线程中的任务 } } ``` 上述代码中,`onStartCommand` 方法在主线程中休眠了 60 秒,这将直接导致 ANR[^2]。 #### 三、ANR 超时处理机制 当应用出现以下情况时,系统会触发 ANR: - 输入事件调度超时:如果应用在 5 秒内未响应输入事件(如按键或触摸)[^4]。 - 服务执行超时:如果声明的服务未能在几秒内完成 `Service.onCreate()` 或 `Service.onStartCommand()` 的执行[^4]。 - 前台服务未及时调用 `startForeground()`:如果通过 `Context.startForegroundService()` 启动的前台服务未能在 5 秒内调用 `startForeground()`[^4]。 对于 `IntentService` 来说,虽然其内部实现了工作线程,但如果 `onStartCommand` 方法在主线程中执行耗时操作,则仍然可能触发 ANR。因此,应确保 `onStartCommand` 方法尽可能轻量化。 #### 四、解决方案 为了避免 ANR 的发生,可以采取以下措施: 1. **避免在 `onStartCommand` 中执行耗时操作**:将耗时任务移至 `onHandleIntent` 方法中处理,确保 `onStartCommand` 方法快速返回[^1]。 2. **优化主线程逻辑**:减少主线程上的阻塞操作,确保输入事件能够及时响应。 3. **使用现代后台任务框架**:推荐使用 `WorkManager` 替代 `IntentService`,以更灵活地管理后台任务[^3]。 以下是优化后的代码示例: ```java public class OptimizedIntentService extends IntentService { public OptimizedIntentService() { super("OptimizedIntentService"); } @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { // 确保 onStartCommand 快速返回 return super.onStartCommand(intent, flags, startId); } @Override protected void onHandleIntent(Intent intent) { // 在工作线程中执行耗时任务 if (intent != null) { String action = intent.getAction(); if ("ACTION_TASK".equals(action)) { performTask(); } } } private void performTask() { try { Thread.sleep(5000); // 模拟耗时操作 } catch (InterruptedException e) { e.printStackTrace(); } } } ``` ### 总结 `IntentService` 的 `onStartCommand` 方法运行在主线程中,若执行时间过长(如超过 5 秒),可能会导致 ANR。为避免此问题,应将耗时任务移至 `onHandleIntent` 方法中处理,并确保 `onStartCommand` 方法快速返回[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值