天天看點

Android9.0 Camera App代碼跟蹤

    各位早上好,這次給大家帶來的是Android9.0 Camera App的代碼閱讀,即是工作需要也是個人的筆記,幫助大家學習。

Android9.0 Camera App源代碼的位置(/android/packages/apps/SnapdragonCamera),其他的Camera App在Android9.0中已經棄用了,我把源碼導入到Android studio中讓大家看看它的代碼結構,如圖:

Android9.0 Camera App代碼跟蹤
Android9.0 Camera App代碼跟蹤

這裡着重要講的是src裡面的Java代碼,導入到Android studio會有很多錯誤,部落客一開始天真地以為可以在Android studio裡面編譯,但發現錯誤太多不現實,還是在源碼裡面編譯吧,android studio就作為代碼閱讀工具用,大家也可以用source insight閱讀,至于部落客,兩個都用。

好了,廢話不多說我們開始閱讀代碼吧。

第一個問題:Carmera App是怎麼啟動的?

有Android App開發基礎的同學就知道,Android App啟動的時候第一個啟動的就是Application.那麼這個類在哪裡,我們可以找到AndroidManifest.xml檔案來看

Android9.0 Camera App代碼跟蹤

看在這裡,我們找到com.android.camera.app.CameraApp先

Android9.0 Camera App代碼跟蹤

look!看到了吧。好現在來看看它做了哪些工作,一般來講這個類肯定是做一些初始化的工作。我們看他的源碼:

@Override
    public void onCreate() {
        super.onCreate();
        ActivityManager actManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
        actManager.getMemoryInfo(memInfo);
        mMaxSystemMemory = memInfo.totalMem;
        if(mMaxSystemMemory <= LOW_MEMORY_DEVICE_THRESHOLD) {//檢查裝置的存儲空間是否充足
            mIsLowMemoryDevice = true;
        }
        SettingsManager.createInstance(this);//執行個體化一個Camera設定管理類
        UsageStatistics.initialize(this);
        CameraUtil.initialize(this);//Camrera工具類初始化
        SDCard.initialize(this);//SD卡初始化
    }
           

這裡面Camre是存儲空間需求比較大的應用,是以有個最低記憶體要求。

接下來要看看App啟動的第一個界面,我們再看一下AndroidManifest.xml裡面的代碼:

<activity
            android:name="com.android.camera.CameraActivity"
            android:clearTaskOnLaunch="true"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:icon="@mipmap/ic_launcher_camera"
            android:label="@string/snapcam_app_name"
            android:launchMode="singleTask"
            android:logo="@mipmap/ic_launcher_gallery"
            android:screenOrientation="portrait"
            android:taskAffinity="com.android.camera.CameraActivity"
            android:theme="@style/Theme.Camera"
            android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
            android:resizeableActivity="true"
            android:visibleToInstantApps="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />//這個Activity作為主界面啟動
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>

            <meta-data
                android:name="com.android.keyguard.layout"
                android:resource="@layout/keyguard_widget" />
            <meta-data
                android:name="android.max_aspect"
                android:value="2.1" />
        </activity>
           

我們找到com.android.camera.CameraActivity類,這個類代碼比較多我就隻摘取其中的片段來講。

首先我們找到Actively的生命周期方法onCreate()這是Activity啟動是首先要執行的方法,這裡面做了什麼工作呢?我們來看下源碼:

@Override
    public void onCreate(Bundle state) {
        super.onCreate(state);
        // Check if this is in the secure camera mode.
        Intent intent = getIntent();
        String action = intent.getAction();//Activity啟動會攜帶相應的action,根據action可以判斷Activity是從哪裡啟動的
        if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
                || ACTION_IMAGE_CAPTURE_SECURE.equals(action)
                || intent.getComponent().getClassName().equals(GESTURE_CAMERA_NAME)) {
            mSecureCamera = true;
        } else {
            mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
        }

        if (mSecureCamera) {//什麼是secure camera mode?就是你在鎖屏狀态下進入的Camera,這種狀态是的Camera功能是有限制的
            // Change the window flags so that secure camera can show when locked
            Window win = getWindow();
            WindowManager.LayoutParams params = win.getAttributes();
            params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
            if (intent.getComponent().getClassName().equals(GESTURE_CAMERA_NAME)) {
                params.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
                PowerManager pm = ((PowerManager) getSystemService(POWER_SERVICE));
                mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
                mWakeLock.acquire();
                Log.d(TAG, "acquire wake lock");
            }
            win.setAttributes(params);
        }
