美文网首页
Android常见原理性问题

Android常见原理性问题

作者: Echopppppp | 来源:发表于2018-10-10 15:53 被阅读19次
    • Handler机制和底层实现

      在消息接收的线程初始化handler实例,若接收消息的线程非主线程,需要开启looper,主线程默认开启looper,一个线程只有一个looper与一个MessageQueue,可以拥有多个handler。</br>
      一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱

      参考文章

    • Handler、Thread和HandlerThread的差别

      handler:线程间通信</br>
      thread:线程</br>
      HandlerThread:内部拥有并管理一个looper的thread,减少了开发者的工作量

    • handler发消息给子线程,looper怎么启动?

      手动调用 </br>
      Looper.prepare():</br>

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

    Looper.loop();</br>

    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();
    
            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
                Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                msg.target.dispatchMessage(msg);
    
                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();
            }
        }
    
    • ThreadLocal原理,实现及如何保证Local属性?

      ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名ThreadLocalVariable更容易让人理解一些。</br>
      当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。</br>
      可以用于维护单例中的非线程安全对象,从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

      实现:在ThreadLocal类中有一个table,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本

    • 请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系

      在开发Android 应用时必须遵守单线程模型的原则:

      1. 不要阻塞UI线程
      2. 确保只在UI线程中访问Android UI工具包

      关系见上

    • 事件分发中的onTouch 和onTouchEvent 有什么区别,又该如何使用?

      事件处理优先级:onTouchListener(若返回true则不向下调用)> onTouchEvent > onClickListener

    • View刷新机制

      参考文章

    • View绘制流程

      measure->layout->draw</br>
      onMeasure执行:

      • forceLayout为true:这表示强制重新布局,可以通过View.requestLayout()来实现;

      • needsLayout为true,这需要specChanged为true(表示本次传入的MeasureSpec与上次传入的不同),并且以下三个条件之一成立:

        1. sAlwaysRemeasureExactly为true: 该变量默认为false;</br>
        • isSpecExactly为false: 若父View对子View提出了精确的宽高约束,则该变量为true,否则为false</br>
        • matchesSpecSize为false: 表示父View的宽高尺寸要求与上次测量的结果不同</br>

    </br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br>

    • 如何取消AsyncTask?

      cancel方法 --> isCancelled()为true --> 在doInBackground中手动检查决定是否继续运行

      我们可以随时调用 cancel(boolean)去取消这个加载任务,调用这个方法会间接调用 iscancelled 并且返回true 。</br>
      调用cancel()后,在doInBackground()return后 我们将会调用onCancelled(Object) 不在调用onPostExecute(Object)</br>
      为了保证任务更快取消掉,你应该在doInBackground()周期性的检查iscancelled 去进行判断。</br>

    • 为什么不能在子线程更新UI?

      Exception:Only the original thread that created a view hierarchy can touch its views

        void checkThread() {
            if (mThread != Thread.currentThread()) {
                throw new CalledFromWrongThreadException(
                        "Only the original thread that created a view hierarchy can touch its views.");
            }
        }
    

    在Activity创建完成后(Activity的onResume之前ViewRootImpl实例没有建立),mThread被赋值为主线程(ViewRootImpl),所以直接在onCreate中创建子线程是可以更新UI的

    在子线程中添加 Window,并且创建 ViewRootImpl,可以在子线程中更新view

    设计思考:</br>
    Android 的单线程模型,因为如果支持多线程修改 View 的话,由此产生的线程同步和线程安全问题将是非常繁琐的,所以 Android 直接就定死了,View 的操作必须在创建它的 UI 线程,从而简化了系统设计。
      有没有可以在其他非原始线程更新 ui 的情况呢?有,SurfaceView 就可以在其他线程更新。

    参考文章

    相关文章

      网友评论

          本文标题:Android常见原理性问题

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