Android 11 SystemUI 启动流程

news2024/9/22 1:31:21

SystemUI 有哪内容

从表面上看, 我们看到的状态栏、通知栏、下拉菜单、导航栏、锁屏、最近任务、低电提示等系统页面都是 SystemUI 的。SystemUI,在源码目录中位于: framework/base/packages 目录下, 可见 SystemUI 和 framework 是关联的, SystemUI 依赖了很多内部 API , 系统资源, SystemUI 编译是要依赖系统源码的。

SystemUI 也是一个应用,不过这个应用特殊之处在于他没有启动图标、也没有入口 Activity 。他的入口程序是一个服务:SystemUIService。 这个服务会被系统服务拉起来, 这个服务起来, SystemUI 应用进程就创建起来了,具体启动过程后面会分析。除了 SystemUIService , SystemUI 还有很多服务, 例如: 负责锁屏的KeyguardService、负责最近任务的 RecentsSystemUserService、负责壁纸的 ImageWallpaper 、负责截屏的TakeScreenshotService 等。

下面是PhoneStatusBarView的view 树形图:

PanelHolder

PanelHolder是用户下拉 status bar 后得到的 view。它主要包含 QuickSettings 和 Notification panel 两个部分。PanelHolder是一个继承自FrameLayout的自定义view,它的内容是通过include status_bar_expanded.xml进行填充的。PanelHolder的布局比较复杂,为了提高view的重用性大量的使用了include标签。下面是PanelHolder的view树形图, 只给出了了主要的view:

架构关系

在系统服务中,有一个服务是专门为 SystemUI 的状态栏服务的, 这个服务就是 StatusbarManagerService (简称:SMS),和这个服务关系比较密切的服务是 WindowManagerService(简称:WMS), SMS 主要管控的是状态栏、导航栏, 例如:我们可以设置全屏、沉浸式状态栏都是 SMS 在起作用。

services组件启动时配置列表 : (R.array.config_systemUIServiceComponents)

所有 SystemUIService 都是继承自 SystemUI.class , SystemUI.class 是一个抽象类

<item>com.android.systemui.util.NotificationChannels</item> 通知信息 
<item>com.android.systemui.keyguard.KeyguardViewMediator</item> 锁屏 
<item>com.android.systemui.recents.Recents</item> 近期列表
Android 10之后近期列表的显示被移到Launcher里面了。在Launcher3的一个 类中TouchInteractionService.java   IBinder mMyBinder = new IOverviewProxy.Stub() 通过AIDL的方法与systemUI通信
————————————————
<item>com.android.systemui.volume.VolumeUI</item> 声音UI显示 
<item>com.android.systemui.statusbar.phone.StatusBar</item> 状态栏及下拉面板
<item>com.android.systemui.usb.StorageNotification</item> usb通知管理 
<item>com.android.systemui.power.PowerUI</item>  电源UI显示管理
<item>com.android.systemui.media.RingtonePlayer</item> 播放铃声 
<item>com.android.systemui.keyboard.KeyboardUI</item>键盘UI 
<item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>快捷方式  
<item>@string/config_systemUIVendorServiceComponent</item>厂商相关定制 
<item>com.android.systemui.util.leak.GarbageMonitor$Service</item>垃圾监测器  
<item>com.android.systemui.LatencyTester</item> 延迟测试仪 
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>  关机界面的显示、全局控制
<item>com.android.systemui.ScreenDecorations</item>屏幕装饰  
<item>com.android.systemui.biometrics.AuthController</item>生物识别  
<item>com.android.systemui.SliceBroadcastRelayHandler</item> 切片广播 
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>  
<item>com.android.systemui.theme.ThemeOverlayController</item>  
<item>com.android.systemui.accessibility.WindowMagnification</item>  
<item>com.android.systemui.accessibility.SystemActions</item>  
<item>com.android.systemui.toast.ToastUI</item>  Toast
<item>com.android.systemui.wmshell.WMShell</item> 

一、SystemUI的启动流程

1、SystemServer

SystemServer启动后,会在Main Thread启动ActivityManagerService,当ActivityManagerService systemReady后,会去启动SystemUIService。

/frameworks/base/services/java/com/android/server/SystemServer.java