//這裡檢查App是否有相關的權限
        if (mSecureCamera && !hasCriticalPermissions()) {
            return;
        }

        if (isStartRequsetPermission()) {
            Log.v(TAG, "onCreate: Missing critical permissions.");
            finish();
            return;
        }

        boolean camera_api_1_support = true;
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
//檢查裝置的api是否支援
        if (!sharedPreferences.contains(CAMERA_API_1_SUPPORT)) {
            camera_api_1_support = cameraAPICheck();
        } else {
            camera_api_1_support = sharedPreferences.getBoolean(CAMERA_API_1_SUPPORT,true);
        }

        mCursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                null, null, null, null);
        GcamHelper.init(getContentResolver());

        getWindow().requestFeature(Window.FEATURE_ACTION_BAR);

        LayoutInflater inflater = getLayoutInflater();
//擷取View
        View rootLayout = inflater.inflate(R.layout.camera, null, false);
        mCameraRootFrame = (FrameLayout)rootLayout.findViewById(R.id.camera_root_frame);
        mCameraPhotoModuleRootView = rootLayout.findViewById(R.id.camera_photo_root);
        mCameraVideoModuleRootView = rootLayout.findViewById(R.id.camera_video_root);
        mCameraPanoModuleRootView = rootLayout.findViewById(R.id.camera_pano_root);
        mCameraCaptureModuleRootView = rootLayout.findViewById(R.id.camera_capture_root);
//界面根據moduleIndex來決定加載那個module
        int moduleIndex = -1;
        if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
                || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
            moduleIndex = ModuleSwitcher.VIDEO_MODULE_INDEX;
        } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
                || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
                .getAction())) {
            moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
            if (prefs.getInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, -1)
                    == ModuleSwitcher.GCAM_MODULE_INDEX && GcamHelper.hasGcamCapture()) {
                moduleIndex = ModuleSwitcher.GCAM_MODULE_INDEX;
            }
        } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
            moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
        } else {
            // If the activity has not been started using an explicit intent,
            // read the module index from the last time the user changed modes
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
            moduleIndex = prefs.getInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, -1);
            if ((moduleIndex == ModuleSwitcher.GCAM_MODULE_INDEX &&
                    !GcamHelper.hasGcamCapture()) || moduleIndex < 0) {
                moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
            }
        }
        boolean cam2on = PersistUtil.getCamera2Mode();
        if (!cam2on && !camera_api_1_support)
            cam2on = true;
        CameraHolder.setCamera2Mode(this, cam2on);
        if (cam2on && (moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX ||
                moduleIndex == ModuleSwitcher.VIDEO_MODULE_INDEX))
            moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX;

        mOrientationListener = new MyOrientationEventListener(this);
        setContentView(R.layout.camera_filmstrip);
        mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view);
        setModuleFromIndex(moduleIndex);//根據moduleIndex切換到相應的子產品

        mActionBar = getActionBar();
        mActionBar.addOnMenuVisibilityListener(this);

        if (ApiHelper.HAS_ROTATION_ANIMATION) {
            setRotationAnimation();
        }

        mMainHandler = new MainHandler(getMainLooper());

        mAboveFilmstripControlLayout =
                (FrameLayout) findViewById(R.id.camera_above_filmstrip_layout);
        mAboveFilmstripControlLayout.setFitsSystemWindows(true);
        mPanoramaManager = AppManagerFactory.getInstance(this)
                .getPanoramaStitchingManager();
        mPlaceholderManager = AppManagerFactory.getInstance(this)
                .getGcamProcessingManager();
        mPanoramaManager.addTaskListener(mStitchingListener);
        mPlaceholderManager.addTaskListener(mPlaceholderListener);
        mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel);
        mBottomProgress = (ProgressBar) findViewById(R.id.pano_stitching_progress_bar);
        mCameraPreviewData = new CameraPreviewData(rootLayout,
                FilmStripView.ImageData.SIZE_FULL,
                FilmStripView.ImageData.SIZE_FULL);
        // Put a CameraPreviewData at the first position.
        mWrappedDataAdapter = new FixedFirstDataAdapter(
                new CameraDataAdapter(new ColorDrawable(
                        getResources().getColor(R.color.photo_placeholder))),
                mCameraPreviewData);

        mFilmStripView.setViewGap(
                getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
        mPanoramaViewHelper = new PanoramaViewHelper(this);
        mPanoramaViewHelper.onCreate();
        mFilmStripView.setPanoramaViewHelper(mPanoramaViewHelper);
        // Set up the camera preview first so the preview shows up ASAP.
        mFilmStripView.setListener(mFilmStripListener);

        if (!mSecureCamera) {
            mDataAdapter = mWrappedDataAdapter;
            mFilmStripView.setDataAdapter(mDataAdapter);
            if (!isCaptureIntent()) {
                mDataAdapter.requestLoad(getContentResolver());
                mDataRequested = true;
            }
        } else {
            // Put a lock placeholder as the last image by setting its date to
            // 0.
            ImageView v = (ImageView) getLayoutInflater().inflate(
                    R.layout.secure_album_placeholder, null);
            v.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    try {
                        UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
                                UsageStatistics.ACTION_GALLERY, null);
                        startActivity(IntentHelper.getGalleryIntent(CameraActivity.this));
                    } catch (ActivityNotFoundException e) {
                        Log.w(TAG, "Failed to launch gallery activity, closing");
                    }
                    finish();
                }
            });
            mDataAdapter = new FixedLastDataAdapter(
                    mWrappedDataAdapter,
                    new SimpleViewData(
                            v,
                            v.getDrawable().getIntrinsicWidth(),
                            v.getDrawable().getIntrinsicHeight(),
                            0, 0));
            // Flush out all the original data.
            mDataAdapter.flush();
            mFilmStripView.setDataAdapter(mDataAdapter);
        }

        setupNfcBeamPush();

        mLocalImagesObserver = new LocalMediaObserver();
        mLocalVideosObserver = new LocalMediaObserver();

        getContentResolver().registerContentObserver(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true,
                mLocalImagesObserver);
        getContentResolver().registerContentObserver(
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true,
                mLocalVideosObserver);

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        mDeveloperMenuEnabled = prefs.getBoolean(CameraSettings.KEY_DEVELOPER_MENU, false);

        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        int width = size.x;
        int height = size.y;

        int lower = Math.min(width, height);

        int offset = lower * 7 / 100;
        SETTING_LIST_WIDTH_1 = lower / 2 + offset;
        SETTING_LIST_WIDTH_2 = lower / 2 - offset;
        registerSDcardMountedReceiver();

        mAutoTestEnabled = PersistUtil.isAutoTestEnabled();

        if (mAutoTestEnabled) {
            registerAutoTestReceiver();
        }
    }
           

