安卓进阶——原理机制

一、事件传递机制

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); // 一次动画
相关推荐
是阿漂啊2 小时前
vscode运行springboot项目
java·spring boot·后端
ghfdgbg2 小时前
13. 配置优先级 + Bean的管理 + SpringBoot核心原理
java·spring boot·后端
Moe4882 小时前
Elasticsearch 8.1 Java API Client 客户端使用指南(索引、文档操作篇)
java·后端·面试
洋亦2 小时前
GoF23种设计模式 简介
java
Javatutouhouduan2 小时前
Java面试常问Redis核心知识点整理!
java·数据库·redis·java面试·后端开发·java架构师·java程序员
AAA简单玩转程序设计2 小时前
谁说Java枚举只是“常量装盒”?它藏着这些骚操作
java·前端
枯基Evan2 小时前
applicationTaskExecutor Spring 内置线程池失效
java·数据库·spring
优爱蛋白2 小时前
IL-21:后Th1/Th2时代的免疫新星
java·服务器·前端·人工智能·健康医疗
深圳佛手2 小时前
LangChain 提供的搜素工具SerpAPIWrapper介绍
开发语言·人工智能·python