源码视角下深入剖析Android应用启动与事件处理机制

概述

在Android开发领域,深入理解应用启动与事件处理机制是进阶的关键。接下来,我们将结合相关流程图,从源码层面详细解读这一复杂过程。

一、AMS驱动应用启动的源码剖析

当点击应用图标启动应用时,AMS的attachApp方法被调用,这一过程在Binder线程中执行。在ActivityManagerService.java源码中,attachApp会与ActivityThread进行交互。例如,ActivityThread中的attach方法会被远程调用,它会将应用进程与AMS进行关联,完成进程的初始化工作。

// ActivityManagerService部分代码示例
public final void attachApp(int pid) {
    // 查找对应的应用进程记录
    ProcessRecord app = getProcessRecordLocked(pid, null, false);
    if (app != null) {
        try {
            // 通过Binder调用ActivityThread的attach方法
            app.thread.attach(false, mCoreSettingsObserver.getCoreSettingsLocked());
        } catch (RemoteException ex) {
            // 处理异常情况
        }
    }
}

AMS在启动Launcher应用进程时,会检查Activity栈中已有的Activity。这涉及到ActivityStackSupervisor类,它负责管理Activity栈。startSpecificActivityLocked方法会判断栈中是否有可复用的Activity,若有则直接启动,避免重复创建,提高启动效率。

// ActivityStackSupervisor部分代码示例
private void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
    ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true);
    if (app != null && app.thread != null) {
        try {
            realStartActivityLocked(r, app, andResume, checkConfig);
        } catch (RemoteException e) {
            // 处理异常
        }
    } else {
        // 启动新的应用进程
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }
}

二、Activity创建与初始化的源码流程

Activity创建时,首先创建PhoneWindow。在Activity.javaattach方法中,会实例化PhoneWindow,并建立Activity与PhoneWindow的关联。

// Activity.java部分代码示例
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback) {
    attachBaseContext(context);
    mFragments.attachHost(null /*parent*/);
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    // 其他初始化操作
}

接着,Activity.onCreate方法被调用,开发者在该方法中进行初始化操作。ActivityThread中的handleLaunchActivity方法会触发ActivityonCreate

// ActivityThread部分代码示例
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // 创建Activity实例
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        // 调用Activity的onCreate方法
        r.createdConfig = new Configuration(mConfiguration);
        Bundle oldState = r.state;
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished &&!r.startsNotResumed, r.lastProcessedSeq, false);
    }
}

Activity.setContentView会调用PhoneWindow.setContentView。在PhoneWindow中,setContentView会将布局资源解析为视图层级结构。

// PhoneWindow.java部分代码示例
@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    // 将布局资源填充到mContentParent中
    LayoutInflater.from(mContext).inflate(layoutResID, mContentParent);
    // 其他操作
}

三、视图与窗口交互及布局请求的源码分析

ViewRootImpl.setView将视图树与窗口关联。在ViewRootImpl的构造函数中,会初始化一些关键变量,setView方法会将视图添加到窗口中,并开始准备布局。

// ViewRootImpl.java部分代码示例
public ViewRootImpl(Context context, Display display) {
    mContext = context;
    mWindowSession = WindowManagerGlobal.getWindowSession();
    // 其他初始化操作
}

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            // 向窗口会话添加窗口
            try {
                mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
            } catch (RemoteException e) {
                // 处理异常
            }
            // 启动布局流程
            requestLayout();
        }
    }
}

ViewRootImpl.requestLayout发起布局请求,它会检查是否处于布局中,若未处于布局中,则会通过scheduleTraversals方法安排一次遍历。

// ViewRootImpl.java部分代码示例
@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

系统发出VSync信号后,FrameDisplayEventReceiver.onVsync被调用,它会触发Choreographer.doFrameChoreographer类负责协调动画、输入和绘制等操作。

// Choreographer.java部分代码示例
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
        implements Runnable {
    @Override
    public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
        // 将时间戳转换为合适的格式
        long now = System.nanoTime();
        if (timestampNanos > now) {
            timestampNanos = now;
        }
        if (mHavePendingVsync) {
            Log.w(TAG, "Already have a pending vsync event.  There should only be "
                    + "one at a time.");
        }
        mHavePendingVsync = true;
        mTimestampNanos = timestampNanos;
        mFrame = frame;
        // 将自身添加到主线程消息队列中
        Message msg = Message.obtain(mHandler, this);
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }
}

Choreographer.doFrame中,会调用ViewRootImpl.enqueueInputEvent将输入事件加入队列。

// Choreographer.java部分代码示例
void doFrame(long frameTimeNanos, int frame) {
    // 计算时间差
    final long startNanos;
    if (DEBUG_FRAMES) {
        startNanos = System.nanoTime();
    }
    try {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
        // 处理输入事件
        if (mInputEventReceiver != null) {
            mInputEventReceiver.consumeEvents();
        }
        // 调用ViewRootImpl的相关方法处理事件
        for (int i = 0; i < mCallbackQueues.length; i++) {
            final CallbackRecord callbacks = mCallbackQueues[i].extractDueCallbacksLocked(frameTimeNanos);
            if (callbacks != null) {
                executeCallbacks(callbacks);
            }
        }
        // 处理动画和绘制
        doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
        doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

四、事件处理责任链模式的源码实现

ViewRootImpl.doTraversal中,会按照责任链模式处理事件。InputStage是责任链中的节点,NativePreImeInputStageEarlyPostImeInputStageNativePostImeInputStage等都继承自InputStage

// ViewRootImpl.java部分代码示例
private void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mBatchingInput = false;
        // 处理输入事件
        if (mPendingInputEvent != null) {
            handleInputEvent(mPendingInputEvent);
            mPendingInputEvent = null;
        }
        // 开始遍历
        performTraversals();
    }
}

// InputStage.java部分代码示例
public abstract class InputStage {
    protected final InputStage mNext;
    public InputStage(InputStage next) {
        mNext = next;
    }
    public void onProcess(QueuedInputEvent q) {
        if (mNext != null) {
            mNext.onProcess(q);
        }
    }
}

当事件到达NativePreImeInputStage时,它会处理与输入法相关的预处理工作。若该阶段未处理完事件,则会传递给下一个阶段EarlyPostImeInputStage,依此类推,直到事件被处理。

// NativePreImeInputStage.java部分代码示例
public class NativePreImeInputStage extends InputStage {
    public NativePreImeInputStage(InputStage next) {
        super(next);
    }
    @Override
    public void onProcess(QueuedInputEvent q) {
        // 处理输入法相关的预处理
        if (!q.mEvent.isConsumed()) {
            forward(q);
        } else {
            finish(q);
        }
    }
}

五、总结与实践意义

通过对源码的深入解读,我们清晰地了解了Android应用启动与事件处理的全过程。从AMS的调度,到Activity的创建与初始化,再到视图与窗口的交互以及事件处理的责任链模式,每一个环节都紧密协作。
对于开发者来说,掌握这些原理有助于在开发过程中优化应用性能,例如在Activity启动时合理复用资源,避免不必要的创建操作;在事件处理时,能够更精准地定位问题,提高应用的稳定性和响应速度。同时,这也为我们深入研究Android系统源码、定制化开发以及解决复杂的兼容性问题提供了坚实的理论基础。希望本文能帮助大家在Android开发道路上更上一层楼,不断探索和创新。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,815评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,251评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,999评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,996评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,993评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,477评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,848评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,823评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,361评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,401评论 3 346
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,518评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,119评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,850评论 3 338
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,292评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,424评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,072评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,588评论 2 365

推荐阅读更多精彩内容