Setting卸载SD卡,可以格式化,卸载过程中还可以卸载问题

问题是当我们再setting点击卸载SD卡的时候,这个时候“卸载SD卡”这一项会变成"正在卸载“并且变灰。但是很快又变亮了。而且卸载完后,还可以格式化。对于出现的这两个问题我们进行分析。


我们首先来看setting的代码

在Memory.java中实现了一个StorageEventListener :

    StorageEventListener mStorageListener = new StorageEventListener() {
        @Override
        public void onStorageStateChanged(String path, String oldState, String newState) {
            Log.i(TAG, "Received storage state changed notification that " + path +
                    " changed state from " + oldState + " to " + newState);
            for (StorageVolumePreferenceCategory category : mCategories) {
                final StorageVolume volume = category.getStorageVolume();
                if (volume != null && path.equals(volume.getPath())) {
                    category.onStorageStateChanged();
                    break;
                }
            }
        }
    };

并且在MountService中注册了

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

        final Context context = getActivity();

        mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);

        mStorageManager = StorageManager.from(context);
        mStorageManager.registerListener(mStorageListener);//注册listener

        addPreferencesFromResource(R.xml.device_info_memory);

        addCategory(StorageVolumePreferenceCategory.buildForInternal(context));

        final StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
        for (StorageVolume volume : storageVolumes) {
            if (!volume.isEmulated()) {
                addCategory(StorageVolumePreferenceCategory.buildForPhysical(context, volume));
            }
        }

        setHasOptionsMenu(true);
    }

在在Memory.java中的StorageEventListener 调用了onStorageStateChanged函数,再来看看StorageVolumePreferenceCategory

    public void onStorageStateChanged() {
        init();
        measure();
    }
    private void measure() {
        mMeasure.invalidate();
        mMeasure.measure();
    }


再来看看mMeature,在StorageVolumePreferenceCategory构造函数里赋值

    private StorageVolumePreferenceCategory(Context context, StorageVolume volume) {
        super(context);

        mVolume = volume;
        mMeasure = StorageMeasurement.getInstance(context, volume);

        mResources = context.getResources();
        mStorageManager = StorageManager.from(context);
        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);

        setTitle(volume != null ? volume.getDescription(context)
                : context.getText(R.string.internal_storage));
    }

而在StorageMeasurement中的measure函数如下

    public void measure() {
        if (!mHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) {
            mHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE);
        }
    }
再来看看消息处理

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_MEASURE: {
                    if (mCached != null) {
                        sendExactUpdate(mCached);
                        break;
                    }

                    final Context context = (mContext != null) ? mContext.get() : null;
                    if (context == null) {
                        return;
                    }

                    synchronized (mLock) {
                        if (mBound) {
                            removeMessages(MSG_DISCONNECT);
                            sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));//继续发送消息
                        } else {
                            Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
                            context.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
                                    UserHandle.OWNER);
                        }
                    }
                    break;
                }
                case MSG_CONNECTED: {
                    IMediaContainerService imcs = (IMediaContainerService) msg.obj;
                    measureApproximateStorage(imcs);//调用measureApproximateStorage
                    measureExactStorage(imcs);
                    break;
                }

看看measureApproximateStorage函数

        private void measureApproximateStorage(IMediaContainerService imcs) {
            final String path = mVolume != null ? mVolume.getPath()
                    : Environment.getDataDirectory().getPath();
            try {
                final long[] stats = imcs.getFileSystemStats(path);
                mTotalSize = stats[0];
                mAvailSize = stats[1];
            } catch (Exception e) {
                Log.w(TAG, "Problem in container service", e);
            }

            sendInternalApproximateUpdate();
        }

而在sendInternalApproximateUpdate函数中调用了receiver.updateApproximate

    private void sendInternalApproximateUpdate() {
        MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
        if (receiver == null) {
            return;
        }
        receiver.updateApproximate(this, mTotalSize, mAvailSize);
    }