我們重點看下setModuleFromIndex(int moduleIndex) 方法,這個方法會根據moduleIndex切換到相應的子產品界面去

/**
     * Sets the mCurrentModuleIndex, creates a new module instance for the given
     * index an sets it as mCurrentModule.
     */
    private void setModuleFromIndex(int moduleIndex) {
        mCameraPhotoModuleRootView.setVisibility(View.GONE);
        mCameraVideoModuleRootView.setVisibility(View.GONE);
        mCameraPanoModuleRootView.setVisibility(View.GONE);
        mCameraCaptureModuleRootView.setVisibility(View.GONE);
        mCurrentModuleIndex = moduleIndex;
        switch (moduleIndex) {
            case ModuleSwitcher.VIDEO_MODULE_INDEX:
                if(mVideoModule == null) {
                    mVideoModule = new VideoModule();//視訊子產品
                    mVideoModule.init(this, mCameraVideoModuleRootView);
                } else {
                    mVideoModule.reinit();
                }
                mCurrentModule = mVideoModule;
                mCameraVideoModuleRootView.setVisibility(View.VISIBLE);
                break;

            case ModuleSwitcher.PHOTO_MODULE_INDEX:
                if(mPhotoModule == null) {
                    mPhotoModule = new PhotoModule();//拍照子產品
                    mPhotoModule.init(this, mCameraPhotoModuleRootView);
                } else {
                    mPhotoModule.reinit();
                }
                mCurrentModule = mPhotoModule;
                mCameraPhotoModuleRootView.setVisibility(View.VISIBLE);
                break;

            case ModuleSwitcher.WIDE_ANGLE_PANO_MODULE_INDEX:
                if(mPanoModule == null) {
                    mPanoModule = new WideAnglePanoramaModule();//全景廣角子產品
                    mPanoModule.init(this, mCameraPanoModuleRootView);
                }
                mCurrentModule = mPanoModule;
                mCameraPanoModuleRootView.setVisibility(View.VISIBLE);
                break;

            case ModuleSwitcher.CAPTURE_MODULE_INDEX:
                if(mCaptureModule == null) {
                    mCaptureModule = new CaptureModule();//快照
                    mCaptureModule.init(this, mCameraCaptureModuleRootView);
                } else {
                    mCaptureModule.reinit();
                }
                mCurrentModule = mCaptureModule;
                mCameraCaptureModuleRootView.setVisibility(View.VISIBLE);
                break;

            case ModuleSwitcher.PANOCAPTURE_MODULE_INDEX:
                final Activity activity = this;
                if(!PanoCaptureProcessView.isSupportedStatic()) {
                    this.runOnUiThread(new Runnable() {
                        public void run() {
                            RotateTextToast.makeText(activity, "Panocapture library is missing", Toast.LENGTH_SHORT).show();
                        }
                    });
                    mCurrentModuleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
                    //Let it fall through to photo module
                } else {
                    if (mPano2Module == null) {
                        mPano2Module = new PanoCaptureModule();//全景快照
                        mPano2Module.init(this, mCameraPanoModuleRootView);
                    }
                    mCurrentModule = mPano2Module;
                    mCameraPanoModuleRootView.setVisibility(View.VISIBLE);
                    break;
                }
            case ModuleSwitcher.LIGHTCYCLE_MODULE_INDEX: //Unused module for now
            case ModuleSwitcher.GCAM_MODULE_INDEX:  //Unused module for now
            default:
                // Fall back to photo mode.
                if(mPhotoModule == null) {
                    mPhotoModule = new PhotoModule();//預設使用拍照子產品
                    mPhotoModule.init(this, mCameraPhotoModuleRootView);
                } else {
                    mPhotoModule.reinit();
                }
                mCurrentModule = mPhotoModule;
                mCameraPhotoModuleRootView.setVisibility(View.VISIBLE);
                break;
        }
    }
           

