美文网首页
死磕Handler(2)

死磕Handler(2)

作者: 程序员要多喝水 | 来源:发表于2019-11-21 13:54 被阅读0次

    Handler在Thread使用

    在子线程中使用handler实例:

    public class MainActivity extends Activity {
        private static final String TAG = "MainAty";
        @BindView(R.id.tvText)
        TextView tvText;
        private Handler threadHandler;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
            new Thread(
                    new Runnable() {
                        @Override
                        public void run() {
                            //在子线程中创建handler
                            Looper.prepare();
                            threadHandler = new Handler() {
                                @Override
                                public void handleMessage(Message msg) {
                                    switch (msg.what) {
                                        case 1:
                                            Log.i(TAG, "threadHandler receive msg");
                                            break;
                                    }
                                }
                            };
                            Looper.loop();
                            Log.i(TAG, "thread exit");
                        }
                    }
                    , "thread-1").start();
        }
    
        @OnClick(R.id.tvText)
        public void ClickTextView(){
            //子线程创建的handler发送消息
            threadHandler.sendMessage(threadHandler.obtainMessage(1));
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            //退出时候需要注意Loop还在死循环,需要退出,不然子线程不会退出
            threadHandler.getLooper().quit();
        }
    }
    

    可以看到,在子线程中创建handler需要注意,首先如果没有Looper.prepare();
    那么直接创建会抛出异常,异常大概如下

    java.lang.RuntimeException: Can't create handler inside thread Thread[thread-1,5,main] that has not called Looper.prepare()
           at android.os.Handler.<init>(Handler.java:205)
    

    此异常跟进源码发现:

      public Handler() {
            this(null, false);
        }
      public Handler(Callback callback, boolean async) {
            mLooper = Looper.myLooper();
            //抛出异常的点
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
       
        } 
    

    那分析Looper.prepare到底干了啥Looper.myLooper:

     public static void prepare() {
            prepare(true);
        }
     private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }   
    
    public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    

    Looper.prepare会new Looper()并且挂在到ThreadLocal上,于是当前线程绑定了创建的Looper对象;而Looper.myLooper就是根据当前线程拿到创建的Looper;

    至此如果在子线程创建handler一定需要Looper.prepare;
    而Looper.loop()是进入一个死循环中,保证当前线程不会退出,如果退出了,那发生的Message消息也没办法处理到,源码位置:

     public static void loop() {
            ...
            //死循环,保证不会退出,否则发送的handler消息处理不到
            for (;;) {
                Message msg = queue.next(); // might block
                //没有msg时候退出
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
                ...
            }
            ...
     }
    

    当然这里循环不会造成CPU资源过度消耗,因为queue.next调用nativePollOnce会阻塞线程;但如果Activity退出时候,还是需要将Loop.quit方法去停止死循环,保证不会内存泄露,资源回收及时,如果Loop.quit源码;

        public void quit() {
            mQueue.quit(false);
        }
        void quit(boolean safe) {
                ...
                //移除所有的Message,这样queue.next == null;
                if (safe) {
                    removeAllFutureMessagesLocked();
                } else {
                    removeAllMessagesLocked();
                }
                //queue.next调用nativePollOnce,这里在唤醒
                nativeWake(mPtr);
            }
        }
    

    HandlerThread

    Android考虑到子线程实现这种方式比较繁琐,因此创建了一个HandlerThread类:

    public class HandlerThreadActivity extends Activity {
        private static final String TAG = "HandlerThreadAty";
        @BindView(R.id.tvText)
        TextView tvText;
        private Handler threadHandler;
        private HandlerThread thread;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
            thread = new HandlerThread("handler-thread");
            thread.start();
            threadHandler = new Handler(thread.getLooper()){
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case 1:
                            Log.i(TAG, "threadHandler receive msg");
                            break;
                    }
                }
            };
        }
    
        @OnClick(R.id.tvText)
        public void ClickTextView(){
            //子线程创建的threadHandler发送消息
            threadHandler.obtainMessage(1).sendToTarget();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            //退出时候需要注意HandlerThread.quit保证退出
            thread.quit();
        }
    }
    

    来分析一波HandlerThread源码:

    public class HandlerThread extends Thread {
           int mPriority;
        int mTid = -1;
        Looper mLooper;
        private @Nullable Handler mHandler;
    
        public HandlerThread(String name) {
            super(name);
            mPriority = Process.THREAD_PRIORITY_DEFAULT;
        }
        
        public HandlerThread(String name, int priority) {
            super(name);
            mPriority = priority;
        }
      
        protected void onLooperPrepared() {
        }
    
        @Override
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
        
        public Looper getLooper() {
            if (!isAlive()) {
                return null;
            }
            synchronized (this) {
                while (isAlive() && mLooper == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
            return mLooper;
        }
    
        @NonNull
        public Handler getThreadHandler() {
            if (mHandler == null) {
                mHandler = new Handler(getLooper());
            }
            return mHandler;
        }
    
        public boolean quit() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quit();
                return true;
            }
            return false;
        }
     
        public boolean quitSafely() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quitSafely();
                return true;
            }
            return false;
        }
       
        public int getThreadId() {
            return mTid;
        }
    }
    

    HandlerThread是继承Thread,说白了就是一个单线程,串型执行,执行效率不高,其中有2个构造方法,
    public HandlerThread(String name):只需要指定线程名
    public HandlerThread(String name, int priority) :指定线程名和线程优先级;
    public void run():run方法会调用Looper.prepare()将当前线程和new Looper绑定在ThreadLocal中,然后Looper.myLooper取出放在mLooper;而getLooper就是返回mLooper,也就是返回HandlerThread线程在run方法创建的那个Looper对象;
    public Handler getThreadHandler():getThreadHandler这个方法对外不公开;
    public boolean quit/quitSafely():调用looper.quit/quitSafely退出;

    IntentService

    IntentService是由handlerThread+Handler+Service组成
    分析其源码:

    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() {
            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);
        }
    
        @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);
    }
    

    其在oncreate时候创建一个HandlerThread,并且利用HandlerThread线程的Looper创建了一个mServiceHandler;
    ondestory时候执行mServiceLooper.quit()退出Looper的循环;
    onStartCommand方法中调用onStart,在onStart方法中利用mServiceHandler发送消息,其中msg.arg1会存startId;
    当ServiceHandler收到消息,执行handleMessage后,调用onHandleIntent,最后stopSelf(startId);
    因此说IntentService一个异步Service,其onHandleIntent是在子线程中执行的,并且执行完毕后,会destory self;

    比如startService启动3次,假设在onStartCommand中startId分别为1,2,3,分别调用stopself(1),stopself(2),stopself(3);
    来看为什么stopself没有走到onDestory方法,分析源码:

    //Service#stopSelf
    public final void stopSelf(int startId) {
            ...
                mActivityManager.stopServiceToken(
                        new ComponentName(this, mClassName), mToken, startId);
            ...
        }
    //AMS#stopServiceToken
    @Override
    public boolean stopServiceToken(ComponentName className, IBinder token,
                int startId) {
            synchronized(this) {
                return mServices.stopServiceTokenLocked(className, token, startId);
            }
        }
        
    //AMS#stopServiceTokenLocked
    boolean stopServiceTokenLocked(ComponentName className, IBinder token,
                int startId) {
         
            ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
            if (r != null) {
                if (startId >= 0) {
                    ...
                    //这里判断如果上次startId和这次stop的startId不等,直接退出
                    if (r.getLastStartId() != startId) {
                        return false;
                    }
    
                    ...
                }
                ...
                //真正StopService的地方
                bringDownServiceIfNeededLocked(r, false, false);
                ...
                return true;
            }
            return false;
        }
    

    因此startId分别启动1,2,3时候,mLastStartId = 3,只有调用stopSelf(3)时候才会进入到onDestory生命周期;

    这里需要注意如下几点:
    IntentService只有一个线程,如果多次start,onCreate只会执行一次,onStartCommand会执行多次,那handler发送的消息会有多个,消息队列会依次执行回调onHandleIntent,等所有消息都依次分发执行完毕后,执行完后最后调用到onDestory生命周期,kill self;此外只能通过startService启动,不能通过bindService启动,原因是生命周期onStartCommand不会在bindService中回调;

    相关文章

      网友评论

          本文标题:死磕Handler(2)

          本文链接:https://www.haomeiwen.com/subject/akftwctx.html