首页 > 其他分享 >UsbHostManager解析

UsbHostManager解析

时间:2024-08-18 23:38:47浏览次数:11  
标签:usb cb host done context device 解析 UsbHostManager

UsbHostManager和UsbDeviceManager的区别在于,UsbDeviceManager是将手机作为一个设备,比如手机连上电脑,使用adb、mtp等;而UsbHostManager,是将手机作为一个host,比如手机连接usb鼠标、usb摄像头等,就会new出一个UsbDevice出来。

UsbHostManager初始化

UsbHostManager和UsbDeviceManager都是在UsbService中创建的

(frameworks\base\services\usb\java\com\android\server\usb\UsbService.java),如下所示:

public UsbService(Context context) {
        mContext = context;
 
        mUserManager = context.getSystemService(UserManager.class);
        mSettingsManager = new UsbSettingsManager(context, this);
        mPermissionManager = new UsbPermissionManager(context, this);
        mAlsaManager = new UsbAlsaManager(context);
 
        final PackageManager pm = mContext.getPackageManager();
        if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
            //创建UsbHostManager
            mHostManager = new UsbHostManager(context, mAlsaManager, mPermissionManager);
        }
        if (new File("/sys/class/android_usb").exists()) {
            //创建UsbDeviceManager
            mDeviceManager = new UsbDeviceManager(context, mAlsaManager, mSettingsManager,
                    mPermissionManager);
        }
        if (mHostManager != null || mDeviceManager != null) {
            mPortManager = new UsbPortManager(context);
        }
 
        BroadcastReceiver receiver = new BroadcastReceiver() {
 			......
        };
		...
        mContext.registerReceiver(receiver, filter, null, null);
    }

上面为UsbService的构造函数,其在构造函数中new了UsbHostManager和UsbDeviceManager。UsbService.java中一个systemReady()方法,该方法如下:

public void systemReady() {
        mAlsaManager.systemReady();
        if (mDeviceManager != null) {
            mDeviceManager.systemReady();
        }
        if (mHostManager != null) {
            mHostManager.systemReady();
        }
        if (mPortManager != null) {
            mPortManager.systemReady();
        }
    }

该方法会分别调用在UsbService构造函数中new出的mAlsaManager、mDeviceManager、mHostManager、和mPortManager的systemReady()方法。那我们现在来看UsbHostManager,UsbHostManager在UsbHostManager.java中(.\frameworks\base\services\usb\java\com\android\server\usb\UsbHostManager.java),在UsbHostManager中,有一个成员变量如下:

    // 包含所有连接的USB设备
    private final HashMap<String, UsbDevice> mDevices = new HashMap<>();

和host连接的USB设备会保存在这个HashMap中。

刚才已经分析了UsbService的systemReady()方法会调用UsbHostManager的systemReady(),该方法如下:

    public void systemReady() {
        synchronized (mLock) {
            // 创建一个线程调用本机代码等待USB主机事件。
            // 这个线程会在usbDeviceAdded和usbdevicermoved上回调我们。
            Runnable runnable = this::monitorUsbHostBus;
            new Thread(null, runnable, "UsbService host thread").start();
        }
    }

可以看到,UsbHostManager的systemReady()方法会创建一个线程,该线程会运行monitorUsbHostBus函数,该函数在什么地方呢?

  private native void monitorUsbHostBus();

在UsbHostManager类中有如下声明,可以看出来这个函数是一个Native函数,即该调用是一个JNI调用。

UsbHostManager的Native层

该函数对应在com_android_server_UsbHostManager.cpp中

(frameworks\base\services\core\jni\com_android_server_UsbHostManager.cpp)ndroid_server_UsbHostManager_monitorUsbHostBus函数,如下所示:

static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz)
{
    struct usb_host_context* context = usb_host_init();
    if (!context) {
        ALOGE("usb_host_init failed");
        return;
    }
    // 这个永远不会返回,所以直接传递这个是安全的
    usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
}

该函数进来之后通过usb_host_init()函数创建了一个struct usb_host_context对象,usb_host_init()函数在system\core\libusbhost\usbhost.c中定义,如下:

struct usb_host_context *usb_host_init()
{
    struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context));
    if (!context) {
        fprintf(stderr, "out of memory in usb_host_context\n");
        return NULL;
    }
    context->fd = inotify_init();
    if (context->fd < 0) {
        fprintf(stderr, "inotify_init failed\n");
        free(context);
        return NULL;
    }
    return context;
}

