安卓进阶——原理机制

一、事件传递机制

1. 核心方法

java 复制代码
// ViewGroup的三个关键方法
public boolean dispatchTouchEvent(MotionEvent ev)  // 分发事件
public boolean onInterceptTouchEvent(MotionEvent ev) // 拦截事件
public boolean onTouchEvent(MotionEvent event)      // 消费事件

// View的两个关键方法
public boolean dispatchTouchEvent(MotionEvent ev)
public boolean onTouchEvent(MotionEvent event)

2. 传递流程

自上而下分发,自下而上处理

  • ACTION_DOWN 决定事件链:

    • Activity → Window → DecorView → ... → 子View

    • 任一View的 onTouchEvent() 返回true,则成为目标视图(TARGET)

  • ACTION_MOVE/UP 跟随事件链:

    • 直接发送给TARGET,不再进行拦截判断

3. 关键规则

复制代码
// 1. 拦截器规则
// 父View一旦拦截(返回true),后续事件(MOVE/UP)不再询问,直接给父View处理
// 但子View可以调用requestDisallowInterceptTouchEvent()禁止父View拦截

// 2. 消费规则
// onTouchListener > onTouchEvent > onClickListener
// 优先级:setOnTouchListener > 自身onTouchEvent > setOnClickListener

// 3. 事件序列
// DOWN → MOVE* → UP/CANCEL (一个完整的触摸序列)

4. 实战技巧

复制代码
// 解决滑动冲突的核心代码
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mLastX = ev.getX();
            mLastY = ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            float deltaX = Math.abs(ev.getX() - mLastX);
            float deltaY = Math.abs(ev.getY() - mLastY);
            // 水平滑动距离大于垂直,父View拦截
            if (deltaX > deltaY && deltaX > mTouchSlop) {
                return true; // 拦截事件
            }
            break;
    }
    return super.onInterceptTouchEvent(ev);
}

二、View绘制流程

1. 三个阶段总览

复制代码
// 三大流程在ViewRootImpl.performTraversals()中触发
performMeasure()  // 测量: 确定View的宽高(MeasureSpec)
performLayout()   // 布局: 确定View的位置
performDraw()     // 绘制: 渲染到屏幕

2. MeasureSpec机制

复制代码
// MeasureSpec = mode + size (32位int, 前2位mode, 后30位size)
public static class MeasureSpec {
    public static final int UNSPECIFIED = 0 << MODE_SHIFT; // 父View不限制(如ScrollView)
    public static final int EXACTLY     = 1 << MODE_SHIFT; // 精确值(match_parent/具体dp)
    public static final int AT_MOST     = 2 << MODE_SHIFT; // 最大限制(wrap_content)
}

// 在onMeasure中必须使用MeasureSpec.getMode()和getSize()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    // ... 计算最终宽高
    setMeasuredDimension(width, height);
}

3. 绘制流程详解

复制代码
// 1. MEASURE阶段
ViewRootImpl.performMeasure()
  → View.measure()
    → onMeasure() // 子类必须实现
      // 对于ViewGroup,需遍历measure所有子View
      → measureChildren()
        → child.measure()

// 2. LAYOUT阶段
performLayout()
  → View.layout(l, t, r, b) // 设置最终位置
    → onLayout() // ViewGroup必须实现,摆放子View

// 3. DRAW阶段
performDraw()
  → View.draw(Canvas)
    1. drawBackground()      // 绘制背景
    2. onDraw()              // 绘制自身内容(重写此方法)
    3. dispatchDraw()        // ViewGroup绘制子View
    4. onDrawForeground()    // 绘制装饰(scrollBar等)

4. 性能优化

复制代码
// 1. 避免过度绘制
// 2. 在onDraw中避免创建对象
@Override
protected void onDraw(Canvas canvas) {
    // 错误:频繁创建Paint
    // Paint paint = new Paint(); 
    
    // 正确:复用对象
    canvas.drawRect(mRect, mPaint);
}

// 3. 使用ViewStub延迟加载
// 4. 使用merge标签减少层级

三、Handler机制

1. 核心组件

复制代码
// 1. Looper: 循环读取消息队列
// 2. MessageQueue: 消息队列(底层为链表, native实现)
// 3. Handler: 发送和处理消息
// 4. Message: 消息载体

2. 工作原理

复制代码
// 主线程Looper初始化(ActivityThread.main)
Looper.prepareMainLooper(); // 创建Looper和MessageQueue
// Looper.loop() 死循环开始工作

// Loop循环核心逻辑
for (;;) {
    Message msg = queue.next(); // 可能阻塞
    if (msg == null) return; // 退出循环
    
    msg.target.dispatchMessage(msg); // target就是Handler
    msg.recycleUnchecked(); // 消息回收
}

