Android之SystemUI加载流程和NavigationBar的分析

本文主要分析了Android 6.0中的SystemUI加载流程,重点探讨了SystemUIService的启动,SystemUIApplication的初始化,以及StatusBar模块中的NavigationBar。通过源码解析,详细阐述了NavigationBar的布局创建、加载以及布局参数的设定,揭示了隐藏和自定义导航栏的可能性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#Android之SystemUI加载流程和NavigationBar的分析

本篇只分析SystemUI的加载过程和SystemUI的其中的一个模块StatusBar的小模块NavigationBar,以Android6.0代码进行分析

##AndroidManifest.xml

 <application
    android:name=".SystemUIApplication"
    android:persistent="true"
    android:allowClearUserData="false"
    android:allowBackup="false"
    android:hardwareAccelerated="true"
    android:label="@string/app_label"
    android:icon="@drawable/icon"
    android:process="com.android.systemui"
    android:supportsRtl="true"
    android:theme="@style/systemui_theme">
    <!-- Keep theme in sync with SystemUIApplication.onCreate().
         Setting the theme on the application does not affect views inflated by services.
         The application theme is set again from onCreate to take effect for those views. -->

    <!-- Broadcast receiver that gets the broadcast at boot time and starts
         up everything else.
         TODO: Should have an android:permission attribute
         -->
    <service android:name="SystemUIService"
        android:exported="true"
        />

##SystemUIService
跟StatusBar相关的服务为SystemUIService,我们查看SystemUIService源码

public class SystemUIService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();
		//获取Application调用startServicesIfNeeded
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

	/*打印堆栈信息*/
    @Override
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        SystemUI[] services = ((SystemUIApplication) getApplication()).getServices();
        if (args == null || args.length == 0) {
            for (SystemUI ui: services) {
                pw.println("dumping service: " + ui.getClass().getName());
                ui.dump(fd, pw, args);
            }
        } else {
            String svc = args[0];
            for (SystemUI ui: services) {
                String name = ui.getClass().getName();
                if (name.endsWith(svc)) {
                    ui.dump(fd, pw, args);
                }
            }
        }
    }
}

分析SystemUIService代码,可以知道SystemUI主要做了两件事

  • 获取Application对象加载SystemUI相关的类,这个等下分析SystemUIApplication代码可以知道
  • dump打印SystenUISerice运行过程中相关的堆栈信息

那么SystemUIService又是哪里开始启动的呢?竟然SystemUIService是个服务,那么启动服务要么就是startService
要么就是bindService进行启动,其启动方式则需要通过Intent来传入类名或者包名,因此在源码中搜索SystemUIService可以对比发现,它在
frameworks\base\services\java\com\android\server\SystemServer.java中进行启动

static final void startSystemUi(Context context) {
    Intent intent = new Intent();
    intent.setComponent(new ComponentName("com.android.systemui",
                "com.android.systemui.SystemUIService"));
    //Slog.d(TAG, "Starting service: " + intent);
    context.startServiceAsUser(intent, UserHandle.OWNER);
}

在SystemServer的run方法中startOtherServices来启动SystemUIService服务,至于SystemServer则涉及到Android的启动流程,其大概流程为

int -> ServerManager -> Zygote -> SystemServer

SystemServer中会初始化一些Android的java层的服务,如ActivityManagerService、WindowManagerService等

这里SystemUI的加载过程就到此告一段落了,下面分析StatusBar的加载流程

上面讲到在SystemUIService的onCreate中获取SystemUIApplication对象来初始化SystemUI相关的类,这些类里面就包括了StatusBar相关的类,我们查看SystemUIApplication类

##SystemUIApplication
###onCreate

@Override
public void onCreate() {
    super.onCreate();
    // Set the application theme that is inherited by all services. Note that setting the
    // application theme in the manifest does only work for activities. Keep this in sync with
    // the theme set there.
    setTheme(R.style.systemui_theme);
	//注释广播
    IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
    filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
    registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
			//开启直接返回
            if (mBootCompleted) return;

            if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
            unregisterReceiver(this);
			//标记启动
            mBootCompleted = true;
			//服务是否启动
            if (mServicesStarted) {
                final int N = mServices.length;
                for (int i = 0; i < N; i++) {
					//回调各服务的onBootCompleted函数
                    mServices[i].onBootCompleted();
                }
            }
        }
    }, filter);
}

