安卓进阶——原理机制

一、事件传递机制

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); // 一次动画
相关推荐
叶羽西3 分钟前
如何对自己开发的系统级APK进行签名
android
Evand J5 分钟前
【MATLAB例程】三维环境下,EKF融合INS与DVL的核心程序,用于惯导和速度传感器的数据融合滤波。附下载链接
开发语言·matlab
枫叶丹46 分钟前
【Qt开发】Qt系统(二)-> 事件分发器
c语言·开发语言·数据库·c++·qt·系统架构
橘色的狸花猫9 分钟前
简历与岗位要求相似度分析系统
java·nlp
独自破碎E13 分钟前
Leetcode1438绝对值不超过限制的最长连续子数组
java·开发语言·算法
用户917439653918 分钟前
Elasticsearch Percolate Query使用优化案例-从2000到500ms
java·大数据·elasticsearch
澜莲花26 分钟前
python图色之opencv基础---验证码实战
开发语言·python·opencv
沐知全栈开发28 分钟前
Numpy 数组操作
开发语言
yaoxin5211231 小时前
279. Java Stream API - Stream 拼接的两种方式:concat() vs flatMap()
java·开发语言
@小码农1 小时前
202512 电子学会 Scratch图形化编程等级考试三级真题(附答案)
服务器·开发语言·数据结构·数据库·算法