①main

    /**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }

②run

    private void run() {
        t.traceBegin("InitBeforeStartServices");
        ....
       // Create the system service manager.
            mSystemServiceManager = new SystemServiceManager(mSystemContext);
            mSystemServiceManager.setStartInfo(mRuntimeRestart,
                    mRuntimeStartElapsedTime, mRuntimeStartUptime);
            LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
        ....
       }

③mActivityManagerService.systemReady

mActivityManagerService.systemReady(() -> {
    //准备好服务
    Slog.i(TAG, "Making services ready");
    ....
            //跟踪开启系统界面
            t.traceBegin("StartSystemUI");
            try {
                //开启系统界面
                startSystemUi(context, windowManagerF);
            } catch (Throwable e) {
                reportWtf("starting System UI", e);
            }
            t.traceEnd();
    ....
}

④startSystemUi

    private static void startSystemUi(Context context, WindowManagerService windowManager) {
        PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
        Intent intent = new Intent();
        intent.setComponent(pm.getSystemUiServiceComponent());
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        //Slog.d(TAG, "Starting service: " + intent);
        //通过startServiceAsUser,SystemUIService就启动了,即SystemUI进程开机启动
        context.startServiceAsUser(intent, UserHandle.SYSTEM);
        windowManager.onSystemUiStarted();
    }

2、systemUIService

在SystemUIService的onCreate方法中会调用SystemUIApplication的startServicesIfNeeded方法,这个方法会调用 startServicesIfNeeded(SERVICES)方法启动一系列服务

    @Override
    public void onCreate() {
        super.onCreate();

        // Start all of SystemUI
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();

3、SystemUIApplication

/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java

①startServicesIfNeeded()

public void startServicesIfNeeded() {
    //获取所有的服务的路径,所有SERVICES统一继承了SystemUI类:
    String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
    startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
}

②startServicesIfNeeded(String metricsPrefix, String[] services)

在重载方法中将每一个名称通过反射来得到实例对象,然后依次调用每一个SystemUI的子类的start方法启动每一个模块。

private void startServicesIfNeeded(String metricsPrefix, String[] services) {
    if (mServicesStarted) {
        return;
    }
    mServices = new SystemUI[services.length];

    //检查一下,也许在我们开始之前很久它就已经完成了
    if (!mBootCompleteCache.isBootComplete()) {
        // check to see if maybe it was already completed long before we began
        // see ActivityManagerService.finishBooting()
        if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
            mBootCompleteCache.setBootComplete();
            if (DEBUG) {
                Log.v(TAG, "BOOT_COMPLETED was already sent");
            }
        }
    }

    final DumpManager dumpManager = mRootComponent.createDumpManager();

    Log.v(TAG, "Starting SystemUI services for user " +
            Process.myUserHandle().getIdentifier() + ".");
    TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
            Trace.TRACE_TAG_APP);
    //开始追踪
    log.traceBegin(metricsPrefix);
    final int N = services.length;
    //遍历services这个数组
    for (int i = 0; i < N; i++) {
        String clsName = services[i];
        if (DEBUG) Log.d(TAG, "loading: " + clsName);
        log.traceBegin(metricsPrefix + clsName);
        long ti = System.currentTimeMillis();
        try {
            SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
            if (obj == null) {
                Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
                obj = (SystemUI) constructor.newInstance(this);
            }
            mServices[i] = obj;
        } catch (ClassNotFoundException
                | NoSuchMethodException
                | IllegalAccessException
                | InstantiationException
                | InvocationTargetException ex) {
            throw new RuntimeException(ex);
        }

        if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
        //依次调用service的start方法启动服务
        mServices[i].start();
        log.traceEnd();

        // Warn if initialization of component takes too long
        //如果组件初始化时间过长,则发出警告
        ti = System.currentTimeMillis() - ti;
        if (ti > 1000) {
            Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms");
        }
        if (mBootCompleteCache.isBootComplete()) {
            mServices[i].onBootCompleted();
        }

        dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
    }
    mRootComponent.getInitController().executePostInitTasks();
    //结束追踪
    log.traceEnd();

    mServicesStarted = true;
}

4、SystemUI

/**
 * @see SystemUIApplication#startServicesIfNeeded()
 *系统界面应用  如果需要,启动服务
 */
public abstract class SystemUI implements Dumpable {
    protected final Context mContext;

    public SystemUI(Context context) {
        mContext = context;
    }

    public abstract void start();

    protected void onConfigurationChanged(Configuration newConfig) {
    }

    @Override
    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
    }

    protected void onBootCompleted() {
    }

    public static void overrideNotificationAppName(Context context, Notification.Builder n,
            boolean system) {
        final Bundle extras = new Bundle();
        String appName = system
                ? context.getString(com.android.internal.R.string.notification_app_name_system)
                : context.getString(com.android.internal.R.string.notification_app_name_settings);
        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName);

        n.addExtras(extras);
    }
}

二、状态栏

1、SystemBars

SystemBars加载基本全部SystemUI的界面显示,由前面可知调用的start方法实际上是每一个继承于SytemUI的子类中的方法

①start

②createStatusBarFromConfig

从string资源文件里面读取class name,通过java的反射机制实例化对象,然后调用start()方法启动,class name的值如下图:

    private void createStatusBarFromConfig() {
        ......
        String clsName = mContext.getString(R.string.config_statusBarComponent);
        ......
        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.start();
        ......
    }

③String

    <!-- Component to be used as the status bar service.  Must implement the IStatusBar
     interface.  This name is in the ComponentName flattened format (package/class)  -->
    <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>

2、StatusBar

①createAndAddWindows

    public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
        //创建状态栏
        makeStatusBarView(result);
        mNotificationShadeWindowController.attach();
        //创建状态栏的窗口
        mStatusBarWindowController.attach();
    }

