Window系列 (四) — Touch 事件是如何传递到 Activity ?

一、概述

熟悉事件分发的人都知道,事件是从 Activity.dispatchTouchEvent() 开始向下传递的,那么 Activity 中的 Touch 事件又是从哪里接受到的呢?在这篇文章中,我们来简单分析一下 Activity 接收 Touch 事件的流程。


版本: Android SDK 29

关联文章:
1.《Window系列 (一) — WindowManager 详解》


二、流程分析

对于一块陌生的代码,去猜测它的调用流程是困难的,这里有个小技巧,可以利用 Thread.dumpStack() 来查看当前线程的方法调用栈 (即 MainActivity.dispatchTouchEvent() 方法的调用栈)。

代码如下所示:

public class MainActivity extends AppCompatActivity {
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    	// 使用 Thread.dumpStack() 来查看 MainActivity.dispatchTouchEvent() 方法的调用栈。
        Thread.dumpStack();
        return super.dispatchTouchEvent(ev);
    }
}

Logcat 导出的堆栈如下:

W/System.err: java.lang.Throwable: stack dump
W/System.err:     at java.lang.Thread.dumpStack(Thread.java:490)
// 到这里,事件已经分发到了 Activity.dispatchTouchEvent() 方法。
W/System.err:     at com.elson.example.MainActivity.dispatchTouchEvent(MainActivity.java:55)
W/System.err:     at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
W/System.err:     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2483)
W/System.err:     at android.view.View.dispatchPointerEvent(View.java:8820)
W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4750)
W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4608)
W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4098)
W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4151)
W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4117)
W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4254)
W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4125)
W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4311)
W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4098)
W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4151)
W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4117)
W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4125)
W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4098)
W/System.err:     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6559)
W/System.err:     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6533)
W/System.err:     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6486)
W/System.err:     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6739)
// 可以看出,这里是事件接收的开始。
W/System.err:     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185) 
W/System.err:     at android.os.MessageQueue.nativePollOnce(Native Method)
W/System.err:     at android.os.MessageQueue.next(MessageQueue.java:148)
W/System.err:     at android.os.Looper.loop(Looper.java:151)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5775)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:975)
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:770)

通过以上的堆栈信息,我们可以看出 Touch 事件从 远程服务端的接收 到 传递给 Activity 的过程大致如下:

InputEventReceiver.dispatchInputEvent()
–>ViewRootImpl.WindowInputEventReceiver.onInputEvent()
–>ViewRootImpl.deliverInputEvent()
–>ViewRootImpl.InputStage.deliver()
这里经过一些了 InputStage 子类的传递处理(责任链模式)
–>ViewRootImpl.ViewPostImeInputStage.processPointerEvent()
–>View.dispatchPointerEvent()
–>PhoneWindow$DecorView.dispatchTouchEvent()
–>WindowCallbackWrapper.dispatchTouchEvent()
–>Activity.dispatchTouchEvent()


三、源码分析

下面从三个方面来分析:

  1. 应用进程如何接收远程服务回传的 Touch 事件?
  2. Touch 事件如何传递到 Activity 中?
  3. InputStage 的链式调用

1. 应用进程如何接收远程服务回传的 Touch 事件?

ViewRootImpl.setView() 中,完成了事件分发的监听操作。

ViewRootImpl

// ViewRootImpl.class
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                // 1.创建一个事件输入通道(InputChannel)。
                mInputChannel = new InputChannel();
            }
            // ...省略代码...
            
            if (mInputChannel != null) {
                // ...省略代码...
                // 2.WindowInputEventReceiver 用于接收远程服务发送的事件,此处与mInputChannel进行关联。
                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
            }
			// ...省略代码...
			
            // 3.这段代码在分析 “事件如何传递到Activity”时有用。
            CharSequence counterSuffix = attrs.getTitle();
            mSyntheticInputStage = new SyntheticInputStage();
            InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
            InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                    "aq:native-post-ime:" + counterSuffix);
            InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
            InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                    "aq:ime:" + counterSuffix);
            InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
            InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                    "aq:native-pre-ime:" + counterSuffix);

            mFirstInputStage = nativePreImeStage;
            mFirstPostImeInputStage = earlyPostImeStage;
        }
    }
}