再来看看这个mReceiver

    public void setReceiver(MeasurementReceiver receiver) {
        if (mReceiver == null || mReceiver.get() == null) {
            mReceiver = new WeakReference<MeasurementReceiver>(receiver);
        }
    }

StorageVolumePreferenceCategory中的mReceiver

    private MeasurementReceiver mReceiver = new MeasurementReceiver() {
        @Override
        public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize) {
            mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE, new long[] {
                    totalSize, availSize }).sendToTarget();
        }

        @Override
        public void updateDetails(StorageMeasurement meas, MeasurementDetails details) {
            mUpdateHandler.obtainMessage(MSG_UI_UPDATE_DETAILS, details).sendToTarget();
        }
    };

在onResume的时候setReceiver

    public void onResume() {
        mMeasure.setReceiver(mReceiver);
        measure();
    }

再看看消息处理:

    private Handler mUpdateHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_UI_UPDATE_APPROXIMATE: {
                    final long[] size = (long[]) msg.obj;
                    updateApproximate(size[0], size[1]);
                    break;
                }
                case MSG_UI_UPDATE_DETAILS: {
                    final MeasurementDetails details = (MeasurementDetails) msg.obj;
                    updateDetails(details);
                    break;
                }
            }
        }
    };


再看看updateApproximate函数:

    public void updateApproximate(long totalSize, long availSize) {
        mItemTotal.setSummary(formatSize(totalSize));
        mItemAvailable.setSummary(formatSize(availSize));

        mTotalSize = totalSize;

        final long usedSize = totalSize - availSize;

        mUsageBarPreference.clear();
        mUsageBarPreference.addEntry(0, usedSize / (float) totalSize, android.graphics.Color.GRAY);
        mUsageBarPreference.commit();

        updatePreferencesFromState();
    }

终于看到我们的主函数updatePreferencesFromState:

 private void updatePreferencesFromState() {
        // Only update for physical volumes
        if (mVolume == null) return;

        mMountTogglePreference.setEnabled(true);//先把卸载sd卡,安装sd卡置亮

        final String state = mStorageManager.getVolumeState(mVolume.getPath());//获取volume的状态

        if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            mItemAvailable.setTitle(R.string.memory_available_read_only);
        } else {
            mItemAvailable.setTitle(R.string.memory_available);
        }

        if (Environment.MEDIA_MOUNTED.equals(state)//如果当前是挂载的置亮
                || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
                mMountTogglePreference.setEnabled(true);
                mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject));
                mMountTogglePreference.setSummary(mResources.getString(R.string.sd_eject_summary));
                
                addPreference(mUsageBarPreference);
                addPreference(mItemTotal);
                addPreference(mItemAvailable);
        } else {
            if (Environment.MEDIA_UNMOUNTED.equals(state) || Environment.MEDIA_NOFS.equals(state)
                    || Environment.MEDIA_UNMOUNTABLE.equals(state)) {//如果是卸载,置亮;title变成安装SD卡
                mMountTogglePreference.setEnabled(true);
                mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
                mMountTogglePreference.setSummary(mResources.getString(R.string.sd_mount_summary));
            } else {//否则是没有SD卡,置灰
                mMountTogglePreference.setEnabled(false);
                mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
                mMountTogglePreference.setSummary(mResources.getString(R.string.sd_insert_summary));
            }

            removePreference(mUsageBarPreference);
            removePreference(mItemTotal);
            removePreference(mItemAvailable);
        }

        if (mUsbConnected && (UsbManager.USB_FUNCTION_MTP.equals(mUsbFunction) ||
                UsbManager.USB_FUNCTION_PTP.equals(mUsbFunction))) {//如果连usb,mtp
            mMountTogglePreference.setEnabled(false);//将sd卡那项置灰
            if (Environment.MEDIA_MOUNTED.equals(state)
                    || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
                mMountTogglePreference.setSummary(
                        mResources.getString(R.string.mtp_ptp_mode_summary));
            }

            if (mFormatPreference != null) {
                mFormatPreference.setEnabled(false);
                mFormatPreference.setSummary(mResources.getString(R.string.mtp_ptp_mode_summary));
            }
        } else if (mFormatPreference != null) {//最后格式化那项根据sd卡那项来置亮还是置灰。
            mFormatPreference.setEnabled(mMountTogglePreference.isEnabled());
            mFormatPreference.setSummary(mResources.getString(R.string.sd_format_summary));
        }
    }


