精华内容
下载资源
问答
  • 学习目标01知识目标理解多线程的概念掌握两种多线程的实现方法02能力目标会使用继承Thread类和实现Runnable接口两种方法完成多线程多线程的概念什么是多线程多线程multi-thread指在单个的程序内可以同时运行多个...
  • ``` public class MyScrollView extends ScrollView { ...因为平时自己的写法都先写postdelay然后里面再写里面的runnable方法,对于他这种写法,不能理解,也不能明白为什么能够自己发送自己?求前辈们指点
  • 我的方法:在一个线程里,开始播放后,...接着就判断,如果不是当前item,该什么样子就什么样子,如此迭代就可以了。 /** 轨迹回放逻辑处理 **/  private Runnable rz = new Runnable()  {  @Override  

    我的方法:在一个线程里,开始播放后,遍历存放item的list,找到当前的item,移除他的marker,然后给他换一个marker,再添加到list里面,然后睡眠;接着就判断,如果不是当前item,该什么样子就什么样子,如此迭代就可以了。

    /** 轨迹回放逻辑处理 **/
     private Runnable rz = new Runnable()
     {
      @Override
      public synchronized void run()
      {
       while (isStartRun)
       {
        for (int i = index; i < mGeoList.size(); i++)
        {
         OverlayItem item = mGeoList.get(i);
         flag = index;
         sendMsg(MapApplication.LOCATEOVER_NOTICE_ADAPTER);
         mOverlayGraphic.removeItem(item);
         item.setMarker(res.get(3));
         if (!isBcak)
         {
          break;
         }
         mOverlayGraphic.addItem(item);
         mMapView.refresh();
         SystemUtils.sleep(1000);
         mOverlayGraphic.removeItem(item);
         if (index == 0)
         {
          item.setMarker(res.get(1));
         }
         else if (index == mGeoList.size() - 1)
         {
          item.setMarker(res.get(2));
          isStartRun = false;
          isFinish = false;
         }
         else
         {
          View view = mInflater.inflate(R.layout.overlay_item, null);
          TextView userItemTv1 = (TextView) view.findViewById(R.id.item_tv1);
          userItemTv1.setText(index + 1 + "");
          Bitmap bmp = userLocateUtil.createNewBitmap(view);
          Drawable drawable = new BitmapDrawable(bmp);
          item.setMarker(drawable);
         }
         if (!isBcak)
         {
          break;
         }
         mOverlayGraphic.addItem(item);
         mMapView.refresh();
         index++;
         if (!isFinish)
         {
          index = 0;
          sendMsg(MapApplication.LOCATEOVER_PLAY_FINISH);
          break;
         }
        }
       }
      }
     };

    原文:http://www.oschina.net/question/992700_120084

    展开全文
  • (尤其第二个,报的最多,我百度了好久也没明白什么意思) (顺便问一下大佬,这个HttpURLConnection从哪一步开始向服务器发起连接请求及数据的---比如OkHttp里面有一句 ``` Request request=new Request....
  • 贴出来 ublic class Cloud implements Runnable...Context.MODE_PRIVATE 下面有浪线 请问是什么原因 报错是Wrong 2nd argument type. Found: 'int', required: 'android.database.sqlite.SQLiteDatabase.CursorFactory
  • 抱歉麻烦各位了,本人大四狗,目前在看安卓开发,学习mars老师的音乐播放器时遇到了 个问题,就是设置了SimpleAdapter之后仍然无法显示歌名的内容,只显示了个空白的 TEXTVIEW,我研究了无数次依旧没有发现问题在...
  • 原代码如下,如果我不用BUTTON去触发的话可以建立SOCKET的,但是用上BUTTON,把建立SOCKET的方法放到子线程中,仍然exception会显示不能再主线程中使用网络操作,真心求解为什么,看了2天了,刚学JAVA,不太理解。...
  • 不到myeclipse的服务器,这什么呢? myeclipse的服务器 package server; import java.io.IOException; import java.io.ObjectInputStream; import java.net.InetSocketAddress; import java.net.ServerSocket;...
  • Runnable runnable=new Runnable() { @Override public void run() { try { Result result = loadResult(width, height, uri, text); if (result != null) { LoaderResult loaderResult = new LoaderResult...
  • new Thread(new Runnable() { //获取内容 @Override public void run() { GethomeInfoThread gethomeInfoThread=new GethomeInfoThread(); gethomeInfoThread.run(); } }).start(); return view; }...
  • 3、 View.post(Runnable action) ,runOnUiThread(Runnable action)和Handler.post(Runnable action)区别 4、 为什么主线程不会因为Looper.loop()方法造成阻塞 5、 为什么主线程会ANR而子线程不会呢 6、...

    消息机制

    你了解安卓中的消息机制吗;
    1、一个线程可以存在多个消息队列吗
    2、延迟消息是怎么处理的
    3、 View.post(Runnable action) ,runOnUiThread(Runnable action)和Handler.post(Runnable action)区别
    4、 为什么主线程不会因为Looper.loop()方法造成阻塞
    5、 为什么主线程会ANR而子线程不会呢
    6、 HandlerThread原理
    7、 IntentService原理

    消息机制
    我们从常用的Handler.post(Runnable action)去一步步解析消息机制

    • 不管是post(),postDelayed(),sendMessage()等方法最后会调sendMessageDelayed();
    public final boolean post(Runnable r){
           return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    • 把runnable或者what都封装成Message
    private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
    }
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
            Message msg = Message.obtain();
            msg.what = what;
            return sendMessageDelayed(msg, delayMillis);
    }
    
    • 消息发送。注意SystemClock.uptimeMillis() + delayMillis SystemClock.uptimeMillis() 是本地方法获取当前时间
    public final boolean sendMessageDelayed(Message msg, long delayMillis) {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    • 加入消息队列。enqueueMessage(queue, msg, uptimeMillis)。注意这里msg.target = this
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
    }
    
    • MessageQueue 消息队列怎么来的呢
      我们从Handle构造方法开始找
    public Handler(Callback callback, boolean async) {
            mLooper = Looper.myLooper();
            //这里的有没有遇到过,我们平常new的Thread是没有mLooper的。在子线程里直接new Handle就会抛出这个
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
    }
    

    -消息队列是从Looper里来拿的,那Looper又是怎么来的呢

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
    }
    
    再看看 sThreadLocal.get()
    public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
           return setInitialValue();
    }
    通过set(T value)方法再看看looper是怎么加到ThreadLocalMap里的
    public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
    }
    
    • Looper又是在哪里实例化并是怎么加到ThreadLocalMap里的呢
        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));
        }
        
        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    
    -前面有提到过"消息队列是从Looper里来拿的"然后一个Thread只有有一个Looper,所以一个Thread只能有一个Looper,对应一个消息列队。
    
    • 前面有提到线程默认是没有Looper的,那么主线程在哪里创建了Looper呢
    //ActivityThread的main方法
        public static void main(String[] args) {
            Looper.prepareMainLooper();
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
            Looper.loop();
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    
    • 加到消息队列
        boolean enqueueMessage(Message msg, long when) {
            ...
            synchronized (this) {
                msg.markInUse();
                msg.when = when;
                //这个mMessages是链表的头
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    // Inserted within the middle of the queue.  Usually we don't have to wake
                    // up the event queue unless there is a barrier at the head of the queue
                    // and the message is the earliest asynchronous message in the queue.
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    for (;;) {
                        prev = p;
                        p = p.next;
                        if (p == null || when < p.when) {
                            break;
                        }
                        if (needWake && p.isAsynchronous()) {
                            needWake = false;
                        }
                    }
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
                }
    
                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    

    通过上边代码可以看到消息是按when排序好的。在插入消息前会记录消息要发送的时间。这样就可以处理延时消息了

    万事俱备,就差牛逼的消息获取以及分发了

    • 死循环一直从MessageQueue中遍历消息
      我们都知道一个线程做完一件事情就没了,但主线程就算没事做也不能结束掉呀。很简单,一个死循环。
     public static void loop() {
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            final MessageQueue queue = me.mQueue;
    
            // Make sure the identity of this thread is that of the local process,
            // and keep track of what that identity token actually is.
            Binder.clearCallingIdentity();
            final long ident = Binder.clearCallingIdentity();
    
            boolean slowDeliveryDetected = false;
    
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
                // This must be in a local variable, in case a UI event sets the logger
                final Printer logging = me.mLogging;
                if (logging != null) {
                    //有没有人用过BlockCanary,原理很简单
                    //在事件分发前后做记录,就能分析出在dispatchMessage中执行了耗时操作
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
                try {
                    msg.target.dispatchMessage(msg);
                } 
                if (logging != null) {
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                }
                msg.recycleUnchecked();
            }
        }
    

    关键是两个点

    • 获取消息: Message msg = queue.next(); // might block
     Message next() {
            // Return here if the message loop has already quit and been disposed.
            // This can happen if the application tries to restart a looper after quit
            // which is not supported.
            final long ptr = mPtr;
            if (ptr == 0) {
                return null;
            }
    
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
    
                nativePollOnce(ptr, nextPollTimeoutMillis);
    
                synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        if (now < msg.when) {
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // Got a message.
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                    }
    
                    // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
    
                    // If first time idle, then get the number of idlers to run.
                    // Idle handles only run if the queue is empty or if the first message
                    // in the queue (possibly a barrier) is due to be handled in the future.
                    if (pendingIdleHandlerCount < 0
                            && (mMessages == null || now < mMessages.when)) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    if (pendingIdleHandlerCount <= 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }
    
                    if (mPendingIdleHandlers == null) {
                        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                    }
                    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
                }
    
                // Run the idle handlers.
                // We only ever reach this code block during the first iteration.
                for (int i = 0; i < pendingIdleHandlerCount; i++) {
                    final IdleHandler idler = mPendingIdleHandlers[i];
                    mPendingIdleHandlers[i] = null; // release the reference to the handler
    
                    boolean keep = false;
                    try {
                        keep = idler.queueIdle();
                    } catch (Throwable t) {
                        Log.wtf(TAG, "IdleHandler threw exception", t);
                    }
    
                    if (!keep) {
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }
    
                // Reset the idle handler count to 0 so we do not run them again.
                pendingIdleHandlerCount = 0;
    
                // While calling an idle handler, a new message could have been delivered
                // so go back and look again for a pending message without waiting.
                nextPollTimeoutMillis = 0;
            }
        }
    

    1、拿到消息,判断消息when
    1.1、立即返回message去分发
    1.2、延时消息计算要分发的时间nextPollTimeoutMillis,会调用本地的方法nativePollOnce(ptr, nextPollTimeoutMillis);
    没有消息nextPollTimeoutMillis = -1 会一直阻塞操作,等待新消息进来
    延时消息nextPollTimeoutMillis > 0 会一直阻塞操作,到点后或者新消息进来唤醒
    1.3、nativePollOnce()方法里本质同步I/O,即读写是阻塞的,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。

    具体nativePollOnce()的Native层实现,想深入研究可查看资料: Android消息机制2-Handler(Native层)

    • 消息分发:msg.target.dispatchMessage(msg);前面有提到要注意Message加入列表前msg.target = this。所以消息分发就是调的Handle的dispatchMessage(msg)
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
    • runOnUiThread(Runnable action)就很简单了,看一下源码
        public final void runOnUiThread(Runnable action) {
            if (Thread.currentThread() != mUiThread) {
                mHandler.post(action);
            } else {
                action.run();
            }
        }
    
    • View.post(Runnable action)复杂一点,我们平时有尝试过拿View的宽高,在onCreate(Bundle savedInstanceState)甚至在onResume() 里都是拿到宽高为0。然后通过View.post(Runnable action)方法在run()里却拿到真正的宽高。
      首先我们看一下post()代码
        public boolean post(Runnable action) {
            final AttachInfo attachInfo = mAttachInfo;
            if (attachInfo != null) {
                return attachInfo.mHandler.post(action);
            }
            // Postpone the runnable until we know on which thread it needs to run.
            // Assume that the runnable will be successfully placed after attach.
            getRunQueue().post(action);
            return true;
        }
    

    代码以及注释很明显,如果AttachInfo 不为空就走的mHandler.post(action);否则推迟这个Runnable直到知道要在哪个线程去运行。那这个getRunQueue().post(action)应该就是暂时缓存起来了。

        public void post(Runnable action) {
            postDelayed(action, 0);
        }
        public void postDelayed(Runnable action, long delayMillis) {
            final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
            synchronized (this) {
                if (mActions == null) {
                    mActions = new HandlerAction[4];
                }
                mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
                mCount++;
            }
        }
    

    1、我们先看AttachInfo。mAttachInfo是在View第一次attach到Window时,ViewRoot传给自己的子View的。这个AttachInfo之后,会顺着布局体系一直传递到最底层的View。

        void dispatchAttachedToWindow(AttachInfo info, int visibility) {
            mAttachInfo = info;
            ...
            if (mRunQueue != null) {
                mRunQueue.executeActions(info.mHandler);
                mRunQueue = null;
                }
            }
     void dispatchDetachedFromWindow(){
     ... 
     mAttachInfo = null;
     }
    
    • HandlerThread 其实HandlerThread 继承Thread,但是它实现了消息队列。特点就是开启一个线程按消息发送顺序串行地处理事务,可以复用,不用重新new Thread。
    • IntentService 继承 Service。内部用了HandlerThread去起一个子线程做耗时操作,最后stopSelf()。
    展开全文
  • 什么是Handler? Handler是安卓消息机制的一个上层接口。 handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。 1.可以让对应的Message和Runnable在未来某个时间点进行相应处理。 2.让自己...

    什么是Handler?

    Handler是安卓消息机制的一个上层接口。
    handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。
    1.可以让对应的Message和Runnable在未来某个时间点进行相应处理。
    2.让自己想要处理的耗时操作放在子线程,让更新ui的操作放在主线程。
    

    Handler的使用

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            
        }
    };
    
    //主线程执行的
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            mHandler.postDelayed(runnable, 1000);
        }
    };
    
    
    //方式1:
    mHandler.post(runnable);
    
    //方式2:
    Message msg=Message.obtain();
    msg.what=1;
    mHandler.sendMessage(msg);
    

    Handler的使用主要有两种方式,如上。

    方式1内部其实也是调用的sendMessage(msg)方法,但是它不会交给Handler的handleMessage(Message msg)来处理,而是调用Runnable的run()方法。这种主要在定时循环的需求上使用。

    方式2是直接发了一个Message消息,最终会由Handler的handleMessage(Message msg)来处理。这种方式往往在子线程调用,用来更新UI。

    这里可能大家会有个疑问,Looper呢?不是说Looper是用来循环读取消息的吗?在哪里呢?其实我们看ActivityThread的源码就知道了。程序启动的时候已经帮我们创建了Looper,并且开启了循环loop()。

    public static void main(String[] args) {
       Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
    
        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
       // StrictMode) on debug builds, but using DropBox, not logs.
       CloseGuard.setEnabled(false);
    
       Environment.initForCurrentUser();
    
       // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());
    
        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
    
       Process.setArgV0("<pre-initialized>");
    
       //创建Looper
       Looper.prepareMainLooper();
    
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
    
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
    
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
    
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        
        //开启循环
        Looper.loop();
    
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    

    所以,我们使用的时候不再创建Looper。但是我们要在子线程去轮询并处理消息呢?是需要我们自己创建Looper的。

    private void startThread(){
       MyThread myThread=new MyThread();
       myThread.start();
       
       
       Message msg=Message.obtain();
       myThread.handler.sendMessage(msg);
    }
    
    class MyThread extends Thread{
        Handler handler;
        
        @Override
        public void run() {
            Looper.prepare();
    
            handler=new Handler(){
                @Override
                public void handleMessage(Message msg) {
    
                }
            };
    
            Looper.loop();
        }
    }
    

    Handler 核心类与源码分析

    Message

    Messgae是在线程之间传送的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。Message的what子段表示携带了什么类型的信息,obj字段表示携带一个Object对象。

    • Message的创建
    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    

    我们创建消息,应避免使用new Message()的方式,而是使用obtain()。这个方法会从一个全局的pool里返回一个实例,避免分配过多的内存。其实采用了单链表存储已经回收了的Message,会每次取头结点的Message来复用,取完后第二个节点变成了头结点,单链表长度-1。当然了,没有可利用的Message,还是会默认通过new Message()来创建。

    • Message的回收
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;
    
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
    

    Message回收后,存储回收Message的单链表会将该Message添加为头结点。

    需要注意区分的:
    sPool是静态的,为所有该类实例共享。
    next是非静态的,为该类每个实例单独所有。
    

    Handler

    Handler就是消息处理者,它主要用于发送消息和处理消息。发出的消息经过一系列处理,最终会传递到Handler对象的handleMessage()方法中再分门别类的处理。

    我们先看一下和发消息相关的代码:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
    
    
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    
    

    post(Runnable r)内部封装了一个Message(这个Message的callback被赋值了,在处理消息时来区分交给谁来处理),最终也是调用了sendMessageAtTime(Message msg, long uptimeMillis)方法。不管是方式1还是方式2,最终都会调用queue.enqueueMessage(msg, uptimeMillis)来将消息添加到消息队列中。

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
    
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//说明是post(Runnable r),交给run()方法处理
            handleCallback(msg);
        } else {
            if (mCallback != null) {//如果我们创建Handler时传了回调
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            
            //如果我们创建Handler没有传Callback,那么由这个方法处理,我们需要重新这个方法。
            handleMessage(msg);
        }
    }
    

    dispatchMessage(Message msg)这个方法是在Looper的loop()方法中被调用的。

    MessageQueue

    MessageQueue是消息队列的意思,它主要用来存放发送的消息。通过Handler发送的消息会存入MessageQueue中等待处理,每个线程中只会有一个MessageQuery 对象。

    它通过一个单链表的数据结构来维护消息队列的,在插入和删除上比较有优势。
    我么先看一下插入的过程:

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
    
        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
    
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
    
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
    
    

    mMessages是链表的头结点,when是消息执行的时间。

    如果链表头结点为null(也就是链表空)或者该消息立即执行或者该消息执行的时间比头结点消息执行的时间还早,那么把该消息插入到头结点p的前面,该消息为头结点。

    如果不是上面那几种情况,开启了死循环遍历链表。跳出死循环的条件是p == null || when < p.when,也就是说某个节点为null(前一个节点就是末尾了呗)或者该节点消息执行时间大于了该消息的时间。然后将该消息插入到合适的(按执行时间when)节点。

    下面看一下取消息的过程,但是我们需要先知道一个概念:消息屏障,也可以说是同步屏障。简单来说,设置了同步屏障之后,Handler只会处理异步消息。因为在next()方法中,会判断忽略同步的消息。那么怎么设置同步屏障呢?postSyncBarrier()方法。

    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }
    
    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;
    
            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }
    

    由源码可见,同步屏障的具体实现是在消息队列中添加了一个target==null的Message。

    下面我们来看next()源码:

    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
    
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
            //把绑定在当前线程中的命令,刷新到内核中,用来阻塞线程
                Binder.flushPendingCommands();
            }
    
            nativePollOnce(ptr, nextPollTimeoutMillis);
    
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                
                //如果设置了同步屏障
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                        
                        //消息为异步则跳出该循环
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {//如果该消息的执行执行还没到
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        //设置一个等待时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {//如果该消息执行时间到了
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            //那msg肯定是头结点,把头结点的next 赋值给mMessage头结点
                            mMessages = msg.next;
                        }
                        
                        //移除当前msg的next指针域的引用
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
    
                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
    
                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
    
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
    
            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler
    
                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
    
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
    
            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;
    
            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }
    

    有消息可以处理,则取出并移除该消息。如果没有,则阻塞。阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。也就是nativePollOnce(ptr, nextPollTimeoutMillis)这个方法。通过Native层的MessageQueue阻塞nextPollTimeoutMillis毫秒的时间。

    • nextPollTimeoutMillis=-1 一直阻塞不会超时
    • nextPollTimeoutMillis=0 不会阻塞,立即返回
    • nextPollTimeoutMillis>0 阻塞nextPollTimeoutMillis毫秒,期间被唤醒则立即返回

    我们经常为了防止内存泄漏而调用removeCallbacksAndMessages(Object token),这个方法内部会调用MessageQueue的下面方法:

    void removeCallbacksAndMessages(Handler h, Object object) {
        if (h == null) {
            return;
        }
    
        synchronized (this) {
            Message p = mMessages;
    
            // Remove all messages at front.
            while (p != null && p.target == h
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }
    
            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }
    

    从队列中删除所有匹配的元素。先从队首删除,如果删除了则队首指向接下来的元素,重复这个过程,直到第一个不匹配的元素出现。接着从这个元素之后开始查找并删除。

    Looper

    Looper是每个线程中的MessageQueue的管家,每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的dispatchMessage(msg)方法中。每个线程也只会有一个Looper对象。

    我们先看一下Looper的创建:

    
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    
    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 void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    

    我们可以通过prepare(boolean quitAllowed)在子线程中创建一个Looper,quitAllowed参数表示是否允许退出。应用启动的时候已经通过prepareMainLooper()创建了Looper了,不需要也不允许我们再次在主线程中创建。并且提供了一个getMainLooper()来获取主线程的Looper。

    Looper最重要的方法就是loop()方法:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
    // //获取当前Looper对象 因为是ThreadLocal中取出可以的保证拿到的是当前线程的Looper。
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
    
        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
    
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
    
            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
    
            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
    
            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }
    
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
    
            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }
    
            msg.recycleUnchecked();
        }
    }
    

    loop()里面获取了当前线程的Looper以及与之关联的MessageQueue。开启了一个死循环,不断地从消息队列MessageQueue拿出消息处理。那么问题就来了。

    • Question1:这个死循环什么时候退出呢?
    queue.next()返回的为null,死循环退出。那么什么时候返回null呢?
    如果你是在子线程中自己创建的Looper,你可以调用Looper的quit()或者quitSafely()即可退出循环。二者的区别是前者直接退出,后者将消息队列中已有的消息处理完再退出。
    
    主线程的Looper呢?很抱歉,Main thread not allowed to quit.主线程的这个死循环退出了应用程序就结束了。
    
    • Question2:主线程的Looper开启了loop()死循环,为什么不会卡死?
    1.首先需要说明的一点是,正是这个死循环保证了应用程序的正常运行。
    2.Looper.loop() 当消息队列为空时,是阻塞状态。Looper空闲,线程进入阻塞,释放CPU执行权,等待唤醒。当有消息的时候就进行消息处理,然后在这个循环里面去执行的。
    3.卡死,我们一般说在主线程进行了耗时操作,可能会卡死。四大组件相关等一些操作都是通过Handler机制进行的,如果在主线程进行耗时操作,可能会导致期间其它的事件无法处理,进而卡死。
    
    • Question3:从消息队列MessageQueue拿出消息来交给谁处理?
    由msg.target.dispatchMessage(msg)可以看出,消息交给了msg.target来处理,也就是Handler的实例。
    
    
    • Question4:Message会不会被回收?
    消息被对应的Handler处理完以后,调用msg.recycleUnchecked();相关源码见上文。
    

    内存泄漏

    原因:非静态内部类持有外部类的引用。

    正确的写法:Handler写成静态的内部类,并持有外部类弱引用。

    MyHandler myHandler=new MyHandler(this);
    private static class MyHandler extends Handler {
    
        private WeakReference<Context> reference;
    
        public MyHandler(Context context) {
            reference=new WeakReference<>(context);
        }
    
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            AccountActivity accountActivity = (AccountActivity) reference.get();
        }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        myHandler.removeCallbacksAndMessages(null);
    }
    
    

    结束语

    欢迎留言交流。
    文章同步在 https://gxh-apologize.github.io/ ,欢迎访问。

    展开全文
  • Handler知识点

    2018-02-18 14:04:19
    一、什么是Handler? Handler是安卓消息机制的上层接口,在子线程中进行耗时操作后如果需要更新UI就把消息发送给Handler重写Handler的handlerMessage方法进行更新UI。Handler通过发送和处理message和runnable对象...

    一、什么是Handler?

            Handler是安卓消息机制的上层接口,在子线程中进行耗时操作后如果需要更新UI就把消息发送给Handler重写Handler的handlerMessage方法进行更新UI。Handler通过发送和处理message和runnable对象关联对应线程的MessageQueue。

            1、可以让对应的Message和Runnable在未来某个时间点进行相应处理

            2、让自己想要处理的耗时操作放在子线程中,让更新UI放在主线程中。

    二、Handler的使用方法

            1、post(runnable)


                 重写自定义Thread类的run方法执行耗时操作,并在里面实例化Runable类并重写run方法run方法里就是你期望在UI线程里面的更新操作,最后用跟UI线程绑定的Handler.post方法执行runnable。


                post方法里面根本还是调用了sendMessage方法,只是将sendMessage方法进行了一个简单的封装。

            2、sendMessage(message)

                首先第一步,还是要实例化UIHandler并重写他的handlerMessage方法这里面可进行相关的UI操作,此处传入的Message对象就是我们在子线程中包装好的Message对象,是用来判断到底是谁给我发送的消息,以及携带的其他相关信息。


                第二步,同样是重写自定义Thread的run方法,但是这里创建了一个Message对象,Message的what属性标识一个识别码,通过这个识别码可以识别出不同的Meassage,根据不同的Message可以做出不同的操作;Message的arg1和arg2属性也是用来赋值的但这个只是知道就可以。最后通过UIHandler.sendMessage方法把Message发送出去。


    三、Handler机制原理

            

            Looper(消息泵):是每个线程所独有的,通过loop()方法读取MessageQueue消息池的消息,读到消息后就把消息发送个Handler进行处理。

         MessageQueue(消息队列):消息队列先进先出的方式来管理Message,在创建Loop的时候同时也创建了MessageQueue,在上图中的Loop成员变量中可以看到MessageQueue对象

            Message(消息对象):what识别码,arg1和arg2传递一些简单的数据,obj对象可以传递一些复杂一点的对象

            Handler(处理者):故名思议是对looper发送过来的消息进行处理,同时也可以发送消息;handler并不是漫无目的的发送消息的,他不能发送到其他的线程,只能发送他相关的线程,也就是他相关线程的MessageQueue当中,MessageQueue又是跟Looper所关联的,所以所Handler发送消息必须要有一个维护他的Looper。


    四、Handler引起的内存泄漏以及解决办法

    原因:静态内部类持有外部类的匿名引用,导致外部Activity无法释放。

    解决:handler内部持有外部activity的弱引用,并把handler改为静态内部类,或者在activity的onDestory里面调用handler.removeCallback()方法。

    UIHandler用static修饰

    展开全文
  • 手机华为,安卓系统4.4.2, 代码网上下载的,弄的几个都一样问题,这完整版的都有问题,我还学什么鬼 @Override public void onError(Exception e)  { // 通过runOnUiThread()方法回到主线...
  • android中的view.post()

    2016-04-20 23:24:33
    对于做过安卓的人应该多少都会了解view.post()的作用。 view.post(new runnable{ run(){ // } }); post中的代码会在ui主线程调用的。 但是会在什么时候调用,这个我之前也不是很清楚,直到今天群里...
  • 球球好心人康康,实在没辙了,本人小菜鸟,公司就我一个安卓开发,也没人可问。 HashMap遍历删除写在方法内的一个线程里: ``` new Thread(new Runnable() { @Override public void run() { Iterator ...
  • 基于Android Studio编写Socket的客户端,与基于Python的Socket服务器进行通信。 这MainActivity相关代码: ``` import android.annotation.SuppressLint;...从上面的代码中能看出有什么不妥吗,急求大神指点!!!
  • webview自定义字体显示不出来,颜色、字体大小、背景什么的都能获得,就这个font-family获取不了,请问是什么原因呢?谢谢啊!先上主要代码: ``` public class MainActivity extends BaseActivity { Context ...
  • 安卓版本9,api29 出错图片: ![图片说明](https://img-ask.csdn.net/upload/202004/17/1587129741_865891.png) json数据使用的气象局提供的免费数据: ![图片说明]...
  • 然后新建一个安卓工程,将unity导出的资源一一拷贝进去,然后在mainActivity中跳转至unityActivity,但是每次finish当前unityactivity都会crash掉,下面我的安卓代码,求大神们帮忙看看这什么,感激不尽!...
  • 知识体系:对安卓知识体系做整理 登录注册:登录、注册、Cookie持久化 我的收藏页面:依靠Cookie持久化,实现对文章的收藏和展示 项目分类:在WanAndroid上发布的项目 网址导航:展示常用的开发网站 搜索功能:输入...
  • 安卓程序连接MySQL一直不成功,本地连接可以。我用手机调试的,点击接mysql按钮时,APP闪退。在网上找了好多方法都不行。求看看什么问题 错误日志如下: 2019-08-25 08:25:33.377 5570-5570/mark.zhang E/...

空空如也

空空如也

1 2
收藏数 24
精华内容 9
关键字:

安卓runnable是什么