这个方法主要有三步:

  1. 步骤1:创建一个事件传递的通道 (InputChannel)。
  2. 步骤2:创建一个接收远程服务发送事件的类 (WindowInputEventReceiver)。
  3. 步骤3:创建一系列的 InputStage,并进行串联。

下面我们来看一下 WindowInputEventReceiver 里面是如何操作的。

WindowInputEventReceiver (ViewRootImpl 内部类)

// ViewRootImpl.WindowInputEventReceiver.class
final class WindowInputEventReceiver extends InputEventReceiver {
    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
        super(inputChannel, looper);
    }
    
    @Override
    public void onInputEvent(InputEvent event) {
        // 这里会接收到远程服务回传的 Touch 事件。
    }
    // ...省略代码...
}

我们看到 InputChannel 被传递到了 WindowInputEventReceiver 的父类中,所以我们接着往下看。

InputEventReceiver

// InputEventReceiver.class
public abstract class InputEventReceiver {
	public InputEventReceiver(InputChannel inputChannel, Looper looper) {
		// ...省略代码...
        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue();
        // 将 InputChannel 传递到 Native 层。
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);

        mCloseGuard.open("dispose");
    }
    
    // 这个方法是被 Native 层调用的,我们可以猜测远程服务要分发 Touch 事件时,会触发这个方法来把事件回传到应用进程。
    // Called from native code.
    @SuppressWarnings("unused")
    @UnsupportedAppUsage
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        // 这个方法在子类 WindowInputEventReceiver 中被重写。
        onInputEvent(event);
    }
}

小结:

  1. 接收事件类的注册是在添加Window的过程中(即:ViewRootImpl.setView())。
  2. 接收远程服务分发的Touch事件的类为 WindowInputEventReceiver

2. Touch 事件如何传递到 Activity 中?

上面我们知道,应用进程中接收 Touch 事件最早是在 WindowInputEventReceiver.onInputEvent() 方法中,下面我们就来分析一下从接收到 Touch 事件到分发到 Activity.dispatchTouchEvent() 的流程。

时序图:
在这里插入图片描述

源码流程分析:

WindowInputEventReceiver (ViewRootImpl 内部类)

// ViewRootImpl.WindowInputEventReceiver.class
final class WindowInputEventReceiver extends InputEventReceiver {
    // ...省略代码...
    
    @Override
    public void onInputEvent(InputEvent event) {
        // ...省略代码...
        // 将事件加入队列
        enqueueInputEvent(event, this, 0, true);
    }
}

ViewRootImpl

// ViewRootImpl.class
void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    // 1. 将参数保包装成 QueuedInputEvent 事件类。
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
    // ...省略加入队列的代码...

    if (processImmediately) {
    	// 2.处理事件类型。
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}

void doProcessInputEvents() {
    // Deliver all pending input events in the queue.
    // 从队列中获取事件进行处理
    while (mPendingInputEventHead != null) {
        QueuedInputEvent q = mPendingInputEventHead;
        // ...省略代码...
        mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
		// 分发 Touch 事件
        deliverInputEvent(q);
    }
    
	// ...省略代码...
}

private void deliverInputEvent(QueuedInputEvent q) {
    // ...省略代码...

    InputStage stage;
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;
    } else {
    	// 在 ViewRootImpl.setView() 中,我们知道:
    	// mFirstInputStage指向NativePreImeInputStage类;
    	// mFirstPostImeInputStage 指向 EarlyPostImeInputStage类。
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
    }
    // ...省略代码...
    if (stage != null) {
        handleWindowFocusChanged();
        // 这里获取到 InputStage 子类进行链式调用来分发事件(每个子类有不同的作用)
        // 根据上面的调用流程,我们知道最终会调用到 ViewPostImeInputStage.Process() 方法。
        stage.deliver(q);
    } else {
        finishInputEvent(q);
    }
}

