Android常见原理性问题

  • Handler机制和底层实现

    在消息接收的线程初始化handler实例,若接收消息的线程非主线程,需要开启looper,主线程默认开启looper,一个线程只有一个looper与一个MessageQueue,可以拥有多个handler。

    一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱

    参考文章

  • Handler、Thread和HandlerThread的差别

    handler:线程间通信

    thread:线程

    HandlerThread:内部拥有并管理一个looper的thread,减少了开发者的工作量

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

    手动调用

    Looper.prepare():

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

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更容易让人理解一些。

    当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

    可以用于维护单例中的非线程安全对象,从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 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

    onMeasure执行:

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

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

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


































  • 如何取消AsyncTask?

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

    我们可以随时调用 cancel(boolean)去取消这个加载任务,调用这个方法会间接调用 iscancelled 并且返回true 。

    调用cancel()后,在doInBackground()return后 我们将会调用onCancelled(Object) 不在调用onPostExecute(Object)

    为了保证任务更快取消掉,你应该在doInBackground()周期性的检查iscancelled 去进行判断。

  • 为什么不能在子线程更新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

设计思考:

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

参考文章

你可能感兴趣的:(Android常见原理性问题)