可以看到分别有,視訊,拍照,全景等子產品,這裡面預設使用拍照子產品(PhotoModule),我們來看下PhotoModule是什麼玩意:

public class PhotoModule
        implements CameraModule,
        PhotoController,
        FocusOverlayManager.Listener,
        CameraPreference.OnPreferenceChangedListener,
        ShutterButton.OnShutterButtonListener,
        MediaSaveService.Listener,
        OnCountDownFinishedListener,
        LocationManager.Listener,
        SensorEventListener, MakeupLevelListener {
           
public interface CameraModule {

    public void init(CameraActivity activity, View frame);

    public void onPreviewFocusChanged(boolean previewFocused);

    public void onPauseBeforeSuper();

    public void onPauseAfterSuper();

    public void onResumeBeforeSuper();

    public void onResumeAfterSuper();

    public void onConfigurationChanged(Configuration config);

    public void onStop();

    public void onDestroy();

    public void installIntentFilter();

    public void onActivityResult(int requestCode, int resultCode, Intent data);

    public boolean onBackPressed();

    public boolean onKeyDown(int keyCode, KeyEvent event);

    public boolean onKeyUp(int keyCode, KeyEvent event);

    public void onSingleTapUp(View view, int x, int y);

    public void onPreviewTextureCopied();

    public void onCaptureTextureCopied();

    public void onUserInteraction();

    public boolean updateStorageHintOnResume();

    public void onOrientationChanged(int orientation);

    public void onShowSwitcherPopup();

    public void onMediaSaveServiceConnected(MediaSaveService s);

    public boolean arePreviewControlsVisible();

    public void resizeForPreviewAspectRatio();

    public void onSwitchSavePath();

    public void waitingLocationPermissionResult(boolean waiting);

    public void enableRecordingLocation(boolean enable);

    public void setPreferenceForTest(String key, String value);
}
           

它實作了CameraModule 接口,這個接口定義了Camera從建立到釋放的過程,我們來看下init()方法:

@Override
    public void init(CameraActivity activity, View parent) {
        ...
        if (mOpenCameraThread == null) {
            mOpenCameraThread = new OpenCameraThread();//啟動一個線程
            mOpenCameraThread.start();
        }
        ...
    }

 private class OpenCameraThread extends Thread {
        @Override
        public void run() {
            openCamera();//打開Camera
            startPreview();//開啟預覽
        }
    }

private void openCamera() {
        ...
//建立一個Camera執行個體
        mCameraDevice = CameraUtil.openCamera(
                mActivity, mCameraId, mHandler,
                mActivity.getCameraOpenErrorCallback());
        ...
    }

           

代碼到了CameraUtil類裡面,我們看下它搞啥玩意:

public static CameraManager.CameraProxy openCamera(
            Activity activity, final int cameraId,
            Handler handler, final CameraManager.CameraOpenErrorCallback cb) {
        try {
            throwIfCameraDisabled(activity);
            return CameraHolder.instance().open(handler, cameraId, cb);
        } catch (CameraDisabledException ex) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    cb.onCameraDisabled(cameraId);
                }
            });
        }
        return null;
    }