小结:

  1. 根据 Locat 中显示的调用流程,我们知道 stage.deliver(q) 调用后,最终会调用到 ViewPostImeInputStage.processPointerEvent() 方法。

ViewPostImeInputStage

// ViewPostImeInputStage.class 
/**
 * Delivers post-ime input events to the view hierarchy.
 */
final class ViewPostImeInputStage extends InputStage {

	private int processPointerEvent(QueuedInputEvent q) {
	    final MotionEvent event = (MotionEvent)q.mEvent;
	    // ...省略代码...
	    
	    // mView 是 DecorView,最终调用了 DecorView.dispatchPointerEvent()方法。
	    boolean handled = mView.dispatchPointerEvent(event);
	    
	    // ...省略代码...
	    return handled ? FINISH_HANDLED : FORWARD;
	}
}

View

// View.class
public final boolean dispatchPointerEvent(MotionEvent event) {
    if (event.isTouchEvent()) {
    	// 执行 Touch 事件
        return dispatchTouchEvent(event);
    } else {
        return dispatchGenericMotionEvent(event);
    }
}

DecorView

// DecorView.class
public boolean dispatchTouchEvent(MotionEvent ev) {
	// cb 指向 Activity,在Activity中实现了Window.Callback接口。
	// 具体给 Window 设置 Callback 的地方在 Activity.attach() 方法中,有一行 PhoneWindow.setCallback(this) 代码。
    final Window.Callback cb = mWindow.getCallback();
    // 所以这里最终触发了 Activity.dispatchTouchEvent() 方法。
    return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
            ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}

到这里,从 WindowInputEventReceiver 接收到 Touch 事件到分发到 Activity 的流程就分析完了。


3. InputStage 的链式调用流程

从上面你的调用流程我们可以看到,这中间涉及到 InputStage 及其子类的一系列链式调用,下面我们来分析一下这个链式调用的流程。

InputStage 是在ViewRootImpl.setView() 方法中创建的。

ViewRootImpl

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
			// ...省略代码...
			
            // 这里使用了责任链,将 InputStage 进行串联。
            CharSequence counterSuffix = attrs.getTitle();
            mSyntheticInputStage = new SyntheticInputStage();
            // Delivers post-ime input events to the view hierarchy.
            InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
            // Delivers post-ime input events to a native activity.
            InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                    "aq:native-post-ime:" + counterSuffix);
            // Performs early processing of post-ime input events.
            InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
            // Delivers input events to the ime. Does not support pointer events.
            InputStage imeStage = new ImeInputStage(earlyPostImeStage, "aq:ime:" + counterSuffix);
            // Delivers pre-ime input events to the view hierarchy. Does not support pointer events.
            InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
            // Delivers pre-ime input events to a native activity. Does not support pointer events.
            InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                    "aq:native-pre-ime:" + counterSuffix);
            mFirstInputStage = nativePreImeStage;
            mFirstPostImeInputStage = earlyPostImeStage;
        }
    }
}

小结:

  1. 从 InputStage 子类的注释,我们知道 NativePreImeInputStage、ViewPreImeInputStage、ImeInputStage 都不处理 Pointer 事件(手势事件,Touch事件属于手势事件)。
  2. NativePreImeInputStage、ViewPreImeInputStageNativePostImeInputStage、ViewPostImeInputStage 类名可以看出,他们的两两配对的。

EarlyPostImeInputStage

// EarlyPostImeInputStage.class
final class EarlyPostImeInputStage extends InputStage {
    public EarlyPostImeInputStage(InputStage next) {
        super(next);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (q.mEvent instanceof KeyEvent) {
            return processKeyEvent(q);
        } else if (q.mEvent instanceof MotionEvent) {
        	// 处理 MotionEvent 事件的。
            return processMotionEvent(q);
        }
        return FORWARD;
    }
}

ViewPostImeInputStage