②makeStatusBarView

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
    final Context context = mContext;
    .....
    FragmentHostManager.get(mPhoneStatusBarWindow)
            .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                //CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域)
                CollapsedStatusBarFragment statusBarFragment =
                        (CollapsedStatusBarFragment) fragment;

                PhoneStatusBarView oldStatusBarView = mStatusBarView;
                mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
                //传递statusBar处理下拉事件
                mStatusBarView.setBar(this);
                //传递 NotificationPanelView 显示下拉UI控制
                mStatusBarView.setPanel(mNotificationPanelViewController);
                mStatusBarView.setScrimController(mScrimController);
                //初始化通知栏区域
                statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
                ......
                    
            }).getFragmentManager()
            .beginTransaction()
            .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
                    CollapsedStatusBarFragment.TAG)
            .commit();
    .....

2、CollapsedStatusBarFragment

①onCreateView

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.status_bar, container, false);
    }

②initNotificationIconArea

public void initNotificationIconArea(NotificationIconAreaController
        notificationIconAreaController) {
    //notification_icon_area是在status_bar.xml的布局,它是属于通知Notification    
    //获取到 notification_icon_area,FrameLayout转为ViewGroup,
    ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
    //调用 notificationIconAreaController 获取通知要显示的view(LinearLayout)
    //在4中跟进
    mNotificationIconAreaInner =
            notificationIconAreaController.getNotificationInnerAreaView();
    //如果已经有显示的view,通过 view 父布局将其自身remove,然后再重新addView。
    //最后将 mNotificationIconAreaInner 显示出来(设置透明度为1,visibility为VISIBLE)
    if (mNotificationIconAreaInner.getParent() != null) {
        ((ViewGroup) mNotificationIconAreaInner.getParent())
                .removeView(mNotificationIconAreaInner);
    }
    notificationIconArea.addView(mNotificationIconAreaInner);

    //与上面一样
    ViewGroup statusBarCenteredIconArea = mStatusBar.findViewById(R.id.centered_icon_area);
    mCenteredIconArea = notificationIconAreaController.getCenteredNotificationAreaView();
    if (mCenteredIconArea.getParent() != null) {
        ((ViewGroup) mCenteredIconArea.getParent())
                .removeView(mCenteredIconArea);
    }
    statusBarCenteredIconArea.addView(mCenteredIconArea);

    //默认为显示,直到我们知道其他情况
    // Default to showing until we know otherwise.
    showNotificationIconArea(false);
}

③showNotificationIconArea

当状态栏下拉时,状态栏中的图标icon会慢慢的变成透明和不可见,就是通过hideSystemIconArea(true), hideNotificationIconArea(true)

//当状态栏下拉时,设置状态栏中的图标icon会慢慢的变成透明和不可见    
public void hideNotificationIconArea(boolean animate) {
        animateHide(mNotificationIconAreaInner, animate);
        animateHide(mCenteredIconArea, animate);
    }
