网上找的源码是Android 8.0(android-28)SnapdragonCamera App源码,先来看AndroidManifest.xml:

        <activityandroid: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:visibleToInstantApps="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.DEFAULT" /></intent-filter><meta-dataandroid:name="com.android.keyguard.layout"android:resource="@layout/keyguard_widget" /></activity>...<activity-aliasandroid:name="com.android.camera.CameraLauncher"android:icon="@mipmap/ic_launcher_camera"android:label="@string/snapcam_app_name"android:launchMode="singleTop"android:targetActivity="com.android.camera.CameraActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity-alias>

可以看到作为SnapdragonCamera应用显示在Launcher上的入口Activity为CameraLauncher,但实际上却是CameraActivity.java,也就是说这个应用的主要工作都是在CameraActivity中完成。

CameraActivity

从CameraActivity生命周期的的onCreate()方法开始:

onCreate()

        // Check if this is in the secure camera mode.Intent intent = getIntent();String action = intent.getAction();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) {// Change the window flags so that secure camera can show when lockedWindow 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);}if (mSecureCamera && !hasCriticalPermissions()) {return;}if (isStartRequsetPermission()) {Log.v(TAG, "onCreate: Missing critical permissions.");finish();return;}

1、判断是否是安全相机,赋值mSecureCamera,当处于安全相机模式下,设置窗口属性;
2、判断相关权限是否已申请,相关权限包括Camera、录音、读写存储权限等:

    /*** Checks if any of the needed Android runtime permissions are missing.* If they are, then launch the permissions activity under one of the following conditions:* a) If critical permissions are missing, display permission request again* b) If non-critical permissions are missing, just display permission request once.* Critical permissions are: camera, microphone and storage. The app cannot run without them.* Non-critical permission is location.*/private boolean hasCriticalPermissions() {boolean hasCriticalPermission = false;if (checkSelfPermission(Manifest.permission.CAMERA) ==PackageManager.PERMISSION_GRANTED &&checkSelfPermission(Manifest.permission.RECORD_AUDIO) ==PackageManager.PERMISSION_GRANTED &&checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) ==PackageManager.PERMISSION_GRANTED &&checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) ==PackageManager.PERMISSION_GRANTED) {hasCriticalPermission = true;} else {hasCriticalPermission = false;}return hasCriticalPermission;}

3、如果权限未申请,先进入PermissionsActivity申请权限,相关权限包括Camera、录音、读写存储、定位权限等:

    private boolean isStartRequsetPermission() {boolean isStartPermissionActivity = false;final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);boolean isRequestShown = prefs.getBoolean(CameraSettings.KEY_REQUEST_PERMISSION, false);if(!mSecureCamera && (!isRequestShown || !hasCriticalPermissions())) {Log.v(TAG, "Start Request Permission");Intent intent = new Intent(this, PermissionsActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);startActivity(intent);SharedPreferences.Editor editor = prefs.edit();editor.putBoolean(CameraSettings.KEY_REQUEST_PERMISSION, true);editor.apply();isStartPermissionActivity = true;}return isStartPermissionActivity;}

当权限申请成功后,再次运行CameraActivity:

    private void handlePermissionsSuccess() {if (mIntent != null) {setRequestPermissionShow();mIsReturnResult = true;mIntent.setClass(this, CameraActivity.class);mIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);startActivity(mIntent);finish();} else {mIsReturnResult = false;Intent intent = new Intent(this, CameraActivity.class);startActivity(intent);finish();}}

继续看onCreate()方法:

        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 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);mMultiCameraModuleRootView = rootLayout.findViewById(R.id.multi_camera_root);

1、查询系统MediaStore数据库,初始化mCursor;
2、加载布局R.layout.camera,初始化相关控件;

根据Intent的启动信息及其前一次退出应用前的所处模式判断当前启动的相机模式,初始化moduleIndex:

        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 modesSharedPreferences 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();CameraHolder.setCamera2Mode(this, cam2on);if (cam2on && (moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX ||moduleIndex == ModuleSwitcher.VIDEO_MODULE_INDEX))moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX;

从后面几句可以看到,如果当前使用CameraApi2且当前出于拍照模式时,则moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX,即使用CaptureModule.java。

初始化mOrientationListener,接收从SensorManager传输过来的屏幕方向切换的数据:

mOrientationListener = new MyOrientationEventListener(this);

设置CameraActivity界面布局R.layout.camera_filmstrip:

        setContentView(R.layout.camera_filmstrip);mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view);

根据刚刚初始化的moduleIndex初始化mCurrentModuleIndex,并创建一个新的Module模式赋值mCurrentModule。这个所谓的Module模式,即我们常说的拍照、录像等模式:

setModuleFromIndex(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);mMultiCameraModuleRootView.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.MULTIE_CAMERA_MODULE_INDEX:if(mMultiCameraModule == null) {mMultiCameraModule = new MultiCameraModule();mMultiCameraModule.init(this, mMultiCameraModuleRootView);} else {mMultiCameraModule.reinit();}mCurrentModule = mMultiCameraModule;mMultiCameraModuleRootView.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 nowcase ModuleSwitcher.GCAM_MODULE_INDEX:  //Unused module for nowdefault:// 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;}}

构建Module对象,并执行相关init()方法;
正常情况下一般初次进入应用时,都是默认出于拍照模式,且由于使用的是CameraAPI2,即初始化CaptureModule,直接来看CaptureModule.java的init()方法:

    @Overridepublic void init(CameraActivity activity, View parent) {mActivity = activity;mRootView = parent;mSettingsManager = SettingsManager.getInstance();mSettingsManager.createCaptureModule(this);mSettingsManager.registerListener(this);if (isBackCameraId()) {CURRENT_ID = BACK_MODE;} else {CURRENT_ID = FRONT_MODE;}mSettingsManager.init();mFirstPreviewLoaded = false;Log.d(TAG, "init");for (int i = 0; i < MAX_NUM_CAM; i++) {mCameraOpened[i] = false;mTakingPicture[i] = false;}for (int i = 0; i < MAX_NUM_CAM; i++) {mState[i] = STATE_PREVIEW;}SceneModule module;for (int i = 0; i < mSelectableModes.length; i++) {module = new SceneModule();module.mode = CameraMode.values()[i];mSceneCameraIds.add(module);}mPostProcessor = new PostProcessor(mActivity, this);mFrameProcessor = new FrameProcessor(mActivity, this);mContentResolver = mActivity.getContentResolver();initModeByIntent();initCameraIds();mUI = new CaptureUI(activity, this, parent);mUI.initializeControlByIntent();mFocusStateListener = new FocusStateListener(mUI);mLocationManager = new LocationManager(mActivity, this);}

执行一些初始化操作,初始化mSettingsManager、判断前后摄、初始化各摄像头状态、根据Intent信息初始化模式设置、初始化CameraID相关信息、构建UI处理对象CaptureUI、初始化mFocusStateListener、mLocationManager;

继续回到onCreate()方法:
初始化mStorage,主要处理图片数据保存:

mStorage = new Storage(this);

继续进行初始化、加载布局等操作,这里不一一赘述;

注册图片、视频数据观察者:

        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);

根据屏幕尺寸计算设置列表的显示宽:

        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;

注册SD card广播事件接收者,监听SD card状态:

registerSDcardMountedReceiver();
    // update the status of storage space when SD card status changed.private BroadcastReceiver mSDcardMountedReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {Log.d(TAG, "SDcard status changed, update storage space");updateStorageSpaceAndHint();}};private void registerSDcardMountedReceiver() {// filter for SDcard statusIntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);filter.addAction(Intent.ACTION_MEDIA_SHARED);filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);filter.addDataScheme("file");registerReceiver(mSDcardMountedReceiver, filter);}

onStart()

这部分比较简单,主要就是绑定启动MediaSaveService:

    @Overridepublic void onStart() {super.onStart();if (mSecureCamera && !hasCriticalPermissions()) {return;}bindMediaSaveService();mPanoramaViewHelper.onStart();}
    private void bindMediaSaveService() {Intent intent = new Intent(this, MediaSaveService.class);bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}

初始化mMediaSaveService,并将其赋值给mCurrentModule:

    private MediaSaveService mMediaSaveService;private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName className, IBinder b) {mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService();mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);}@Overridepublic void onServiceDisconnected(ComponentName className) {if (mMediaSaveService != null) {mMediaSaveService.setListener(null);mMediaSaveService = null;}}};

onResume()