//代碼到CameraHolder類的open方法
 public synchronized CameraProxy open(
            Handler handler, int cameraId,
            CameraManager.CameraOpenErrorCallback cb) {
        ...
                mCameraDevice = CameraManagerFactory
                        .getAndroidCameraManager().cameraOpen(handler, cameraId, cb);
            ...
        return mCameraDevice;
    }
//CameraManagerFactory類
public class CameraManagerFactory {

    private static AndroidCameraManagerImpl sAndroidCameraManager;

    /**
     * Returns the android camera implementation of {@link CameraManager}.
     *
     * @return The {@link CameraManager} to control the camera device.
     */
    public static synchronized CameraManager getAndroidCameraManager() {
        if (sAndroidCameraManager == null) {
            sAndroidCameraManager = new AndroidCameraManagerImpl();
        }
        return sAndroidCameraManager;
    }
}
//AndroidCameraManagerImpl 類的cameraOpen
@Override
    public CameraManager.CameraProxy cameraOpen(
        Handler handler, int cameraId, CameraOpenErrorCallback callback) {
        mCameraHandler.errorCbInstance = CameraOpenErrorCallbackForward
                .getNewInstance(handler, callback);
//在這裡面使用mCameraHandler發送OPEN_CAMERA消息執行個體化一個Camera對象
        mCameraHandler.obtainMessage(OPEN_CAMERA, cameraId, 0, mCameraHandler.errorCbInstance)
                .sendToTarget();
        mCameraHandler.waitDone();
        if (mCamera != null) {
//将AndroidCameraProxyImpl代理類對象傳回給調用者,調用者對象将通過它來操作Camera執行個體
            return new AndroidCameraProxyImpl();
        } else {
            return null;
        }
    }
//CameraHandler類,它是AndroidCameraManagerImpl 的内部類

@Override
        public void handleMessage(final Message msg) {
            try {
                switch (msg.what) {
                    case OPEN_CAMERA:
                        try {
//這裡通過Java的反射機制來執行個體化Camrea
                            Method openMethod = Class.forName("android.hardware.Camera").getMethod(
                                    "openLegacy", int.class, int.class);
                            mCamera = (android.hardware.Camera) openMethod.invoke(
                                    null, msg.arg1, CAMERA_HAL_API_VERSION_1_0);
                        } catch (Exception e) {
                            /* Retry with open if openLegacy doesn't exist/fails */
                            Log.v(TAG, "openLegacy failed due to " + e.getMessage()
                                    + ", using open instead");
                            mCamera = android.hardware.Camera.open(msg.arg1);
                        }

                        if (mCamera != null) {
                            mParametersIsDirty = true;

                            // Get a instance of Camera.Parameters for later use.
                            if (mParamsToSet == null) {
                                mParamsToSet = mCamera.getParameters();
                            }
                        } else {
                            if (msg.obj != null) {
                                ((CameraOpenErrorCallback) msg.obj).onDeviceOpenFailure(msg.arg1);
                            }
                        }
                        return;

                    case RELEASE:
                        if (mCamera == null) {
                            return;
                        }
                        mCamera.release();
                        errorCbInstance = null;
                        mCamera = null;
                        return;

                    case RECONNECT:
                        mReconnectIOException = null;
                        try {
                            mCamera.reconnect();
                        } catch (IOException ex) {
                            mReconnectIOException = ex;
                        }
                        return;

                    ....
                }
           

代碼跟蹤到這裡就要進入系統層的代碼了,android.hardware.Camera的類在源碼目錄的位置/android/frameworks/base/core/java/android/hardware/Camera.java。

好了,文章到這裡就先告一段落了,我們來終結一下:

App啟動後代碼的大緻流程CameraApp-->CameraActivity.onCreate()-->CameraActivity.setModuleFromIndex()-->PhotoModule.init()-->啟動OpenCameraThread線程-->打開相機openCamera()-->在AndroidCameraManagerImpl的CameraHandler裡面通過反射機制執行個體化android.hardware.Camera

限于篇幅原因,我會在下一個部落格講解相機預覽。

謝謝大家,希望這篇部落格呢幫助到您,祝大家生活愉快,工作順心。

繼續閱讀