//设置状态栏图标透明度为1,visibility为VISIBLE
    public void showNotificationIconArea(boolean animate) {
        animateShow(mNotificationIconAreaInner, animate);
        animateShow(mCenteredIconArea, animate);
    }

    public void hideOperatorName(boolean animate) {
        if (mOperatorNameFrame != null) {
            animateHide(mOperatorNameFrame, animate);
        }
    }

    public void showOperatorName(boolean animate) {
        if (mOperatorNameFrame != null) {
            animateShow(mOperatorNameFrame, animate);
        }

④animateShow

    private void animateShow(View v, boolean animate) {
        v.animate().cancel();
        //(设置透明度为1,visibility为VISIBLE)
        v.setVisibility(View.VISIBLE);
        if (!animate) {
            v.setAlpha(1f);
            return;
        }
        .....
    }

⑤animateHiddenState

//将视图动画化为 INVISIBLE 或 GONE    
private void animateHiddenState(final View v, int state, boolean animate) {
        v.animate().cancel();
        if (!animate) {
            v.setAlpha(0f);
            v.setVisibility(state);
            return;
        }

        v.animate()
                .alpha(0f)
                .setDuration(160)
                .setStartDelay(0)
                .setInterpolator(Interpolators.ALPHA_OUT)
                .withEndAction(() -> v.setVisibility(state));
    }

3、status_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->

<!--    android:background="@drawable/status_bar_closed_default_background" -->
<com.android.systemui.statusbar.phone.PhoneStatusBarView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
    android:layout_width="match_parent"
    android:layout_height="@dimen/status_bar_height"
    android:id="@+id/status_bar"
    android:orientation="vertical"
    android:focusable="false"
    android:descendantFocusability="afterDescendants"
    android:accessibilityPaneTitle="@string/status_bar"
    >

    <!-- add for KGDAANWIKFRA-135 -->
    <View
        android:layout_width="match_parent"
        android:layout_height="@dimen/status_bar_height"
        android:id="@+id/status_bar_dark_view"
        android:background="#ff000000"
        android:visibility="gone" />
    //<!--通知灯,默认gone-->
    <ImageView
        android:id="@+id/notification_lights_out"
        android:layout_width="@dimen/status_bar_icon_size"
        android:layout_height="match_parent"
        android:paddingStart="@dimen/status_bar_padding_start"
        android:paddingBottom="2dip"
        android:src="@drawable/ic_sysbar_lights_out_dot_small"
        android:scaleType="center"
        android:visibility="gone"
        />
    //<!--状态栏内容-->
    <LinearLayout android:id="@+id/status_bar_contents"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingStart="@dimen/status_bar_padding_start"
        android:paddingEnd="@dimen/status_bar_padding_end"
        android:paddingTop="@dimen/status_bar_padding_top"
        android:orientation="horizontal"
        >
        <FrameLayout
            android:layout_height="match_parent"
            android:layout_width="0dp"
            android:layout_weight="1">

            <include layout="@layout/heads_up_status_bar_layout" />

            <!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the
             individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and
             DISABLE_NOTIFICATION_ICONS, respectively -->
            <LinearLayout
                android:id="@+id/status_bar_left_side"
                android:layout_height="match_parent"
                android:layout_width="match_parent"
                android:clipChildren="false"
            >
                <ViewStub
                    android:id="@+id/operator_name"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:layout="@layout/operator_name" />

                <com.android.systemui.statusbar.policy.Clock
                    android:id="@+id/clock"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:textAppearance="@style/TextAppearance.StatusBar.Clock"
                    android:singleLine="true"
                    android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
                    android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
                    android:gravity="center_vertical|start"
                />

                //<!--通知图标区域-->
                <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                    android:id="@+id/notification_icon_area"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:orientation="horizontal"
                    android:clipChildren="false"/>

            </LinearLayout>
        </FrameLayout>

        <!-- Space should cover the notch (if it exists) and let other views lay out around it -->
        <android.widget.Space
            android:id="@+id/cutout_space_view"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:gravity="center_horizontal|center_vertical"
        />

        //居中的图标区域        
        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
            android:id="@+id/centered_icon_area"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:clipChildren="false"
            android:gravity="center_horizontal|center_vertical"/>

        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal"
            android:gravity="center_vertical|end"
            >
            //<!--系统图标-->
            <include layout="@layout/system_icons" />
        </com.android.keyguard.AlphaOptimizedLinearLayout>
    </LinearLayout>
    //<!--紧急密码管理员文本-->
    <ViewStub
        android:id="@+id/emergency_cryptkeeper_text"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout="@layout/emergency_cryptkeeper_text"
    />

</com.android.systemui.statusbar.phone.PhoneStatusBarView>

4、NotificationIconAreaController

①getNotificationInnerAreaView

    /**
     * Returns the view that represents the notification area.+
     * 返回表示通知区域的视图。
     */
    public View getNotificationInnerAreaView() {
        return mNotificationIconArea;
    }

②initializeNotificationAreaViews

/**
 * Initializes the views that will represent the notification area.
 * 初始化将表示通知区域的视图。
 */
protected void initializeNotificationAreaViews(Context context) {
    reloadDimens(context);
    
    LayoutInflater layoutInflater = LayoutInflater.from(context);
    //通知图标区域布局
    mNotificationIconArea = inflateIconArea(layoutInflater);
    //通知图标
    mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons);
    //获取通知滚动布局
    mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
    //中心图标区域布局
    mCenteredIconArea = layoutInflater.inflate(R.layout.center_icon_area, null);
    //居中的图标
    mCenteredIcon = mCenteredIconArea.findViewById(R.id.centeredIcon);

    initAodIcons();
}

③inflateIconArea

    protected View inflateIconArea(LayoutInflater inflater) {
        return inflater.inflate(R.layout.notification_icon_area, null);
    }

三、status icon加载流程

1、 status_bar.xml

        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
            android:id="@+id/centered_icon_area"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:clipChildren="false"
            android:gravity="center_horizontal|center_vertical"/>

        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal"
            android:gravity="center_vertical|end"
            >
            <!--系统图标-->
            <include layout="@layout/system_icons" />
        </com.android.keyguard.AlphaOptimizedLinearLayout>

2、system_icons.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/system_icons"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical">
    //StatusIconContainer继承AlphaOptimizedLinearLayout
    <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:paddingEnd="@dimen/signal_cluster_battery_padding"
        android:gravity="center_vertical"
        android:orientation="horizontal"/>

    <com.android.systemui.BatteryMeterView android:id="@+id/battery"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:clipToPadding="false"
        android:clipChildren="false"
        systemui:textAppearance="@style/TextAppearance.StatusBar.Clock" />
</LinearLayout>

3、AlphaOptimizedLinearLayout

    //该方法用来标记当前view是否存在过度绘制,存在返回ture,不存在返回false,
    //api里面默认返回为true,status icon不存在过度绘制。
    @Override
    public boolean hasOverlappingRendering() {
        return false;
    }