// ViewPostImeInputStage.class
final class ViewPostImeInputStage extends InputStage {

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (q.mEvent instanceof KeyEvent) {
            return processKeyEvent(q);
        } else {
            final int source = q.mEvent.getSource();
            if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                return processPointerEvent(q); // 核心代码
            }
            // ...省略代码...
        }
    }
    
	private int processPointerEvent(QueuedInputEvent q) {
		// ...省略代码...
		// 核心代码
        boolean handled = mView.dispatchPointerEvent(event);
       	// ...省略代码...
        return handled ? FINISH_HANDLED : FORWARD;
    }
}

InputStage 链式调用的流程:
InputStage

// InputStage.class
abstract class InputStage {
    private final InputStage mNext;

	// 将下个InputStage 与当前InputStage 作关联。
    public InputStage(InputStage next) {
        mNext = next;
    }

    // 开始调用 InputStage.deliver 进行事件分发。
    public final void deliver(QueuedInputEvent q) {
        if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
            forward(q);
        } else if (shouldDropInputEvent(q)) {
            finish(q, false);
        } else {
        	// 执行分发逻辑
            apply(q, onProcess(q)); //onProcess()默认返回FORWARD
        }
    }
    
    protected int onProcess(QueuedInputEvent q) {
        return FORWARD;
    }
    
    // 根据子类重写 onProcess 方法返回不同的 result 来执行不同的逻辑。
    protected void apply(QueuedInputEvent q, int result) {
        if (result == FORWARD) {
        	// 继续执行下一个
            forward(q);
        } else if (result == FINISH_HANDLED) {
            finish(q, true);
        } else if (result == FINISH_NOT_HANDLED) {
            finish(q, false);
        } else {
            throw new IllegalArgumentException("Invalid result: " + result);
        }
    }
    
    /**
     * Forwards the event to the next stage.
     */
    protected void forward(QueuedInputEvent q) {
        onDeliverToNext(q);
    }

    /**
     * Called when an event is being delivered to the next stage.
     */
    protected void onDeliverToNext(QueuedInputEvent q) {
        if (DEBUG_INPUT_STAGES) {
            Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
        }
        if (mNext != null) {
        	// 然后执行下一个 InputStage 的分发流程。
            mNext.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }
}