可以看到函数进来分配了一块struct usb_host_context内存之后,使用inotify_init()新建了一个INotify,并且将这个INotify的fd赋值给了usb_host_context的成员变量。什么是INotify呢?

简单说一下,INotify是一个内核用于通知用户空间程序文件系统发生变化的一种机制,例如文件增加、删除等事件都可以立刻让用户得知。类似的机制还有,Hotplug 是一种内核向用户态应用通报关于热插拔设备一些事件发生的机制,桌面系统能够利用它对设备进行有效的管理;udev 动态地维护 /dev 下的设备文件等。后面再写关于INotify的内容。

再回来,usb_host_init创建了一个INotify之后,将这个INotify的fd赋值给了usb_host_context的成员变量,然后在android_server_UsbHostManager_monitorUsbHostBus中就进入了usb_host_run函数,需要注意这里将JNI中的jobject thiz作为指针也传给了usb_host_run。该函数也在usbhost.c中,如下:

void usb_host_run(struct usb_host_context *context,
                  usb_device_added_cb added_cb,
                  usb_device_removed_cb removed_cb,
                  usb_discovery_done_cb discovery_done_cb,
                  void *client_data)
{
    int done;
 
    done = usb_host_load(context, added_cb, removed_cb, discovery_done_cb, client_data);
 
    while (!done) {
 
        done = usb_host_read_event(context);
    }
} /* usb_host_run() */

android_server_UsbHostManager_monitorUsbHostBus调用usb_host_run的时候传入了两个比较关键的函数指针,usb_device_added和usb_device_removed,看名字就知道这两个函数和USB设备的添加和删除有关,两个函数都定义在com_android_server_UsbHostManager.cpp文件中,我们后面再对这两个函数作分析。

usb_host_run()进来之后直接就调用了usb_host_load函数,同时将两个函数指针传了进去(discovery_done_cb为NULL),我们接着看usb_host_load函数,也是在usbhost.c中,如下:

int usb_host_load(struct usb_host_context *context,
                  usb_device_added_cb added_cb,
                  usb_device_removed_cb removed_cb,
                  usb_discovery_done_cb discovery_done_cb,
                  void *client_data)
{
    int done = 0;
    int i;
 
    context->cb_added = added_cb;
    context->cb_removed = removed_cb;
    context->data = client_data;
	
    /* 观察在USB_FS_DIR中添加和删除的文件 */
    context->wddbus = -1;
    for (i = 0; i < MAX_USBFS_WD_COUNT; i++)
        context->wds[i] = -1;
 
    /* 查看根目录是否有新的子目录  #define DEV_DIR "/dev"  */
    context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE);
    if (context->wdd < 0) {
        fprintf(stderr, "inotify_add_watch failed\n");
        if (discovery_done_cb)
            discovery_done_cb(client_data);
        return done;
    }
	//#define MAX_USBFS_WD_COUNT      10
    watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
 
    /* 在我们设置inotify之后,首先检查现有的设备 */	
    done = find_existing_devices(added_cb, client_data);
    if (discovery_done_cb)
        done |= discovery_done_cb(client_data);
 
    return done;
} /* usb_host_load() */

在usb_host_load中,首先将两个函数指针赋值给struct usb_host_context中的成员变量,client_data是在JNI中传入的thiz指针,discovery_done_cb传入的值为NULL。这里又看到了inotify开头的函数inotify_add_watch,这个也是和inotify机制相关的东西,这里再简单的说一下。

在inotify机制中,一个称作watches 的对象管理文件系统的变化事件,每一个 watch 是一个二元组(目标,事件掩码),目标可以是文件或目录,事件掩码表示应用希望关注的 inotify 事件,每一个位对应一个 inotify 事件。具体的一个Watch 对象通过 watch描述符引用, watch描述符就是inotify_add_watch函数返回的int值,添加一个watch通过目标文件或目录的路径名来添加,如果添加的watch是一个目录,那么该watches将会根据设置的事件掩码来返回在该目录下的所有文件上面发生的事件。

再回到代码中来,在usb_host_load中,首先通过inotify_add_watch,添加了一个目录watch("/dev"),其事件掩码设置为IN_CREATE | IN_DELETE(即观察创建和删除事件),并将这个watch的描述符储存在usb_host_context的成员变量wdd中。后面紧接着就调用watch_existing_subdirs函数,该函数也在usbhost.c中,如下所示:

static void watch_existing_subdirs(struct usb_host_context *context,
                                   int *wds, int wd_count)
{
    char path[100];
    int i, ret;
	//#define USB_FS_DIR          "/dev/bus/usb"
    wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE);
    if (wds[0] < 0)
        return;
 
    /* 查看USB_FS_DIR的现有子目录   #define USB_FS_DIR   "/dev/bus/usb" */
    for (i = 1; i < wd_count; i++) {
        snprintf(path, sizeof(path), USB_FS_DIR "/%03d", i);//
        ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE);
        if (ret >= 0)
            wds[i] = ret;
    }
}

wds是usb_host_context中的一个int数组,其长度为MAX_USBFS_WD_COUNT。这里又添加了一个目录watch("/dev/usb"),其事件掩码也设置为IN_CREATE | IN_DELETE,然后将watch描述符储存在wds[0]的位置。

紧接着就是一个for循环,该for循环将"/dev/bus/usb"目录下的设备目录(从001开始到MAX_USBFS_WD_COUNT),都添加到inotify中去,作为目录watch进行观察,然后各个watch描述符依次存放在wds数组剩余的位置中。

watch_existing_subdirs函数返回之后,在usb_host_load函数中接着又调用了find_existing_devices函数。(usbhost.c)如下所示:

/* 如果其中一个回调指示我们完成,则返回true */
static int find_existing_devices(usb_device_added_cb added_cb,
                                  void *client_data)
{
    char busname[32];
    DIR *busdir;
    struct dirent *de;
    int done = 0;
	//#define USB_FS_DIR          "/dev/bus/usb"
    busdir = opendir(USB_FS_DIR);
    if(busdir == 0) return 0;
 
    while ((de = readdir(busdir)) != 0 && !done) {
        if(badname(de->d_name)) continue;
 
        snprintf(busname, sizeof(busname), USB_FS_DIR "/%s", de->d_name);
        done = find_existing_devices_bus(busname, added_cb,
                                         client_data);
    } //end of busdir while
    closedir(busdir);
 
    return done;
}

该函数首先打开"/dev/bus/usb"目录,然后对该目录下的目录调用find_existing_devices_bus函数(usbhost.c),如下:

static int find_existing_devices_bus(char *busname,
                                     usb_device_added_cb added_cb,
                                     void *client_data)
{
    char devname[32];
    DIR *devdir;
    struct dirent *de;
    int done = 0;
 
    devdir = opendir(busname);
    if(devdir == 0) return 0;
 
    while ((de = readdir(devdir)) && !done) {
        if(badname(de->d_name)) continue;
 
        snprintf(devname, sizeof(devname), "%s/%s", busname, de->d_name);
        done = added_cb(devname, client_data);
    } // end of devdir while
    closedir(devdir);
 
    return done;
}

该函数会打开之前传入的目录,例如:"/dev/bus/usb/001",然后会遍历该目录下的所有文件,将文件名和之前的目录路径组合到一起就可以得到准确的文件位置,这个文件就代表了一个USB设备,该USB设备是在UsbHostManager运行之前就挂载好了的,所以此时上层还不知道该USB设备的存在,所以需要对该USB设备向上层做补充的通知,即通过added_cb函数指针调用usb_device_added函数。

这里我们还是先不分析usb_device_added函数,只需要知道find_existing_devices函数的作用是,将"/dev/bus/usb"子目录下,在UsbHostManager运行之前就挂载好了的USB设备向上做补充的通知,让上层感知到这些USB设备的存在。

这样usb_host_load函数就返回了,该函数返回之后,usb_host_run会进入一个while循环去执行usb_host_read_event函数(usbhost.c),usb_host_read_event函数非常的长如下:

int usb_host_read_event(struct usb_host_context *context)
{
    struct inotify_event* event;
    char event_buf[512];
    char path[100];
    int i, ret, done = 0;
    int offset = 0;
    int wd;
 
    ret = read(context->fd, event_buf, sizeof(event_buf));
    if (ret >= (int)sizeof(struct inotify_event)) {
        while (offset < ret && !done) {
            event = (struct inotify_event*)&event_buf[offset];
            done = 0;
            wd = event->wd;
            if (wd == context->wdd) {
                if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) {
                    context->wddbus = inotify_add_watch(context->fd, DEV_BUS_DIR, IN_CREATE | IN_DELETE);
                    if (context->wddbus < 0) {
                        done = 1;
                    } else {
                        watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
                        done = find_existing_devices(context->cb_added, context->data);
                    }
                }
            } else if (wd == context->wddbus) {
                if ((event->mask & IN_CREATE) && !strcmp(event->name, "usb")) {
                    watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
                    done = find_existing_devices(context->cb_added, context->data);
                } else if ((event->mask & IN_DELETE) && !strcmp(event->name, "usb")) {
                    for (i = 0; i < MAX_USBFS_WD_COUNT; i++) {
                        if (context->wds[i] >= 0) {
                            inotify_rm_watch(context->fd, context->wds[i]);
                            context->wds[i] = -1;
                        }
                    }
                }
            } else if (wd == context->wds[0]) {
                i = atoi(event->name);
                snprintf(path, sizeof(path), USB_FS_DIR "/%s", event->name);
                D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ?
                        "new" : "gone", path, i);
                if (i > 0 && i < MAX_USBFS_WD_COUNT) {
                    int local_ret = 0;
                    if (event->mask & IN_CREATE) {
                        local_ret = inotify_add_watch(context->fd, path,
                                IN_CREATE | IN_DELETE);
                        if (local_ret >= 0)
                            context->wds[i] = local_ret;
                        done = find_existing_devices_bus(path, context->cb_added,
                                context->data);
                    } else if (event->mask & IN_DELETE) {
                        inotify_rm_watch(context->fd, context->wds[i]);
                        context->wds[i] = -1;
                    }
                }
            } else {
                for (i = 1; (i < MAX_USBFS_WD_COUNT) && !done; i++) {
                    if (wd == context->wds[i]) {
                        snprintf(path, sizeof(path), USB_FS_DIR "/%03d/%s", i, event->name);
                        if (event->mask == IN_CREATE) {
                            D("new device %s\n", path);
                            done = context->cb_added(path, context->data);
                        } else if (event->mask == IN_DELETE) {
                            D("gone device %s\n", path);
                            done = context->cb_removed(path, context->data);
                        }
                    }
                }
            }
 
            offset += sizeof(struct inotify_event) + event->len;
        }
    }
 
    return done;
} /* usb_host_read_event() */

大致就是使用read调用去读取context->fd中的数据,这个fd就是一开始创建的inotify的fd,可以对这个fd做select、poll()等操作。read读到的数据可以转换为struct inotify_event类型的结构体,通过这个结构体中的一些字段来走不同的分支,这里先关注最后一个分支,即是wds数组中,索引0之外的watch描述符发生了变化就会走该分支,根据mask来判断是发生了IN_CREATE事件还是IN_DELETE事件,前者调用usb_device_added函数,后者调用usb_device_removed函数。

添加和删除USB设备时的回调函数

usb_device_added函数

当mask为IN_CREATE会调用该回调函数。

static int usb_device_added(const char *devAddress, void* clientData) {
    struct usb_device *device = usb_device_open(devAddress);
    if (!device) {
        ALOGE("usb_device_open failed\n");
        return 0;
    }
 
    const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
    int classID = deviceDesc->bDeviceClass;
    int subClassID = deviceDesc->bDeviceSubClass;
 
    // get the raw descriptors
    int numBytes = usb_device_get_descriptors_length(device);
    if (numBytes > 0) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        jobject thiz = (jobject)clientData;
        jstring deviceAddress = env->NewStringUTF(devAddress);
 
        jbyteArray descriptorsArray = env->NewByteArray(numBytes);
        const jbyte* rawDescriptors = (const jbyte*)usb_device_get_raw_descriptors(device);
        env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawDescriptors);
 
        env->CallBooleanMethod(thiz, method_usbDeviceAdded,
                deviceAddress, classID, subClassID, descriptorsArray);
 
        env->DeleteLocalRef(descriptorsArray);
        env->DeleteLocalRef(deviceAddress);
 
        checkAndClearExceptionFromCallback(env, __FUNCTION__);
    } else {
        // TODO return an error code here?
        ALOGE("error reading descriptors\n");
    }
 
    usb_device_close(device);
 
    return 0;
}