4、CollapsedStatusBarFragment

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mStatusBar = (PhoneStatusBarView) view;
        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
            mStatusBar.restoreHierarchyState(
                    savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
        }
        // TINNO BEGIN, add for KGDAANWIKFRA-135
        if (Utils.TINNO_NOTCH_SCREEN) {
            mStatusBarDarkView = mStatusBar.findViewById(R.id.status_bar_dark_view);
            toggleStatusBarDarkView();
            mLastOrientation = getContext().getResources().getConfiguration().orientation;
        }
        // TINNO END
        //DarkIconManager初始化
        mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons),
                Dependency.get(CommandQueue.class));
        
        mDarkIconManager.setShouldLog(true);
        Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
        mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
        mClockView = mStatusBar.findViewById(R.id.clock);
        showSystemIconArea(false);
        showClock(false);
        initEmergencyCryptkeeperText();
        initOperatorName();
    }

4、StatusBarIconController

①DarkIconManager

    /**
     * Version of ViewGroup that observes state from the DarkIconDispatcher.
     */
    public static class DarkIconManager extends IconManager {
        private final DarkIconDispatcher mDarkIconDispatcher;
        private int mIconHPadding;

        public DarkIconManager(LinearLayout linearLayout, CommandQueue commandQueue) {
            super(linearLayout, commandQueue);
            mIconHPadding = mContext.getResources().getDimensionPixelSize(
                    R.dimen.status_bar_icon_padding);
            mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
        }
        //每个icon应该就是对应着代表顺序的index和数据类型为String的slot
        @Override
        protected void onIconAdded(int index, String slot, boolean blocked,
                StatusBarIconHolder holder) {
            StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
            mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view);
        }
        .....

        //onSetIcon可能就是刷新icon状态的
        @Override
        public void onSetIcon(int viewIndex, StatusBarIcon icon) {
            super.onSetIcon(viewIndex, icon);
            mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex));
        }
        .....

    }

②IconManager

public static class IconManager implements DemoMode {
    .....
    protected void onIconAdded(int index, String slot, boolean blocked,
        StatusBarIconHolder holder) {
        
        addHolder(index, slot, blocked, holder);
    }    
    
    protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
        StatusBarIconHolder holder) {
        switch (holder.getType()) {
            case TYPE_ICON:
                return addIcon(index, slot, blocked, holder.getIcon());
    
            case TYPE_WIFI:
                return addSignalIcon(index, slot, holder.getWifiState());
    
            case TYPE_MOBILE:
                return addMobileIcon(index, slot, holder.getMobileState());
        }
        return null;
    }
    
    @VisibleForTesting
    protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
            StatusBarIcon icon) {
        
            StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
            view.set(icon);
            mGroup.addView(view, index, onCreateLayoutParams());
            return view;
    }
        .....
}

5、StatusBarIconControllerImpl

//继承StatusBarIconList
public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
        ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {   
    ......        
    @Inject
    public StatusBarIconControllerImpl(Context context, CommandQueue commandQueue) {
        //config_statusBarIcons
        super(context.getResources().getStringArray(
                com.android.internal.R.array.config_statusBarIcons));
        Dependency.get(ConfigurationController.class).addCallback(this);
     .....
    }
}

6、StatusBarIconList

在初始化的时候就已经定义好了所有的slots,然后从framework中加载出来,index就是string-array中的顺序。

public class StatusBarIconList {
    private ArrayList<Slot> mSlots = new ArrayList<>();

    public StatusBarIconList(String[] slots) {
        final int N = slots.length;
        for (int i=0; i < N; i++) {
            mSlots.add(new Slot(slots[i], null));
        }
    }

7、config_statusBarIcons

<string-array name="config_statusBarIcons">
        <item><xliff:g id="id">@string/status_bar_rotate</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_headset</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_data_saver</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_ime</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_sync_failing</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_sync_active</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_nfc</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_tty</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_speakerphone</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_clock</xliff:g></item>
    </string-array>
 