在SystemUIApplication的onCreate中主要做了

  • 设置主题(这个会影响其SystemUI的界面显示效果)
  • 注册开机广播,设置标志位

###startServicesIfNeeded

SystemUIService中的onCreate启动了这个方法,我们着重分析这个方法

public void startServicesIfNeeded() {
    if (mServicesStarted) {
        return;
    }

    if (!mBootCompleted) {
        // check to see if maybe it was already completed long before we began
        // see ActivityManagerService.finishBooting()
		//获取系统文件中的sys.boot_completed的值
        if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
            mBootCompleted = true;
            if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
        }
    }

    Log.v(TAG, "Starting SystemUI services.");
    final int N = SERVICES.length;
    for (int i=0; i<N; i++) {
        Class<?> cl = SERVICES[i];
        if (DEBUG) Log.d(TAG, "loading: " + cl);
		//实例化各个类实例,放入mServices数组中
        try {
            mServices[i] = (SystemUI)cl.newInstance();
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        } catch (InstantiationException ex) {
            throw new RuntimeException(ex);
        }
        mServices[i].mContext = this;
        mServices[i].mComponents = mComponents;
        if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
        mServices[i].start();

        if (mBootCompleted) {
            mServices[i].onBootCompleted();
        }
    }
	//服务启动标志
    mServicesStarted = true;
}

这个方法中,首先判断mServicesStarted标志为来判断SystemUI相关的服务是否启动,同时根据系统配置文件来检查ActivityManagerService是否finishBooting,然后通过类加载机制来初始化SERVICES数组里面相关的类加入mServices中,然后start

 /**
 * The classes of the stuff to start.
 */
private final Class<?>[] SERVICES = new Class[] {
        com.android.systemui.tuner.TunerService.class,//定制状态栏服务
        com.android.systemui.keyguard.KeyguardViewMediator.class,//锁屏相关
        com.android.systemui.recents.Recents.class,//近期任务
        com.android.systemui.volume.VolumeUI.class,//音量条
        com.android.systemui.statusbar.SystemBars.class,//状态栏
        com.android.systemui.usb.StorageNotification.class,//通知栏
        com.android.systemui.power.PowerUI.class,//电源相关
        com.android.systemui.media.RingtonePlayer.class,//铃声播放相关
};

/**
 * Hold a reference on the stuff we start.
 */
private final SystemUI[] mServices = new SystemUI[SERVICES.length];

从mServices和SERVICES的定义可以发现SERVICES是一组包含全路径的相关的类,这些类包含一些我们常见的TunerService(定制状态栏服务)、
KeyguardViewMediator(锁屏相关)、Recents(近期任务)、VolumeUI(音量条)、SystemBars(状态栏)、StorageNotification(通知栏)、PowerUI(电源相关)、RingtonePlayer(铃声播放相关)类,它们都是继承与SystemUI抽象类,现在只分析StatusBar相关的SystemBars类

##SystemBars

public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks {
    private static final String TAG = "SystemBars";
    private static final boolean DEBUG = false;
    private static final int WAIT_FOR_BARS_TO_DIE = 500;

    // manages the implementation coming from the remote process
    private ServiceMonitor mServiceMonitor;

    // in-process fallback implementation, per the product config
    private BaseStatusBar mStatusBar;

    @Override
    public void start() {
        if (DEBUG) Log.d(TAG, "start");
		//实例化ServiceMonitor
        mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
                mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
		//start
        mServiceMonitor.start();  // will call onNoService if no remote service is found
    }
	/*服务没启动时,ServiceMonitor会回调onNoService*/
    @Override
    public void onNoService() {
        if (DEBUG) Log.d(TAG, "onNoService");
        createStatusBarFromConfig();  // fallback to using an in-process implementation
    }
	/*服务已经启动的回调*/
    @Override
    public long onServiceStartAttempt() {
        if (DEBUG) Log.d(TAG, "onServiceStartAttempt mStatusBar="+mStatusBar);
        if (mStatusBar != null) {
            // tear down the in-process version, we'll recreate it again if needed
            mStatusBar.destroy();
            mStatusBar = null;
            return WAIT_FOR_BARS_TO_DIE;
        }
        return 0;
    }
	/*系统配置改变*/
    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        if (mStatusBar != null) {
            mStatusBar.onConfigurationChanged(newConfig);
        }
    }
	/*打印堆栈*/
    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        if (mStatusBar != null) {
            mStatusBar.dump(fd, pw, args);
        }
    }
	/*从xml文件中获取PhoneStatusBar全路径,通过类加载器实例化类,调用其start*/
    private void createStatusBarFromConfig() {
        if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
        final String clsName = mContext.getString(R.string.config_statusBarComponent);
        if (clsName == null || clsName.length() == 0) {
            throw andLog("No status bar component configured", null);
        }
        Class<?> cls = null;
        try {
            cls = mContext.getClassLoader().loadClass(clsName);
        } catch (Throwable t) {
            throw andLog("Error loading status bar component: " + clsName, t);
        }
        try {
            mStatusBar = (BaseStatusBar) cls.newInstance();
        } catch (Throwable t) {
            throw andLog("Error creating status bar component: " + clsName, t);
        }
        mStatusBar.mContext = mContext;
        mStatusBar.mComponents = mComponents;
        mStatusBar.start();
        if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
    }

    private RuntimeException andLog(String msg, Throwable t) {
        Log.w(TAG, msg, t);
        throw new RuntimeException(msg, t);
    }
}