参数devAddress为发生变化的文件的路径,clientData为android_server_UsbHostManager_monitorUsbHostBus时设置的jobject thiz指针。该函数通过JNI调用UsbHostManager.java中的usbDeviceAdded方法:

   private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,
            byte[] descriptors) {
        if (DEBUG) {
            Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start");
        }
		
        if (isBlackListed(deviceAddress)) {
            if (DEBUG) {
                Slog.d(TAG, "device address is black listed");
            }
            return false;
        }
 
        if (isBlackListed(deviceClass, deviceSubclass)) {
            if (DEBUG) {
                Slog.d(TAG, "device class is black listed");
            }
            return false;
        }
				
        UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors);
        if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE
                && !checkUsbInterfacesBlackListed(parser)) {
            return false;
        }
 
        // Potentially can block as it may read data from the USB device.
        logUsbDevice(parser);
 
        if (isBlackListed(parser)) {
            if (DEBUG) {
                Slog.d(TAG, "device UsbDescriptorParser is black listed");
            }
            return false;
        }
		
        synchronized (mLock) {
            if (mDevices.get(deviceAddress) != null) {
                Slog.w(TAG, "device already on mDevices list: " + deviceAddress);
                //TODO If this is the same peripheral as is being connected, replace
                // it with the new connection.
                return false;
            }
 
            UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDeviceBuilder();
            if (newDeviceBuilder == null) {
                Slog.e(TAG, "Couldn't create UsbDevice object.");
                // Tracking
                addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
                        parser.getRawDescriptors());
            } else {
                UsbSerialReader serialNumberReader = new UsbSerialReader(mContext,
                        mPermissionManager, newDeviceBuilder.serialNumber);
                UsbDevice newDevice = newDeviceBuilder.build(serialNumberReader);
                serialNumberReader.setDevice(newDevice);
 
                mDevices.put(deviceAddress, newDevice);
                Slog.d(TAG, "Added device " + newDevice);
 
                // It is fine to call this only for the current user as all broadcasts are
                // sent to all profiles of the user and the dialogs should only show once.
                ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
                if (usbDeviceConnectionHandler == null) {
                    getCurrentUserSettings().deviceAttached(newDevice);
                } else {
                    getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
                            usbDeviceConnectionHandler);
                }
 
                mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
 
                // Tracking
                addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
                        parser.getRawDescriptors());
 
                // Stats collection
                FrameworkStatsLog.write(FrameworkStatsLog.USB_DEVICE_ATTACHED,
                        newDevice.getVendorId(), newDevice.getProductId(),
                        parser.hasAudioInterface(), parser.hasHIDInterface(),
                        parser.hasStorageInterface(),
                        FrameworkStatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
            }
        }
 
        if (DEBUG) {
            Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end");
        }
 
        return true;
    }

这个方法里面会向mDevices(一开始的那个HashMap)中填加UsbDevice。

usb_device_removed函数

该函数和上面的流程相同,这里直接贴代码。

static int usb_device_removed(const char *devAddress, void* clientData) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    jobject thiz = (jobject)clientData;
 
    jstring deviceAddress = env->NewStringUTF(devAddress);
    env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceAddress);
    env->DeleteLocalRef(deviceAddress);
    checkAndClearExceptionFromCallback(env, __FUNCTION__);
    return 0;
}
    private void usbDeviceRemoved(String deviceAddress) {
        if (DEBUG) {
            Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") end");
        }
 
        synchronized (mLock) {
            UsbDevice device = mDevices.remove(deviceAddress);
            if (device != null) {
                Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
                mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
                mPermissionManager.usbDeviceRemoved(device);
                getCurrentUserSettings().usbDeviceRemoved(device);
                ConnectionRecord current = mConnected.get(deviceAddress);
                // Tracking
                addConnectionRecord(deviceAddress, ConnectionRecord.DISCONNECT, null);
 
                if (current != null) {
                    UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress,
                            current.mDescriptors);
                        // Stats collection
                    FrameworkStatsLog.write(FrameworkStatsLog.USB_DEVICE_ATTACHED,
                            device.getVendorId(), device.getProductId(), parser.hasAudioInterface(),
                            parser.hasHIDInterface(),  parser.hasStorageInterface(),
                            FrameworkStatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
                            System.currentTimeMillis() - current.mTimestamp);
                }
            } else {
                Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone");
            }
        }
    }

标签:usb,cb,host,done,context,device,解析,UsbHostManager
From: https://www.cnblogs.com/linhaostudy/p/18366394