    <string translatable="false" name="status_bar_rotate">rotate</string>
    <string translatable="false" name="status_bar_headset">headset</string>
    <string translatable="false" name="status_bar_data_saver">data_saver</string>
    <string translatable="false" name="status_bar_managed_profile">managed_profile</string>
    <string translatable="false" name="status_bar_ime">ime</string>
    <string translatable="false" name="status_bar_sync_failing">sync_failing</string>
    <string translatable="false" name="status_bar_sync_active">sync_active</string>
    <string translatable="false" name="status_bar_cast">cast</string>
    <string translatable="false" name="status_bar_hotspot">hotspot</string>
    <string translatable="false" name="status_bar_location">location</string>
    <string translatable="false" name="status_bar_bluetooth">bluetooth</string>
    <string translatable="false" name="status_bar_nfc">nfc</string>
    <string translatable="false" name="status_bar_tty">tty</string>
    <string translatable="false" name="status_bar_speakerphone">speakerphone</string>
    <string translatable="false" name="status_bar_zen">zen</string>
    <string translatable="false" name="status_bar_mute">mute</string>
    <string translatable="false" name="status_bar_volume">volume</string>
    <string translatable="false" name="status_bar_wifi">wifi</string>
    <string translatable="false" name="status_bar_cdma_eri">cdma_eri</string>
    <string translatable="false" name="status_bar_data_connection">data_connection</string>
    <string translatable="false" name="status_bar_phone_evdo_signal">phone_evdo_signal</string>
    <string translatable="false" name="status_bar_phone_signal">phone_signal</string>
    <string translatable="false" name="status_bar_battery">battery</string>
    <string translatable="false" name="status_bar_alarm_clock">alarm_clock</string>
    <string translatable="false" name="status_bar_secure">secure</string>
    <string translatable="false" name="status_bar_clock">clock</string>
    <string translatable="false" name="status_bar_mobile">mobile</string>
    <string translatable="false" name="status_bar_vpn">vpn</string>
    <string translatable="false" name="status_bar_ethernet">ethernet</string>
    <string translatable="false" name="status_bar_airplane">airplane</string>

好了,到这里我们的第一部分初始化流程就讲完了

四、状态显示流程

由上面的初始化流程我们可以知道,每个icon都对应了slot,slot数量比较多,我们就挑一个常见的Headset讲下,其他的流程都是大致一样的。

1、PhoneStatusBarPolicy

初始化注册了大量的监听

①init

//  初始化headset的slot
mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);
/** Initialize the object after construction. */
public void init() {
    // listen for broadcasts
    IntentFilter filter = new IntentFilter();
    //  注册headset状态变化的action
    filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
    
    filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
    filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
    filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
    filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
    filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
    mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
    Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen);

    mRingerModeTracker.getRingerMode().observeForever(observer);
    mRingerModeTracker.getRingerModeInternal().observeForever(observer);
    ....
    }

②BroadcastReceiver

    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            switch (action) {
                case Intent.ACTION_SIM_STATE_CHANGED:
                    // Avoid rebroadcast because SysUI is direct boot aware.
                    if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
                        break;
                    }
                    break;
                case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:
                    updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
                            TelecomManager.TTY_MODE_OFF));
                    break;
                case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
                case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
                case Intent.ACTION_MANAGED_PROFILE_REMOVED:
                    updateManagedProfile();
                    break;
                //监听ACTION_HEADSET_PLUG
                case AudioManager.ACTION_HEADSET_PLUG:
                    updateHeadsetPlug(context, intent);
                    break;
            }
        }
    };

③updateHeadsetPlug

完成icon添加和状态监听,然后当收到对应的action变化的时候,更新headset icon

    private void updateHeadsetPlug(Context context, Intent intent) {
        boolean connected = intent.getIntExtra("state", 0) != 0;
        boolean hasMic = intent.getIntExtra("microphone", 0) != 0;
        if (connected) {
            String contentDescription = mResources.getString(hasMic
                    ? R.string.accessibility_status_bar_headset
                    : R.string.accessibility_status_bar_headphones);
            //setIcon负责设置icon
            mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.stat_sys_headset_mic
                    : R.drawable.stat_sys_headset, contentDescription);
            //setIconVisibility则根据connected状态设置icon的可见性
            mIconController.setIconVisibility(mSlotHeadset, true);
        } else {
            /*UNISOC: Add for bug 1130932 {@ */
            AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
            if (!audioManager.isWiredHeadsetOn()) {
                //setIconVisibility则根据connected状态设置icon的可见性
                mIconController.setIconVisibility(mSlotHeadset, false);
            }
            /* @} */
        }
    }

2、StatusBarIconControllerImpl

①setIcon

@Override
public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
    //根据slot找到对应的index
    int index = getSlotIndex(slot);
    //用index取得对应的icon
    StatusBarIconHolder holder = getIcon(index, 0);
    if (holder == null) {
        //第一次的时候,icon == null,所以通过new StatusBarIcon创建一个
        StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
                Icon.createWithResource(
                        mContext, resourceId), 0, 0, contentDescription);
        holder = StatusBarIconHolder.fromIcon(icon);
        //然后调用此方法
        setIcon(index, holder);
    } else {
        holder.getIcon().icon = Icon.createWithResource(mContext, resourceId);
        holder.getIcon().contentDescription = contentDescription;
        handleSet(index, holder);
    }
}


@Override
public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
    boolean isNew = getIcon(index, holder.getTag()) == null;
    super.setIcon(index, holder);

    if (isNew) {
        addSystemIcon(index, holder);
    } else {
        handleSet(index, holder);
    }
}

private void addSystemIcon(int index, StatusBarIconHolder holder) {
    String slot = getSlotName(index);
    int viewIndex = getViewIndex(index, holder.getTag());
    boolean blocked = mIconBlacklist.contains(slot);
    //onIconAdded-》初始化流程里面StatusBarIconController中添加icon的入口
    //到这里setIcon就添加完毕了
    mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));
}

②setIconVisibility

public void setIconVisibility(String slot, boolean visibility) {
    int index = getSlotIndex(slot);
    StatusBarIconHolder holder = getIcon(index, 0);
    if (holder == null || holder.isVisible() == visibility) {
        return;
    }
    holder.setVisible(visibility);
    //icon已经创建成功了,icon非空,并且第一次是icon.visible != visibility的
    //就会顺利的走到handleSet(index, icon)
    handleSet(index, holder);
}