好,看了主函数后,问题来了。

首先当我们点击卸载sd卡后:

    @Override
    public Dialog onCreateDialog(int id) {
        switch (id) {
        case DLG_CONFIRM_UNMOUNT:
                return new AlertDialog.Builder(getActivity())
                    .setTitle(R.string.dlg_confirm_unmount_title)
                    .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            doUnmount();//点击后调用doUnmount
                        }})
                    .setNegativeButton(R.string.cancel, null)
                    .setMessage(R.string.dlg_confirm_unmount_text)
                    .create();
        case DLG_ERROR_UNMOUNT:
                return new AlertDialog.Builder(getActivity())
            .setTitle(R.string.dlg_error_unmount_title)
            .setNeutralButton(R.string.dlg_ok, null)
            .setMessage(R.string.dlg_error_unmount_text)
            .create();
        }
        return null;
    }

    private void doUnmount() {
        // Present a toast here
        Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
        IMountService mountService = getMountService();
        try {
            sLastClickedMountToggle.setEnabled(false);//将卸载SD卡那项置灰
            sLastClickedMountToggle.setTitle(getString(R.string.sd_ejecting_title));//title改成"正在卸载"
            sLastClickedMountToggle.setSummary(getString(R.string.sd_ejecting_summary));
            mountService.unmountVolume(sClickedMountPoint, true, false);
        } catch (RemoteException e) {
            // Informative dialog to user that unmount failed.
            showDialogInner(DLG_ERROR_UNMOUNT);
        }
    }


我们再来看看vold对卸载这个命令怎么处理:

int Volume::unmountVol(bool force, bool revert) {
    int i, rc;

    int flags = getFlags();
    bool providesAsec = (flags & VOL_PROVIDES_ASEC) != 0;

    if (getState() != Volume::State_Mounted) {
        SLOGE("Volume %s unmount request when not mounted", getLabel());
        errno = EINVAL;
        return UNMOUNT_NOT_MOUNTED_ERR;
    }

    setState(Volume::State_Unmounting);//将状态置为unmounting,直接发送。setState中后面会直接发给MountService
    usleep(1000 * 1000); // Give the framework some time to react

    char service[64];
    snprintf(service, 64, "fuse_%s", getLabel());
    property_set("ctl.stop", service);
    /* Give it a chance to stop.  I wish we had a synchronous way to determine this... */
    sleep(1);

    // TODO: determine failure mode if FUSE times out

    if (providesAsec && doUnmount(Volume::SEC_ASECDIR_EXT, force) != 0) {
        SLOGE("Failed to unmount secure area on %s (%s)", getMountpoint(), strerror(errno));
        goto out_mounted;
    }

    /* Now that the fuse daemon is dead, unmount it */
    if (doUnmount(getFuseMountpoint(), force) != 0) {
        SLOGE("Failed to unmount %s (%s)", getFuseMountpoint(), strerror(errno));
        goto fail_remount_secure;
    }

    /* Unmount the real sd card */
    if (doUnmount(getMountpoint(), force) != 0) {
        SLOGE("Failed to unmount %s (%s)", getMountpoint(), strerror(errno));
        goto fail_remount_secure;
    }

    SLOGI("%s unmounted successfully", getMountpoint());

    /* If this is an encrypted volume, and we've been asked to undo
     * the crypto mapping, then revert the dm-crypt mapping, and revert
     * the device info to the original values.
     */
    if (revert && isDecrypted()) {
        cryptfs_revert_volume(getLabel());
        revertDeviceInfo();
        SLOGI("Encrypted volume %s reverted successfully", getMountpoint());
    }

    setUuid(NULL);
    setUserLabel(NULL);
    setState(Volume::State_Idle);
    mCurrentlyMountedKdev = -1;
    return 0;

fail_remount_secure:
    if (providesAsec && mountAsecExternal() != 0) {
        SLOGE("Failed to remount secure area (%s)", strerror(errno));
        goto out_nomedia;
    }

out_mounted:
    setState(Volume::State_Mounted);
    return -1;

out_nomedia:
    setState(Volume::State_NoMedia);
    return -1;
}