我们先从start方法开始分析

    @Override
    public void start() {
        if (DEBUG) Log.d(TAG, "start");
        mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
                mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
        mServiceMonitor.start();  // will call onNoService if no remote service is found
    }

这里实例化ServiceMonitor类start,继续分析ServiceMonitor

###ServiceMonitor

	...
    public ServiceMonitor(String ownerTag, boolean debug,
            Context context, String settingKey, Callbacks callbacks) {
        mTag = ownerTag + ".ServiceMonitor";
        mDebug = debug;
        mContext = context;
        mSettingKey = settingKey; // Settings.Secure.BAR_SERVICE_COMPONENT
        mCallbacks = callbacks;
    }

    public void start() {
        // listen for setting changes
		/*Settings.Secure.BAR_SERVICE_COMPONENT改变时回调*/
        ContentResolver cr = mContext.getContentResolver();
        cr.registerContentObserver(Settings.Secure.getUriFor(mSettingKey),
                false /*notifyForDescendents*/, mSettingObserver, UserHandle.USER_ALL);

        // listen for package/component changes
		//应用安装,改变,卸载会触发mBroadcastReceiver广播
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addDataScheme("package");
        mContext.registerReceiver(mBroadcastReceiver, filter);

        mHandler.sendEmptyMessage(MSG_START_SERVICE);
    }
	...

ServiceMOnitor是一个监听Settings.Secure.BAR_SERVICE_COMPONENT是否改变的类,在start中通过监听系统系统时应用的变化来启动服务

private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String pkg = intent.getData().getSchemeSpecificPart();
        if (mServiceName != null && mServiceName.getPackageName().equals(pkg)) {
            mHandler.sendMessage(mHandler.obtainMessage(MSG_PACKAGE_INTENT, intent));
        }
    }
};