private void handleSet(int index, StatusBarIconHolder holder) {
    int viewIndex = getViewIndex(index, holder.getTag());
    //初始化流程里面StatusBarIconController中setIcon的地方
    //到这里setIconVisibility也设置完毕了
    mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
}

五、创建状态栏的窗口

1、StatusBarWindowController

mStatusBarWindowController.attach()

    /**
     * Adds the status bar view to the window manager.
     */
    public void attach() {
        // Now that the status bar window encompasses the sliding panel and its
        // translucent backdrop, the entire thing is made TRANSLUCENT and is
        // hardware-accelerated.
        mLp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                mBarHeight,
                WindowManager.LayoutParams.TYPE_STATUS_BAR,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                PixelFormat.TRANSLUCENT);
        mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
        mLp.token = new Binder();
        mLp.gravity = Gravity.TOP;
        mLp.setFitInsetsTypes(0 /* types */);
        mLp.setTitle("StatusBar");
        mLp.packageName = mContext.getPackageName();
        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;

        //WindowManager中添加view
        //mStatusBarView = mSuperStatusBarViewFactory.getStatusBarWindowView();
        //private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
        mWindowManager.addView(mStatusBarView, mLp);
        mLpChanged.copyFrom(mLp);
    }

2、SuperStatusBarViewFactory

    /**
     * Gets the inflated {@link StatusBarWindowView} from {@link R.layout#super_status_bar}.
     * Returns a cached instance, if it has already been inflated.
     */
    public StatusBarWindowView getStatusBarWindowView() {
        if (mStatusBarWindowView != null) {
            return mStatusBarWindowView;
        }

        //由其可知加载的布局来自于super_status_bar
        mStatusBarWindowView =
                (StatusBarWindowView) mInjectionInflationController.injectable(
                LayoutInflater.from(mContext)).inflate(R.layout.super_status_bar,
                /* root= */ null);
        if (mStatusBarWindowView == null) {
            throw new IllegalStateException(
                    "R.layout.super_status_bar could not be properly inflated");
        }
        return mStatusBarWindowView;
    }


3、super_status_bar.xml

从前面可知这里会用CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域),完成图标的显示

<!-- This is the status bar window. -->
<com.android.systemui.statusbar.phone.StatusBarWindowView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sysui="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
    >
        <FrameLayout
            android:id="@+id/status_bar_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="gone"
        />

        <FrameLayout
            android:id="@+id/car_top_navigation_bar_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>

</com.android.systemui.statusbar.phone.StatusBarWindowView>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/728866.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3脚本绑定CodeMirror的使用

代码&#xff1a; <template><CodeMirrorref"codeMirror":value"codeVal":languageSingle"languageSingle":readOnly"!isEdit"submitCode"submitCode"></CodeMirror> </template><script setup…

文华财经期货APP随身行和同花顺期货可以模拟交易的期货软件,那个更好用?

期货app是一种可以在手机上进行期货交易和行情分析的软件&#xff0c;它可以让投资者随时随地掌握期货市场的动态&#xff0c;进行投资决策。随着科技的发展&#xff0c;越来越多的期货投资者选择通过手机端app来进行期货交易&#xff0c;享受随时随地、方便快捷的服务。市面上…

Js获取浏览器地址栏参数

获取浏览器地址参数 //获取浏览器地址栏参数function getQueryString(name) {var reg new RegExp("(^|&)" name "([^&]*)(&|$)");var result window.location.search.substr(1).match(reg);if (result ! null) {return unescape(result[2…

VMware 17虚拟Ubuntu 22.04设置共享目录

之前使用VM 17之前的版本虚拟CentOS&#xff0c;设置共享目录非常方便&#xff0c;在CentOS中安装VMware Tools即可。随着CentOS变成上游版本后&#xff0c;转向使用Ubuntu&#xff0c;VM也升级到了17&#xff0c;Ubuntu也升级到了最新的22.04&#xff0c;但是发现共享目录不能…

阿里大牛新产Java面试速成指南,主打就是躺着拿Ofeer

很多粉丝后台留言&#xff0c;Java程序员面临的竞争太激烈了…… 我自己也有实感&#xff0c;多年身处一线互联网公司&#xff0c;虽没有直面过求职跳槽的残酷&#xff0c;但经常担任技术面试考官&#xff0c;对程序员招聘市场的现状很清楚。导致现在激烈竞争的原因不外乎三方面…

sqlite-manage数据库可视化管理uniqpp

一、sqlite-manage介绍 sqlite-manage 是 SQLite 数据库可视化管理插件&#xff0c;更方前期查看和操作SQLite数据库&#xff0c;给APP开发者提供方便&#xff0c;避免重复造轮子。 内置增删改查工具类&#xff0c;可按需全局引用或单独引用。 二、使用sqlite要打开模块选项 三…

java 计算网段范围 分析网段包含关系

目录 一、网段范围 二、思路说明 三、代码 1、将一个ip转为数字 2、转换子网掩码&#xff08;255.255.255.0 转为 24&#xff09; 3、根据 ip 与 掩码 计算最大值和最小值 4、测试 5、完整代码 四、难点讲解 1、转换子网掩码&#xff0c; 例&#xff1a;255.255.25…