相关文章

  • 浏览器解析html文件src静态资源路径问题
    相对路径src资源引号内部不以/分割符开头,浏览器从当html文件前路径拼接url:场景a<imgsrc="static/1.jpeg"width="258"height="39"/>当前请求地址xxxx:80/html/1.html浏览器解析图片地址为xxxx:80/html/static/1.jpeg场景b<imgsrc="../static/1.jpeg"width="......
  • 深入解析当下流行的JDK版本:JDK 17的优势与特色
    引言Java作为一门成熟的编程语言,其发展历程中经历了多个重要版本的迭代。目前,JDK(JavaDevelopmentKit)已经更新至JDK21,但JDK17因其长期支持(LTS)特性,成为当前最流行的选择。本文将深入分析JDK17及其相较于其他主要版本的差异和优势。1.JDK版本概览1.1JDK8:里程碑式的版本......
  • 工厂设计模式:深入解析与应用
    工厂设计模式:深入解析与应用在软件开发领域,设计模式是解决常见问题的最佳实践。工厂设计模式(FactoryDesignPattern)作为一种创建型设计模式,提供了一种创建对象的接口,但由子类决定要实例化的类是哪一个。本文将深入探讨工厂设计模式的定义、分类、实现方式、优缺点以及应用......
  • Spring中的循环依赖:深入解析与解决方案
    Spring中的循环依赖:深入解析与解决方案在Spring框架中,循环依赖(CircularDependency)是一个常见的问题,它发生在两个或多个Bean之间相互依赖,形成一个闭环。循环依赖可能导致应用启动失败、Bean创建失败等问题,影响应用的正常运行。本文将深入探讨Spring中循环依赖的产生原因、......
  • Ubuntu无法解析域名DNS指向127.0.0.53问题处理
    首次尝试编辑/etc/resolved.conf文件DNS为114.114.114.114发现reboot重启后又恢复到127.0.0.53的内容再次尝试修改文件vi/etc/systemd/resolved.conf 在其中添加dns信息DNS=114.114.114.114保存退出依次执行重启解析服务systemctlrestartsystemd-resolved设置解析服务......
  • 如何快速将地址解析为经纬度坐标?
    GIS数据转换器的"地址转坐标"功能,可以帮助用户将地址文本快速转换为对应的经纬度坐标,广泛应用于地图定位、数据分析、GIS项目、在线导航、城市规划、紧急服务以及科学研究等多个领域,极大地提高了地理信息处理的效率和准确性。下面是详细的使用步骤:方法/步骤1. ......
  • 【OpenHarmony4.1 之 U-Boot 2024.07源码深度解析】019 - RK3568 Uboot 完整流程梳理
    【OpenHarmony4.1之U-Boot2024.07源码深度解析】019-RK3568Uboot完整流程梳理一、系统环境初始化:_start入口地址,初始化CPU环境二、系统环境初始化:_main入口,初始化堆栈,初始化gd全局环境变量,初始化CRuntime运行环境,开始执行board_init_f函数三、board_......
  • AI语言大模型商业价值深度解析
    点击蓝字关注我随着人工智能(AI)技术的飞速发展,特别是深度学习算法的进步,AI语言大模型在自然语言处理领域的表现日益突出。国内外多种语言大模型如:OpenAi的ChatGpt,阿里通义千问,百度文心一言,科大讯飞星火大模型等等纷纷推出相关应用以及算力服务。这些模型通过大规......
  • 【C++进阶学习】第十三弹——C++智能指针的深入解析
    前言:在C++编程中,内存管理是至关重要的一个环节。传统的手动内存管理方式容易导致内存泄漏、悬挂指针等问题。为了解决这些问题,C++引入了智能指针。本文将详细讲解C++中智能指针的概念、种类、使用方法以及注意事项。目录一、引言二、智能指针的原理及目的2.1智能指针......
  • 【漫谈C语言和嵌入式006】深入解析NVRAM与Flash:嵌入式系统中的关键非易失性存储器
            在嵌入式系统设计中,非易失性存储器(Non-VolatileMemory,NVM)扮演着至关重要的角色。这类存储器能够在设备断电后保留数据,对于存储系统配置、固件、用户数据等关键信息至关重要。NVRAM(非易失性随机存取存储器)和Flash是两种常见的非易失性存储器类型,各有其独特的......