应用装载时,通过Handler发送MSG_PACKAGE_INTENT消息事件,我们查看Handler消息回调

    // internal handler + messages used to serialize access to internal state
    public static final int MSG_START_SERVICE = 1; //启动服务,并非真正启动,会根据ServiceName进行判断
    public static final int MSG_CONTINUE_START_SERVICE = 2; //启动服务
    public static final int MSG_STOP_SERVICE = 3;//停止服务消息
    public static final int MSG_PACKAGE_INTENT = 4;//包安装事件消息
    public static final int MSG_CHECK_BOUND = 5;//包改变或者卸载时,重新启动服务消息
    public static final int MSG_SERVICE_DISCONNECTED = 6;//服务断开消息

    private final Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch(msg.what) {
                case MSG_START_SERVICE:
                    startService();
                    break;
                case MSG_CONTINUE_START_SERVICE:
                    continueStartService();
                    break;
                case MSG_STOP_SERVICE:
                    stopService();
                    break;
                case MSG_PACKAGE_INTENT:
                    packageIntent((Intent)msg.obj);
                    break;
                case MSG_CHECK_BOUND:
                    checkBound();
                    break;
                case MSG_SERVICE_DISCONNECTED:
                    serviceDisconnected((ComponentName)msg.obj);
                    break;
            }
        }
    };

    private void packageIntent(Intent intent) {
        if (mDebug) Log.d(mTag, "packageIntent intent=" + intent
                + " extras=" + bundleToString(intent.getExtras())
<think>嗯,用户想解决Android系统中SystemUI在锁屏时NavigationBar的自定义或问题处理。首先,我需要明确用户的具体需求是什么。可能的问题包括导航栏在锁屏时不显示、显示异常,或者用户想修改导航栏的布局或功能。接下来,我应该回顾一下SystemUINavigationBar的相关结构代码流程。 根据引用[3]的信息,NavigationBar的关键类包括NavigationBarComponent、NavigationBarNavigationBarView等,布局文件是navigation_bar.xml。锁屏时的NavigationBar可能涉及到不同的窗口或视图层级,可能需要检查锁屏状态下的布局加载逻辑。 首先,锁屏界面通常由Keyguard模块处理,可能需要查看Keyguard与NavigationBar的交互。在锁屏时,系统可能会隐藏导航栏或者限制某些功能,比如禁用返回键。用户可能遇到导航栏在锁屏时仍然显示,或者按钮无法正常工作的问题。 代码流程方面,锁屏状态下SystemUI的初始化可能有所不同。需要检查NavigationBar的可见性控制逻辑,比如在onViewCreated方法中是否有根据锁屏状态调整的代码,如引用[2]提到的prepareNavigationBarView()。此外,导航栏的配置文件如config.xml可能包含相关的布局设置,需要检查是否有针对锁屏的特殊配置。 另外,导航栏的触摸事件处理在KeyButtonView.java中,如果锁屏时需要阻止某些操作,可能需要在这里拦截事件。同时,引用[4]提到NavigationBarController管理导航栏的行为,可能需要检查控制器的状态是否在锁屏时被正确设置。 可能的解决方案包括修改布局文件的可见性属性,或者在代码中根据锁屏状态动态调整导航栏。例如,在onViewCreated中添加逻辑判断当前是否为锁屏状态,并隐藏或禁用导航栏。此外,可能需要处理窗口管理器中的标志位,确保导航栏在锁屏窗口中的正确显示。 用户可能需要具体的代码示例,比如如何检测锁屏状态,如何动态隐藏导航栏按钮。同时,要注意不同Android版本的差异,比如引用[2]提到的是Android9,而引用[3]是Android13,不同版本SystemUI的结构可能有所变化,需要确认用户的目标版本。 最后,生成相关问题需要考虑用户可能遇到的更深层次问题,比如性能优化、兼容性处理或事件拦截机制。这些问题可以帮助用户进一步解决复杂情况。</think>### Android SystemUI锁屏时NavigationBar定制与问题解决步骤 1. **确定导航栏可见性控制逻辑** 在锁屏界面,导航栏的显示状态由`KeyguardSecurityContainer``NavigationBarController`共同管理。需要检查`NavigationBarView.updateStates()`方法中的可见性判断条件: ```java // 示例代码路径:/frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java public void updateStates(boolean isKeyguardShowing) { if (isKeyguardShowing) { setVisibility(View.GONE); // 或特殊布局处理 } } ``` 锁屏状态通过`KeyguardUpdateMonitor`获取[^3][^4]。 2. **修改锁屏布局文件** 导航栏默认布局文件`navigation_bar.xml`需区分锁屏/非锁屏状态。通过创建`navigation_bar_keyguard.xml`实现特殊布局: ```xml <!-- 示例路径:/frameworks/base/packages/SystemUI/res/layout/navigation_bar_keyguard.xml --> <com.android.systemui.navigationbar.NavigationBarView> <include layout="@layout/back_keyguard"/> <!-- 定制返回键 --> <include layout="@layout/home_keyguard"/> <!-- 禁用主页键 --> </com.android.systemui.navigationbar.NavigationBarView> ``` 在`NavigationBarInflaterView`中加载对应布局[^3]。 3. **事件拦截处理** 在`KeyButtonView.onTouchEvent()`中增加锁屏状态判断: ```java // 示例路径:/frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/KeyButtonView.java public boolean onTouchEvent(MotionEvent event) { if (KeyguardStateController.getInstance().isShowing()) { return true; // 拦截锁屏时按键事件 } return super.onTouchEvent(event); } ``` 4. **动态显示控制(Android 13示例)** 在`NavigationBarFragment`中绑定锁屏状态监听: ```java // 示例路径:/frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarFragment.java KeyguardStateController.Callback mKeyguardCallback = new KeyguardStateController.Callback() { @Override public void onKeyguardShowingChanged() { mNavigationBarView.setLockScreenFlag(KeyguardUpdateMonitor.getInstance(context).isKeyguardShowing()); } }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值