MountService收到vold的消息,先调用onEvent函数,而unmounting属于VolumeStateChange

    public boolean onEvent(int code, String raw, String[] cooked) {
        if (DEBUG_EVENTS) {
            StringBuilder builder = new StringBuilder();
            builder.append("onEvent::");
            builder.append(" raw= " + raw);
            if (cooked != null) {
                builder.append(" cooked = " );
                for (String str : cooked) {
                    builder.append(" " + str);
                }
            }
            Slog.i(TAG, builder.toString());
        }
        if (code == VoldResponseCode.VolumeStateChange) {
            /*
             * One of the volumes we're managing has changed state.
             * Format: "NNN Volume <label> <path> state changed
             * from <old_#> (<old_str>) to <new_#> (<new_str>)"
             */
            notifyVolumeStateChange(//调用notifyVolumeStateChange函数
                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),
                            Integer.parseInt(cooked[10]));
        }

再来看看notifyVolumeStateChange函数:

    private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
        final StorageVolume volume;
        final String state;
        synchronized (mVolumesLock) {
            volume = mVolumesByPath.get(path);
            state = getVolumeState(path);
        }

        if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChange::" + state);

        String action = null;

        if (oldState == VolumeState.Shared && newState != oldState) {
            if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
            sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);
        }

        if (newState == VolumeState.Init) {
        } else if (newState == VolumeState.NoMedia) {
            // NoMedia is handled via Disk Remove events
        } else if (newState == VolumeState.Idle) {
            /*
             * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
             * if we're in the process of enabling UMS
             */
            if (!state.equals(
                    Environment.MEDIA_BAD_REMOVAL) && !state.equals(
                            Environment.MEDIA_NOFS) && !state.equals(
                                    Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
                if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
                action = Intent.ACTION_MEDIA_UNMOUNTED;
            }
        } else if (newState == VolumeState.Pending) {
        } else if (newState == VolumeState.Checking) {
            if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
            updatePublicVolumeState(volume, Environment.MEDIA_CHECKING);
            action = Intent.ACTION_MEDIA_CHECKING;
        } else if (newState == VolumeState.Mounted) {
            if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
            updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
            action = Intent.ACTION_MEDIA_MOUNTED;
        } else if (newState == VolumeState.Unmounting) {//Unmounting的时候没有updatePublicVolumeState
            action = Intent.ACTION_MEDIA_EJECT;
        } else if (newState == VolumeState.Formatting) {
        } else if (newState == VolumeState.Shared) {
            if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
            /* Send the media unmounted event first */
            updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
            sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);

            if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
            updatePublicVolumeState(volume, Environment.MEDIA_SHARED);
            action = Intent.ACTION_MEDIA_SHARED;
            if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
        } else if (newState == VolumeState.SharedMnt) {
            Slog.e(TAG, "Live shared mounts not supported yet!");
            return;
        } else {
            Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
        }

        if (action != null) {
            sendStorageIntent(action, volume, UserHandle.ALL);//发送广播给应用,但是我们没有使用广播。而是注册的回调
        }
    }

我们再来看看updatePublicVolumeState函数:

    private void updatePublicVolumeState(StorageVolume volume, String state) {
        final String path = volume.getPath();
        final String oldState;
        synchronized (mVolumesLock) {
            oldState = mVolumeStates.put(path, state);
            volume.setState(state);
        }

        if (state.equals(oldState)) {
            Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
                    state, state, path));
            return;
        }