五、参考文章

  1. 《Activity是如何接收到touch事件的(窗口与用户输入系统)》
  2. 《ViewRootImpl源码分析事件分发》
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本书是一本Android进阶类书籍,采用理论、源码和实践相结合的方式来阐述高水准的Android应用开发要点。本书从三个方面来组织内容。第一,介绍Android开发者不容易掌握的一些知识点;第二,结合Android源代码和应用层开发过程,融会贯通,介绍一些比较深入的知识点;第三,介绍一些核心技术和Android的性能优化思想。 第1章 Activity的生命周期和启动模式 1 1.1 Activity的生命周期全面分析 1 1.1.1 典型情况下的生命周期分析 2 1.1.2 异常情况下的生命周期分析 8 1.2 Activity的启动模式 16 1.2.1 Activity的LaunchMode 16 1.2.2 Activity的Flags 27 1.3 IntentFilter的匹配规则 28 第2章 IPC机制 35 2.1 Android IPC简介 35 2.2 Android中的多进程模式 36 2.2.1 开启多进程模式 36 2.2.2 多进程模式的运行机制 39 2.3 IPC基础概念介绍 42 2.3.1 Serializable接口 42 2.3.2 Parcelable接口 45 2.3.3 Binder 47 2.4 Android中的IPC方式 61 2.4.1 使用Bundle 61 2.4.2 使用文件共享 62 2.4.3 使用Messenger 65 2.4.4 使用AIDL 71 2.4.5 使用ContentProvider 91 2.4.6 使用Socket 103 2.5 Binder连接池 112 2.6 选用合适的IPC方式 121 第3章 View的事件体系 122 3.1 View基础知识 122 3.1.1 什么是View 123 3.1.2 View的位置参数 123 3.1.3 MotionEventTouchSlop 125 3.1.4 VelocityTracker、GestureDetector和Scroller 126 3.2 View的滑动 129 3.2.1 使用scrollTo/scrollBy 129 3.2.2 使用动画 131 3.2.3 改变布局参数 133 3.2.4 各种滑动方式的对比 133 3.3 弹性滑动 135 3.3.1 使用Scroller 136 3.3.2 通过动画 138 3.3.3 使用延时策略 139 3.4 View的事件分发机制 140 3.4.1 点击事件传递规则 140 3.4.2 事件分发的源码解析 144 3.5 View的滑动冲突 154 3.5.1 常见的滑动冲突场景 155 3.5.2 滑动冲突的处理规则 156 3.5.3 滑动冲突的解决方式 157 第4章 View的工作原理 174 4.1 初识ViewRoot和DecorView 174 4.2 理解MeasureSpec 177 4.2.1 MeasureSpec 177 4.2.2 MeasureSpec和LayoutParams的对应关系 178 4.3 View的工作流程 183 4.3.1 measure过程 183 4.3.2 layout过程 193 4.3.3 draw过程 197 4.4 自定义View 199 4.4.1 自定义View的分类 200 4.4.2 自定义View须知 201 4.4.3 自定义View示例 202 4.4.4 自定义View的思想 217 第5章 理解RemoteViews 218 5.1 RemoteViews的应用 218 5.1.1 RemoteViews在通知栏上的应用 219 5.1.2 RemoteViews在桌面小部件上的应用 221 5.1.3 PendingIntent概述 228 5.2 RemoteViews的内部机制 230 5.3 RemoteViews的意义 239 第6章 Android的Drawable 243 6.1 Drawable简介 243 6.2 Drawable的分类 244 6.2.1 BitmapDrawable 244 6.2.2 ShapeDrawable 247 6.2.3 LayerDrawable 251 6.2.4 StateListDrawable 253 6.2.5 LevelListDrawable 255 6.2.6 TransitionDrawable 256 6.2.7 Ins
第1章 Activity的生命周期和启动模式 / 1   1.1 Activity的生命周期全面分析 / 1   1.1.1 典型情况下的生命周期分析 / 2   1.1.2 异常情况下的生命周期分析 / 8   1.2 Activity的启动模式 / 16   1.2.1 Activity的LaunchMode / 16   1.2.2 Activity的Flags / 27   1.3 IntentFilter的匹配规则 / 28   第2章 IPC机制 / 35   2.1 Android IPC简介 / 35   2.2 Android中的多进程模式 / 36   2.2.1 开启多进程模式 / 36   2.2.2 多进程模式的运行机制 / 39   2.3 IPC基础概念介绍 / 42   2.3.1 Serializable接口 / 42   2.3.2 Parcelable接口 / 45   2.3.3 Binder / 47   2.4 Android中的IPC方式 / 61   2.4.1 使用Bundle / 61   2.4.2 使用文件共享 / 62   2.4.3 使用Messenger / 65   2.4.4 使用AIDL / 71   2.4.5 使用ContentProvider / 91   2.4.6 使用Socket / 103   2.5 Binder连接池 / 112   2.6 选用合适的IPC方式 / 121   第3章 View的事件体系 / 122   3.1 View基础知识 / 122   3.1.1 什么是View / 123   3.1.2 View的位置参数 / 123   3.1.3 MotionEventTouchSlop / 125   3.1.4 VelocityTracker、GestureDetector和Scroller / 126   3.2 View的滑动 / 129   3.2.1 使用scrollTo/scrollBy / 129   3.2.2 使用动画 / 131   3.2.3 改变布局参数 / 133   3.2.4 各种滑动方式的对比 / 133   3.3 弹性滑动 / 135   3.3.1 使用Scroller / 136   3.3.2 通过动画 / 138   3.3.3 使用延时策略 / 139   3.4 View的事件分发机制 / 140   3.4.1 点击事件传递规则 / 140   3.4.2 事件分发的源码解析 / 144   3.5 View的滑动冲突 / 154   3.5.1 常见的滑动冲突场景 / 155   3.5.2 滑动冲突的处理规则 / 156   3.5.3 滑动冲突的解决方式 / 157   第4章 View的工作原理 / 174   4.1 初识ViewRoot和DecorView / 174   4.2 理解MeasureSpec / 177   4.2.1 MeasureSpec / 177   4.2.2 MeasureSpec和LayoutParams的对应关系 / 178   4.3 View的工作流程 / 183   4.3.1 measure过程 / 183   4.3.2 layout过程 / 193   4.3.3 draw过程 / 197   4.4 自定义View / 199   4.4.1 自定义View的分类 / 200   4.4.2 自定义View须知 / 201   4.4.3 自定义View示例 / 202   4.4.4 自定义View的思想 / 217   第5章 理解RemoteViews / 218   5.1 RemoteViews的应用 / 218   5.1.1 RemoteViews在通知栏上的应用 / 219   5.1.2 RemoteViews在桌面小部件上的应用 / 221   5.1.3 PendingIntent概述 / 228   5.2 RemoteViews的内部机制 / 230   5.3 RemoteViews的意义 / 239   第6章 Android的Drawable / 243   6.1 Drawable简介 / 243   6.2 Drawable的分类 / 244   6.2.1 BitmapDrawable / 244   6.2.2 ShapeDrawable / 247   6.2.3 LayerDrawable / 251   6.2.4 StateListDrawable / 253   6.2.5 LevelListDrawable / 255   6.2.6 TransitionDrawable / 256   6.2.7 InsetDrawable / 257   6.2.8 ScaleDrawable / 258   6.2.9 ClipDrawable / 260   6.3 自定义Drawable / 262   第7章 Android动画深入分析 / 265   7.1 View动画 / 265   7.1.1 View动画的种类 / 265   7.1.2 自定义View动画 / 270   7.1.3 帧动画 / 272   7.2 View动画的特殊使用场景 / 273   7.2.1 LayoutAnimation / 273   7.2.2 Activity的切换效果 / 275   7.3 属性动画 / 276   7.3.1 使用属性动画 / 276   7.3.2 理解插值器和估值器 / 280   7.3.3 属性动画的监听器 / 282   7.3.4 对任意属性做动画 / 282   7.3.5 属性动画的工作原理 / 288   7.4 使用动画的注意事项 / 292   第8章 理解WindowWindowManager / 294   8.1 WindowWindowManager / 294   8.2 Window的内部机制 / 297   8.2.1 Window的添加过程 / 298   8.2.2 Window的删除过程 / 301   8.2.3 Window的更新过程 / 303   8.3 Window的创建过程 / 304   8.3.1 ActivityWindow创建过程 / 304   8.3.2 Dialog的Window创建过程 / 308   8.3.3 Toast的Window创建过程 / 311   第9章 大组件的工作过程 / 316   9.1 大组件的运行状态 / 316   9.2 Activity的工作过程 / 318   9.3 Service的工作过程 / 336   9.3.1 Service的启动过程 / 336   9.3.2 Service的绑定过程 / 344   9.4 BroadcastReceiver的工作过程 / 352   9.4.1 广播的注册过程 / 353   9.4.2 广播的发送和接收过程 / 356   9.5 ContentProvider的工作过程 / 362   第10章 Android的消息机制 / 372   10.1 Android的消息机制概述 / 373   10.2 Android的消息机制分析 / 375   10.2.1 ThreadLocal的工作原理 / 375   10.2.2 消息队列的工作原理 / 380   10.2.3 Looper的工作原理 / 383   10.2.4 Handler的工作原理 / 385   10.3 主线程的消息循环 / 389   第11章 Android的线程和线程池 / 391   11.1 主线程和子线程 / 392   11.2 Android中的线程形态 / 392   11.2.1 AsyncTask / 392   11.2.2 AsyncTask的工作原理 / 395   11.2.3 HandlerThread / 402   11.2.4 IntentService / 403   11.3 Android中的线程池 / 406   11.3.1 ThreadPoolExecutor / 407   11.3.2 线程池的分类 / 410   第12章 Bitmap的加载和Cache / 413   12.1 Bitmap的高效加载 / 414   12.2 Android中的缓存策略 / 417   12.2.1 LruCache / 418   12.2.2 DiskLruCache / 419   12.2.3 ImageLoader的实现 / 424   12.3 ImageLoader的使用 / 441   12.3.1 照片墙效果 / 441   12.3.2 优化列表的卡顿现象 / 446   第13章 综合技术 / 448   13.1 使用CrashHandler来获取应用的crash信息 / 449   13.2 使用multidex来解决方法数越界 / 455   13.3 Android的动态加载技术 / 463   13.4 反编译初步 / 469   13.4.1 使用dex2jar和jd-gui反编译apk / 470   13.4.2 使用apktool对apk进行二次打包 / 470   第14章 JNI和NDK编程 / 473   14.1 JNI的开发流程 / 474   14.2 NDK的开发流程 / 478   14.3 JNI的数据类型和类型签名 / 484   14.4 JNI调用Java方法的流程 / 486   第15章 Android性能优化 / 489   15.1 Android的性能优化方法 / 490   15.1.1 布局优化 / 490   15.1.2 绘制优化 / 493   15.1.3 内存泄露优化 / 493   15.1.4 响应速度优化和ANR日志分析 / 496   15.1.5 ListView和Bitmap优化 / 501   15.1.6 线程优化 / 501   15.1.7 一些性能优化建议 / 501   15.2 内存泄露分析之MAT工具 / 502   15.3 提高程序的可维护性 / 506
要在 Android 中的多个 Activity 中弹出对话框并将其置于顶部,可以使用以下步骤: 1. 创建一个单独的类来管理对话框的显示和隐藏。 2. 在该类中,创建一个无界面的 Activity 作为对话框容器,并将其属性设置为对话框样式。 3. 在 Activity 中,使用 WindowManager 将该对话框容器添加到 WindowManager 中。 4. 在需要显示对话框的 Activity 中,调用对话框管理器类的方法来显示对话框。 以下是一个示例代码: 1. 创建一个名为 DialogManager 的单例类: ``` public class DialogManager { private static DialogManager instance; private Dialog dialog; private WindowManager windowManager; private WindowManager.LayoutParams layoutParams; private DialogManager() { // 初始化对话框容器 Context context = MyApplication.getInstance(); View dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_layout, null); dialog = new Dialog(context, R.style.DialogStyle); dialog.setContentView(dialogView); // 初始化 WindowManager 和 LayoutParams windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); layoutParams = new WindowManager.LayoutParams(); layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; layoutParams.format = PixelFormat.TRANSPARENT; layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER; layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; } public static DialogManager getInstance() { if (instance == null) { instance = new DialogManager(); } return instance; } public void showDialog() { if (!dialog.isShowing()) { windowManager.addView(dialog.getWindow().getDecorView(), layoutParams); } } public void hideDialog() { if (dialog.isShowing()) { windowManager.removeView(dialog.getWindow().getDecorView()); } } } ``` 2. 创建一个名为 DialogStyle 的样式: ``` <style name="DialogStyle" parent="Theme.AppCompat.Light.Dialog"> <item name="android:windowIsFloating">false</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowAnimationStyle">@style/DialogAnimation</item> </style> <style name="DialogAnimation"> <item name="android:windowEnterAnimation">@android:anim/fade_in</item> <item name="android:windowExitAnimation">@android:anim/fade_out</item> </style> ``` 3. 在需要显示对话框的 Activity 中,调用对话框管理器类的 showDialog() 方法来显示对话框: ``` DialogManager.getInstance().showDialog(); ``` 4. 在 Activity 销毁时,调用对话框管理器类的 hideDialog() 方法来隐藏对话框: ``` @Override protected void onDestroy() { super.onDestroy(); DialogManager.getInstance().hideDialog(); } ``` 请注意,此方法将对话框添加到 WindowManager 中,因此必须在 Activity 销毁时将其从 WindowManager 中删除,以避免内存泄漏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值