判断是否申请权限,是否存在可用Camera devices:

        if (mSecureCamera && !hasCriticalPermissions()) {super.onResume();showOpenCameraErrorDialog();return;}if (isStartRequsetPermission()) {super.onResume();Log.v(TAG, "onResume: Missing critical permissions.");finish();return;}if (!cameraConnected()) {super.onResume();Log.v(TAG, "onResume: No camera devices connected.");finish();}

实例化mSettingsManager:

        mSettingsManager = SettingsManager.getInstance();if (mSettingsManager == null) {mSettingsManager = SettingsManager.createInstance(this);}
        private SettingsManager(Context context) {mListeners = new ArrayList<>();mCharacteristics = new ArrayList<>();mPrepNameKeys = new ArrayList<>();mContext = context;mPreferences = ComboPreferences.get(mContext);if (mPreferences == null) {mPreferences = new ComboPreferences(mContext);}upgradeGlobalPreferences(mPreferences.getGlobal(), mContext);CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);try {String[] cameraIdList = manager.getCameraIdList();boolean isFirstBackCameraId = true;boolean isRearCameraPresent = false;for (int i = 0; i < cameraIdList.length; i++) {String cameraId = cameraIdList[i];CameraCharacteristics characteristics= manager.getCameraCharacteristics(cameraId);Log.d(TAG,"cameraIdList size ="+cameraIdList.length);byte monoOnly = 0;try {monoOnly = characteristics.get(CaptureModule.MetaDataMonoOnlyKey);}catch(Exception e) {}if (monoOnly == 1) {CaptureModule.MONO_ID = i;mIsMonoCameraPresent = true;}int facing = characteristics.get(CameraCharacteristics.LENS_FACING);if (facing == CameraCharacteristics.LENS_FACING_FRONT) {CaptureModule.FRONT_ID = i;mIsFrontCameraPresent = true;}if (facing == CameraCharacteristics.LENS_FACING_BACK) {isRearCameraPresent = true;if (isFirstBackCameraId) {isFirstBackCameraId = false;mHasMultiCamera = true;upgradeCameraId(mPreferences.getGlobal(), i);}}mCharacteristics.add(i, characteristics);}if (isRearCameraPresent) {initPrepNameKeys(CameraCharacteristics.LENS_FACING_BACK);}if (mIsFrontCameraPresent) {initPrepNameKeys(CameraCharacteristics.LENS_FACING_FRONT);}} catch (CameraAccessException e) {e.printStackTrace();}mDependency = parseJson("dependency.json");mHeifWriterSupported = isHeifWriterSupported();}public void reloadCharacteristics(int cameraId){CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);try {CameraCharacteristics characteristics= manager.getCameraCharacteristics(String.valueOf(cameraId));mCharacteristics.set(cameraId, characteristics);} catch (CameraAccessException e) {e.printStackTrace();}}public static SettingsManager createInstance(Context context) {if (sInstance == null) {sInstance = new SettingsManager(context.getApplicationContext());}return sInstance;}public static SettingsManager getInstance() {return sInstance;}

初始化、读取所有可用Camera Devices设备信息;

继续回到onResume():
开始监听屏幕方向事件:

mOrientationListener.enable();

调用mCurrentModule的onResumeBeforeSuper()、onResumeAfterSuper()方法,从上面可知初次进入应用时mCurrentModule实为CaptureModule:

        mCurrentModule.onResumeBeforeSuper();super.onResume();mPaused = false;mCurrentModule.onResumeAfterSuper();

主要设置界面显示、创建Session等,不做具体赘述,后续再具体分析;
可以看到在CameraActivity的onResume()方法中,最关键的就是mCurrentModule.onResumeBeforeSuper()和mCurrentModule.onResumeAfterSuper(),这两句主要就是用来通知各模式开始处理Camera相关业务逻辑的,如开始预览等。

onPause()

停止监听屏幕方向事件:

mOrientationListener.disable();

在生命周期的这个方法内,最主要的其实也跟onResume()方法内对应,也是这两句:

        mCurrentModule.onPauseBeforeSuper();super.onPause();mCurrentModule.onPauseAfterSuper();

主要也是用来通知各模式停止处理Camera相关业务逻辑,如停止预览、关闭Camera等操作;
后续分析Camera流程时再详细描述。