...........

        synchronized (mListeners) {
            for (int i = mListeners.size() -1; i >= 0; i--) {//遍历回调函数
                MountServiceBinderListener bl = mListeners.get(i);
                try {
                    bl.mListener.onStorageStateChanged(path, oldState, state);
                } catch (RemoteException rex) {
                    Slog.e(TAG, "Listener dead");
                    mListeners.remove(i);
                } catch (Exception ex) {
                    Slog.e(TAG, "Listener failed", ex);
                }
            }
        }
    }


现在的现象是,当用户点击卸载sd卡后,updatePreferencesFromState又被调用了,而且经过验证是setting自己调用的,而非MountService中的回调。

private void updatePreferencesFromState() {
        // Only update for physical volumes
        if (mVolume == null) return;

        mMountTogglePreference.setEnabled(true);

        final String state = mStorageManager.getVolumeState(mVolume.getPath());

        if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            mItemAvailable.setTitle(R.string.memory_available_read_only);
        } else {
            mItemAvailable.setTitle(R.string.memory_available);
        }

        if (Environment.MEDIA_MOUNTED.equals(state)
                || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
                mMountTogglePreference.setEnabled(true);//去查询sd卡的状态还是mounted,所以又置亮了。所以出现了卸载过程中,还能卸载的情况
                mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject));
                mMountTogglePreference.setSummary(mResources.getString(R.string.sd_eject_summary));
                
                addPreference(mUsageBarPreference);
                addPreference(mItemTotal);
                addPreference(mItemAvailable);
        } else {
            if (Environment.MEDIA_UNMOUNTED.equals(state) || Environment.MEDIA_NOFS.equals(state)
                    || Environment.MEDIA_UNMOUNTABLE.equals(state)) {
                mMountTogglePreference.setEnabled(true);
                mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
                mMountTogglePreference.setSummary(mResources.getString(R.string.sd_mount_summary));
            } else {
                mMountTogglePreference.setEnabled(false);
                mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
                mMountTogglePreference.setSummary(mResources.getString(R.string.sd_insert_summary));
            }

            removePreference(mUsageBarPreference);
            removePreference(mItemTotal);
            removePreference(mItemAvailable);
        }

        if (mUsbConnected && (UsbManager.USB_FUNCTION_MTP.equals(mUsbFunction) ||
                UsbManager.USB_FUNCTION_PTP.equals(mUsbFunction))) {
            //mMountTogglePreference.setEnabled(false);
            if (Environment.MEDIA_MOUNTED.equals(state)
                    || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
                mMountTogglePreference.setSummary(
                        mResources.getString(R.string.mtp_ptp_mode_summary));
            }

            if (mFormatPreference != null) {
                mFormatPreference.setEnabled(false);
                mFormatPreference.setSummary(mResources.getString(R.string.mtp_ptp_mode_summary));
            }
        } else if (mFormatPreference != null) {
            mFormatPreference.setEnabled(mMountTogglePreference.isEnabled());//卸载完后,格式化应该置灰。这边卸载完,变成"安装SD卡"的titiel并且也是亮的,所以格式化也是亮的
            mFormatPreference.setSummary(mResources.getString(R.string.sd_format_summary));
        }
    }


至于这样的两种情况,在MountService的notifyVolumeStateChange函数中增加一个Unmounting状态,在Environment.java中增加Environment.MEDIA_UNMOUNTING

         } else if (newState == VolumeState.Unmounting) {
             action = Intent.ACTION_MEDIA_EJECT;
            Log.d(TAG,"Unmounting");
            updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTING);
         }