【习题之Python篇】习题24——回文

问题描述 2020年春节期间&#xff0c;有一个特殊的日期引起了大家的注意&#xff1a;2020年2月2日。因为如果将这个日期按 yyyymmdd 的格式写成一个8位数是 &#xff0c;恰好是一个回文数。我们称这样的日期是回文日期。 有人表示 是“千年一遇”的特殊日子。对此小明很不认同…

JavaWeb 速通CSS

目录 一、CSS入门 1.基本介绍 : 2.CSS的作用 : 3.CSS的语法 : 二、CSS样式 1.字体颜色&#xff1a; 1 说明 2 演示 2.边框 : 1 说明 2 演示 3.背景颜色 : 1 说明 2 演示 4.字体样式 : 1 说明 2 演示 5.div块居中 : 1 说明 2 演示 6.div文本居中 : 1 说明 2 演示 7.超…

【软件测试】Git详细-获取Git仓库,全网最全一篇打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 官方提供了两种获…

GCC is no longer supported解决方法Android Studio

先说解决办法&#xff1a; 找到 -DANDROID_TOOLCHAINgcc进行删除。 原因是NDK版本过高了&#xff0c;r13b开始&#xff0c;llvm / Clang成为默认工具链&#xff0c;r18b开始删除了gcc。 删掉-DANDROID_TOOLCHAINgcc后&#xff0c;构建系统会默认使用clang。

最大流?费用流?结合二分图?例题

最大流 给出起点&#xff0c;终点&#xff0c;与边&#xff0c;边有最大流量限制&#xff0c;问从起点在不超过边的流量限制的情况下最大能从起点流多少流量到终点 反悔思想&#xff1a;如果我们每次找到一条路径就把这条路径上流量最小的边删去直到没有路径连接起点和终点&am…

飞行动力学 - 第5节-part3-爬升性能随高度的变化趋势 之 基础点摘要

飞行动力学 - 第5节-part3-爬升性能随高度的变化趋势 之 基础点摘要 1. 动力学方程2. 爬升角、爬升率趋势3. 参考资料 1. 动力学方程 回顾下&#xff0c;根据牛顿第一运动定律给出的动力学方程&#xff1a; 2. 爬升角、爬升率趋势 从推导公式的角度&#xff0c;上述趋势需要考…

按下数实融合的加速键,新华三推动基础设施变革

去年底&#xff0c;生成式AI&#xff08;AIGC&#xff09;开始席卷全球&#xff0c;吸引社会各界的广泛关注。 正所谓AI黄金时代的到来&#xff0c;将重新定义各行各业。AIGC热浪来袭&#xff0c;标志着在数实融合的大趋势下&#xff0c;人工智能大范围应用的奇点已经来临&…

Excel转图片(Java方式)

先看效果、先看效果、先看效果 左侧&#xff1a;Excel的截图 右侧&#xff1a;生成的图片 开发工具&#xff1a; Eclipse 开发环境&#xff1a; JDK1.8 使用技术&#xff1a; Graphics2D&#xff1a;&#xff08;JDK自带&…

使用python调用ChatGPT API 简单示例

如果你已经获得了OpenAI的API密钥&#xff0c;并且想要使用Python发起ChatGPT对话&#xff0c;你可以使用OpenAI的Python SDK来实现。下面是一个简单的示例代码&#xff1a; 首先&#xff0c;你需要确保已安装OpenAI的Python SDK。你可以使用pip来安装&#xff1a; pip insta…

模板(上)

文章目录 泛型编程函数模板类模板 一、泛型编程 泛型编程&#xff1a;编写与类型无关的通用代码&#xff0c;是代码复用的一种手段。模板是泛型编程的基础。 void Swap(int& left, int& right) {int temp left;left right;right temp; } void Swap(double& …

什么是混合云?

混合云&#xff08;Hybrid Cloud&#xff09;是指结合了私有云和公有云的计算环境。私有云是指在企业内部建立的基础设施&#xff0c;由企业自己管理和控制&#xff1b;而公有云是由第三方云服务提供商&#xff08;如亚马逊AWS、微软Azure、谷歌云等&#xff09;提供的计算资源…

浅谈无效化一个空窗口的后果

有时候&#xff0c;你可能会注意到这样一个现象&#xff1a;桌面上的所有窗口都刷新了自身并产生了闪烁。 导致这个现象的原因之一是一个所谓的空句柄窗口 Bug。 如果你研究过 Win32 SDK 编程&#xff0c;则你应该比较熟悉这个函数&#xff1a;InvalidateRect。 调用这个函数可…

C# PaddleInference 图片旋转角度检测

效果 项目 VS2022.net4.8 OpenCvSharp4Sdcb.PaddleInference 代码 using OpenCvSharp; using Sdcb.PaddleInference; using Sdcb.PaddleInference.Native; using System; using System.Drawing; using System.Globalization; using System.Linq; using System.Runtime.Interop…