goAsync帮你在onReceive中简便地进行异步操作

广播回调onReceive是在主线程跑的,所以我们不能在里面搞耗时操作,不然秒秒钟ANR。

又因为onReceive中的代码在执行完后,BroadcastReceiver对象就无效了,生命周期结束。
所以我们不能直接在里面起子线程,若应用进程被回收掉,线程的任务就可能无法完成。徒增不可控因素。

解决:
普遍的处理方式是在onReceive中再起一个IntentService去执行异步操作。这样就有了Service组件的保障,进程不会被轻易杀掉,但同时这个操作也比较重,代码实现上还得再去写个处理特殊业务的IntentService。

比如说我在onReceive里面只做一些很简单的耗时操作,可能就一两行代码我也要去写个Service?显然谷歌爹已经想到了这一点。

在API 11以后,BroadcastReceiver新增了一个静态内部类PendingResult,我们可以通过调用goAsync()方法来获取这个PendingResult对象,然后就可以愉快地进行子线程操作了,最后通过调用它的finish方法来结束广播接收者的生命。一切就变得可控了(相当于强行给Receiver续命)。

...
    @Override
    public void onReceive(final Context context, final Intent intent) {
        final PendingResult result = goAsync();
        AsyncHandler.post(new Runnable() {
            @Override
            public void run() {
                // 在这里搞事情
                ...
                // 成功续命,可以手动结束了
                result.finish();
            }
        });
    }
...

要注意的是,尽管这里可以异步操作,但耗时超过10s依然会爆ANR。
其中AsyncHandler是一个简单封装的可复用的异步操作类(来自Android源码):

/**
 * Helper class for managing the background thread used to perform io operations
 * and handle async broadcasts.
 */
public final class AsyncHandler {
 
    private static final HandlerThread sHandlerThread = new HandlerThread("AsyncHandler");
    private static final Handler sHandler;
 
    static {
        sHandlerThread.start();
        sHandler = new Handler(sHandlerThread.getLooper());
    }
 
    public static void post(Runnable r) {
        sHandler.post(r);
    }
 
    public static void postDelayed(Runnable r, long delayedMills) {
        sHandler.postDelayed(r, delayedMills);
    }
 
    public static Message obtain(Runnable r) {
        return Message.obtain(sHandler, r);
    }
 
    public static void sendMessageDelayed(Message message, long delayedMills) {
        sHandler.sendMessageDelayed(message, delayedMills);
    }
 
    public static void removeCallbacks(int what) {
        sHandler.removeMessages(what);
    }
 
    private AsyncHandler() {}
}

最后,我们可以回味一下BroadcastReceiver源码是怎么描述PendingResult和goAsync的:

    /**
     * State for a result that is pending for a broadcast receiver.  Returned
     * by {@link BroadcastReceiver#goAsync() goAsync()}
     * while in {@link BroadcastReceiver#onReceive BroadcastReceiver.onReceive()}.
     * This allows you to return from onReceive() without having the broadcast
     * terminate; you must call {@link #finish()} once you are done with the
     * broadcast.  This allows you to process the broadcast off of the main
     * thread of your app.
     *
     * <p>Note on threading: the state inside of this class is not itself
     * thread-safe, however you can use it from any thread if you properly
     * sure that you do not have races.  Typically this means you will hand
     * the entire object to another thread, which will be solely responsible
     * for setting any results and finally calling {@link #finish()}.
     */
    public static class PendingResult {
...
    /**
     * This can be called by an application in {@link #onReceive} to allow
     * it to keep the broadcast active after returning from that function.
     * This does <em>not</em> change the expectation of being relatively
     * responsive to the broadcast (finishing it within 10s), but does allow
     * the implementation to move work related to it over to another thread
     * to avoid glitching the main UI thread due to disk IO.
     *
     * @return Returns a {@link PendingResult} representing the result of
     * the active broadcast.  The BroadcastRecord itself is no longer active;
     * all data and other interaction must go through {@link PendingResult}
     * APIs.  The {@link PendingResult#finish PendingResult.finish()} method
     * must be called once processing of the broadcast is done.
     */
    public final PendingResult goAsync() {
        PendingResult res = mPendingResult;
        mPendingResult = null;
        return res;
    }
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
06-12 323
在 `onReceive()` 方法开启线程是可能存在问题的,主要有以下几个方面的考虑: 1. 主线程阻塞:`onReceive()` 方法是在主线程执行的,如果在该方法开启一个耗时操作的线程,可能会导致主线程被阻塞,影响应用程序的响应性能。如果耗时操作持续时间较长,甚至可能引发 ANR(Application Not Responding)错误。 2. 生命周期管理:`onReceive()` 方法的执行时间是有限制的,如果在该方法开启一个新的线程,而且该线程的执行时间超过了 `onReceive()` 方法的时间限制,那么系统可能会终止该线程的执行,从而导致未完成的操作。 3. 内存泄漏:如果在 `onReceive()` 方法开启一个线程,并且该线程持有外部对象的引用,如果没有正确地管理线程生命周期,可能会导致内存泄漏问题。例如,如果线程在 `onReceive()` 方法执行完后仍然在运行,并且持有对 `Context` 或其他对象的引用,这些对象无法被及时释放,从而造成内存泄漏。 为了避免以上问题,可以考虑以下几种解决方案: 1. 使用 IntentService:可以将耗时操作放在 IntentService 执行,IntentService 会自动创建一个工作线程来处理任务,在任务执行完毕后自动停止。 2. 使用 AsyncTask:可以在 `onReceive()` 方法使用 AsyncTask 执行耗时操作AsyncTask 可以方便地在后台线程执行任务,并在任务完成后回到主线程更新 UI。 3. 使用 JobScheduler 或 WorkManager:适用于需要执行较长时间的后台任务,可以使用 JobScheduler 或 WorkManager 来调度任务的执行,以避免在 `onReceive()` 方法开启线程。 需要根据具体的场景和需求来选择适合的解决方案,确保在 `onReceive()` 方法不会产生性能问题和内存泄漏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值