然后在Setting增加如下,在最前面增加一个Memory.STATE_UNMOUNTING状态,如果是这个状态,将sd卡状态和格式化这两项都置灰。

    private void updatePreferencesFromState() {
        if (mVolume == null) {
            return;
        }

        mMountTogglePreference.setEnabled(true);

        final String state = mStorageManager.getVolumeState(mVolume.getPath());
        if (mVolume == null || (state != null && state.equals(Memory.STATE_UNMOUNTING))) {
            Log.i(TAG, "state return");
            mMountTogglePreference.setEnabled(false);
            mFormatPreference.setEnabled(false);
            mMountTogglePreference.setTitle(mResources.getString(R.string.sd_ejecting_title));
            mMountTogglePreference.setSummary(mResources.getString(R.string.sd_ejecting_summary));
           return;
        }

这样这个问题就解决了。





  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,下面是配置setting.json文件实现自动格式化的步骤: 1. 打开VS Code,按下 `Ctrl + Shift + P`,在搜索框输入“settings.json”,选择“Preferences: Open User Settings”选项,打开用户设置。 2. 在用户设置页面,点击右上角的“{}”按钮,打开settings.json文件。 3. 在settings.json文件,添加以下内容: ```json "editor.formatOnSave": true, "[javascript]": { "editor.formatOnSave": false }, "[json]": { "editor.formatOnSave": true }, "[typescript]": { "editor.formatOnSave": false }, ``` 其,`editor.formatOnSave`表示保存时是否自动格式化代码。在上面的配置,JavaScript文件保存时不进行自动格式化,而JSON文件保存时自动格式化。 4. 保存settings.json文件,配置就生效了。 这样,当你在VS Code编辑代码时,保存文件时会自动进行格式化,让你的代码更加整洁易读。 ### 回答2: VSCode是一款功能强大的代码编辑器,可以通过配置setting.json文件实现自动格式化。 首先,我们需要打开VSCode并在左侧的侧边栏选择“扩展”图标。接下来,点击搜索框并输入“Prettier - Code formatter”来安装Prettier插件。Prettier是一个代码格式化工具,可以帮助我们自动格式化代码。 安装完成后,点击左上角的文件菜单,选择“首选项”并选择“设置”。这将打开setting.json文件,其包含了VSCode的所有配置选项。 在setting.json文件,我们需要配置以下几个参数来实现自动格式化: 1. `"editor.formatOnSave": true`:这个参数表示在保存文件时自动格式化代码。 2. `"editor.defaultFormatter": "esbenp.prettier-vscode"`:这个参数指定了默认的代码格式化工具为Prettier。 配置完成后,保存setting.json文件。现在,只要我们编辑保存文件时,VSCode将会自动调用Prettier插件来格式化代码。 此外,我们还可以根据自己的需求进行更多的配置。例如,可以配置Prettier的格式化规则,如缩进大小、换行符等。可以在setting.json文件添加以下参数: ``` "prettier.tabWidth": 2, "prettier.singleQuote": true ``` 这两个参数分别设置缩进大小为2和使用单引号。根据自己的需要进行配置即可。 总之,通过配置setting.json文件的相关选项,我们可以实现VSCode的自动格式化功能,使代码看起来更加整洁和易读。 ### 回答3: 要实现在VSCode自动格式化代码,需要配置setting.json文件。首先,打开VSCode,点击左下角的设置图标,选择“首选项”>“设置”。在用户设置或工作区设置,找到“Editor: Default Formatter”选项,并选择适合的代码格式化程序,如Prettier。 接下来,在setting.json文件添加以下代码实现自动格式化: { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode" } 其,"editor.formatOnSave": true表示在保存文件时自动格式化代码,"editor.defaultFormatter": "esbenp.prettier-vscode"表示使用Prettier作为默认的代码格式化程序。 保存setting.json文件后,即可在保存文件时自动进行代码格式化。这样可以节省时间和提高代码的可读性。 需要注意的是,如果VSCode还没有安装Prettier插件,需要先安装该插件,方法是点击左侧的扩展图标,搜索Prettier,然后选择并安装。这样才能正确使用Prettier进行代码格式化。 通过以上步骤配置setting.json文件,就可以在VSCode实现自动格式化代码的功能了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值