3. 内存泄漏与解决

复制代码
// 错误写法:隐式持有外部类引用
private Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        // 隐式持有Activity引用
    }
};

// 正确写法1:静态内部类 + WeakReference
private static class MyHandler extends Handler {
    private WeakReference<Activity> mRef;
    MyHandler(Activity activity) {
        mRef = new WeakReference<>(activity);
    }
    @Override
    public void handleMessage(Message msg) {
        Activity activity = mRef.get();
        if (activity != null) {
            // 处理消息
        }
    }
}

// 正确写法2:View.post() + 生命周期移除
@Override
protected void onDestroy() {
    super.onDestroy();
    mHandler.removeCallbacksAndMessages(null); // 移除所有回调
}

4. 高级应用

复制代码
// 1. IdleHandler: 主线程空闲时执行
Looper.myQueue().addIdleHandler(() -> {
    // 延迟初始化、埋点上报等
    return false; // true表示保留,false执行后移除
});

// 2. Barrier(同步屏障): 优先处理异步消息
// 用于UI绘制,确保VSync信号优先处理
MessageQueue.postSyncBarrier();

// 3. HandlerThread: 自带Looper的子线程
HandlerThread thread = new HandlerThread("Worker");
thread.start();
Handler workerHandler = new Handler(thread.getLooper());

四、动画机制

1. 三种动画对比

类型 View Animation Property Animation Transition
API Tween/Frame ObjectAnimator/ValueAnimator Transition API
特点 仅视觉变化 真实改变属性 场景切换
性能 较差
适用 简单效果 复杂交互 Activity/Fragment切换

2. 属性动画核心

复制代码
// 1. ObjectAnimator: 直接修改属性
ObjectAnimator animator = ObjectAnimator.ofFloat(
    view, "translationX", 0f, 100f
);
animator.setDuration(300);
animator.setInterpolator(new FastOutSlowInInterpolator());
animator.start();

// 2. ValueAnimator: 自定义属性
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
valueAnimator.addUpdateListener(animation -> {
    int value = (int) animation.getAnimatedValue();
    // 手动更新属性
    view.getLayoutParams().height = value;
    view.requestLayout();
});

// 3. AnimatorSet: 组合动画
AnimatorSet set = new AnimatorSet();
set.playTogether(
    ObjectAnimator.ofFloat(view, "scaleX", 1f, 1.5f),
    ObjectAnimator.ofFloat(view, "scaleY", 1f, 1.5f)
);
set.setDuration(500).start();

3. 硬件加速与离屏缓冲

复制代码
// 开启硬件加速
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);

// 动画结束后关闭(重要!避免内存泄漏)
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});

// 离屏缓冲优化复杂动画
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null); // 关闭硬件加速,兼容某些Canvas操作

4. 过渡动画(Transition)

复制代码
// 1. Activity转场动画
ActivityCompat.startActivity(this, intent, 
    ActivityOptionsCompat.makeSceneTransitionAnimation(
        this, sharedView, "transition_name"
    ).toBundle()
);

// 2. Fragment过渡
fragment.setSharedElementEnterTransition(new ChangeBounds());
fragment.setEnterTransition(new Fade());

// 3. 布局变化过渡
TransitionManager.beginDelayedTransition(rootView);
view.setVisibility(View.GONE); // 自动执行消失动画

5. 性能优化

java 复制代码
// 1. 避免在动画中触发Layout
// 使用translationX/Y代替layout(left,top,right,bottom)

// 2. 使用ViewPropertyAnimator简化代码
view.animate()
    .translationX(100)
    .alpha(0.5f)
    .setDuration(300)
    .withEndAction(() -> { /* 结束回调 */ });

// 3. 批量更新属性
view.animate().translationX(100).translationY(100); // 一次动画
相关推荐
SimonKing1 小时前
OpenCode AI辅助编程,不一样的编程思路,不写一行代码
java·后端·程序员
FastBean1 小时前
Jackson View Extension Spring Boot Starter
java·后端
Kapaseker2 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴2 小时前
Android17 为什么重写 MessageQueue
android
Seven972 小时前
剑指offer-79、最⻓不含重复字符的⼦字符串
java
皮皮林55112 小时前
Java性能调优黑科技!1行代码实现毫秒级耗时追踪,效率飙升300%!
java
冰_河12 小时前
QPS从300到3100:我靠一行代码让接口性能暴涨10倍,系统性能原地起飞!!
java·后端·性能优化
桦说编程15 小时前
从 ForkJoinPool 的 Compensate 看并发框架的线程补偿思想
java·后端·源码阅读
躺平大鹅16 小时前
Java面向对象入门(类与对象,新手秒懂)
java