onStop()

    @Overrideprotected void onStop() {super.onStop();if (mSecureCamera && !hasCriticalPermissions()) {return;}mPanoramaViewHelper.onStop();unbindMediaSaveService();}

对应onStart(),解绑MediaSaveService服务;

onDestory()

    @Overridepublic void onDestroy() {if (mWakeLock != null && mWakeLock.isHeld()) {mWakeLock.release();Log.d(TAG, "wake lock release");}if (mCursor != null) {getContentResolver().unregisterContentObserver(mLocalImagesObserver);getContentResolver().unregisterContentObserver(mLocalVideosObserver);unregisterReceiver(mSDcardMountedReceiver);mCursor.close();mCursor=null;}if (mAutoTestEnabled) {unregisterReceiver(mAutoTestReceiver);}if(mCurrentModule != null){mCurrentModule.onDestroy();}super.onDestroy();}

1、释放电源锁;
2、注销图片、视频等观察者;注销 SDcard广播监听;关闭回收数据库Cursor。
3、通知各模式去进行善后收尾操作。

从上可以看到,CameraActivity逻辑比较简单,只是用来接收一些系统事件,进而通知、调度到各模式,使其各模式自已进行逻辑功能控制。

SnapdragonCamera源码分析(一)CameraActivity相关推荐

  1. SnapdragonCamera源码分析

    SnapdragonCamera源码分析 原文:https://blog.csdn.net/qingsheng33/article/details/84401223 SnapdragonCamera是 ...

  2. SnapdragonCamera源码分析(二)OpenCamera流程

    记录一下SnapdragonCamera的相关流程. 从上一篇 SnapdragonCamera源码分析(一)CameraActivity可以知道,桌面点击相机图标实质上启动的是CameraActiv ...

  3. android6.0源码分析之Camera API2.0下的Preview(预览)流程分析

    1.Camera2 preview的应用层流程分析 preview流程都是从startPreview开始的,所以来看startPreview方法的代码: <code class="hl ...

  4. android6.0源码分析之Camera API2.0下的初始化流程分析

    1.Camera2初始化的应用层流程分析 Camera2的初始化流程与Camera1.0有所区别,本文将就Camera2的内置应用来分析Camera2.0的初始化过程.Camera2.0首先启动的是C ...

  5. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  6. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  7. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  8. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

  9. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

最新文章

  1. 微信公众平台开发者文档
  2. 怎样去掉a标签的蓝框
  3. JAVA核心知识总结
  4. [NOIP2012] 提高组 洛谷P1080 国王游戏
  5. deepin linux下解决Qt搜狗输入法无法输入中文
  6. 菜鸟裹裹电脑版_天猫淘宝“基本盘”放缓,阿里云、菜鸟爆发,马云迎来拐点?...
  7. 火狐浏览器插件大全,火狐插件大全,firefox插件使用方法
  8. matlab二进制十进制十六进制和任意进制之间的转换
  9. K8s CKA认证学习全套笔记
  10. 【FinalIK】Full Body Biped IK
  11. Java校验手机号格式
  12. android应用的优化建议(转载)
  13. 测试一个教室和一个椅子
  14. oracle和mysql查询条件排序_Oracle数据库中ORDERBY排序和查询按IN条件的顺序输出
  15. HelloWorld--JAVA程序
  16. license程序设计 系统划分 及加密设计 加密算法调用openssl库
  17. BlueTooth: 什么是蓝牙(Bluetooth)
  18. 亚马逊程序员:我曾拼命逃离996!
  19. Photoshop入门与进阶实例:2.6 黄金效果字的制作
  20. 服务器租用和服务器托管到底有什么区别

热门文章

  1. 网页设计软件列表HTML,免费的网页设计软件有哪些
  2. 混凝土搅拌站价格清单
  3. Jenkins+Gitlab+Generic Webhook Trigger插件
  4. 网上的一些日语教学视频合集
  5. matlab中升余弦滚降滤波器_升余弦滤波器 matlab代码
  6. matlab otsu csdn,matlab otsu csdn_matlab_otsu函数
  7. 木桶理论在现实中有什么指导意义
  8. 有感于盛大帝国的逐渐衰落!
  9. 即食燕窝的优势有哪些?即食燕窝好吗?
  10. Tensorflow的负采样函数Sampled softmax loss踩坑之旅