Android13系统源码内置App并通过AIDL调用获取内置存储卡的真实大小

本文主要是介绍Android13系统源码内置App并通过AIDL调用获取内置存储卡的真实大小,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

jix 进行从事Android系统源码开发不得不在原有的系统上内置自己的App。通过内置App一般都需要调用些系统才能访问的系统级App。App的部署和调试需要依赖源码系统。通过命令 : mm 来实现。

第三方App想调用内置的app需要通过跨进程调用。

这里通过AIDL来实现跨进程调用。

首先声明AIDL文件,

Android源码工程的文件构成和格式和标准的app完全不一样。

为了方便调试,先在标准的App中调试通过。

再copy标准工程到源码App工程里。

声明的AIDL文件:

Callback.aidl

package com.android.kvservice;interface Callback {oneway void onMessageReceived(int type, String value);
}

KvInterface.aidl

package com.android.kvservice;import com.android.kvservice.Callback;interface KvInterface {void registerCallback(Callback callback);void unregisterCallback(Callback callback);void sendMessage(int type, String value);
}

AIDL的文件夹放的位置

注意在build.gradle 里声明: aidl true

plugins {alias(libs.plugins.androidApplication)alias(libs.plugins.jetbrainsKotlinAndroid)
}android {namespace 'com.yyy.xxx.service'compileSdk 34defaultConfig {applicationId "com.yyy.xxx.kvservice"minSdk 29targetSdk 34versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"vectorDrawables {useSupportLibrary true}}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = '1.8'}buildFeatures {compose trueviewBinding trueaidl true}composeOptions {kotlinCompilerExtensionVersion '1.5.1'}packaging {resources {excludes += '/META-INF/{AL2.0,LGPL2.1}'}}}dependencies {implementation libs.androidx.core.ktximplementation libs.androidx.lifecycle.runtime.ktximplementation libs.androidx.activity.composeimplementation platform(libs.androidx.compose.bom)implementation libs.androidx.uiimplementation libs.androidx.ui.graphicsimplementation libs.androidx.ui.tooling.previewimplementation libs.androidx.material3testImplementation libs.junitandroidTestImplementation libs.androidx.junitandroidTestImplementation libs.androidx.espresso.coreandroidTestImplementation platform(libs.androidx.compose.bom)androidTestImplementation libs.androidx.ui.test.junit4debugImplementation libs.androidx.ui.toolingdebugImplementation libs.androidx.ui.test.manifest
}

实现AIDL接口的地方

import android.content.Context;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.StatFs;
import android.os.storage.StorageVolume;
import android.util.Log;
import android.os.storage.DiskInfo;
import android.os.storage.VolumeInfo;
import android.os.storage.StorageManager;
import android.app.usage.StorageStatsManager;import com.android.kvservice.Callback;
import com.android.kvservice.KvInterface;
import com.android.server.kvservice.storage.StorageEntry;
import com.android.server.kvservice.storage.StorageUtils;import java.util.List;public class KVService extends KvInterface.Stub {/*** 获取系统全部内存大小,包括隐藏的内存*/
//    public static final int GET_SYSTEM_STORGE_TOTAL = 1;public static final int GET_ALL_STORGE = 1;/****/public static final int GET_SYSTEM_STORGE_REMAIN = 2;public static final int GET_SDCARD_TOTAL = 3;public static final int GET_SDCARD_REMAIN = 4;private static final String TAG = KVService.class.getSimpleName();private RemoteCallbackList<Callback> mCallbackList = new RemoteCallbackList<>();private Context mContext;private Object lack = new Object();private StorageManager storageManager = null;public KVService(Context context) {this.mContext = context;storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);Log.d(TAG, "henryservice init");}@Overridepublic void registerCallback(Callback callback) {boolean result = mCallbackList.register(callback);Log.d(TAG, "register pid:" + Binder.getCallingPid()+ " uid:" + Binder.getCallingUid() + " result:" + result);}@Overridepublic void unregisterCallback(Callback callback) {boolean result = mCallbackList.unregister(callback);Log.d(TAG, "unregister pid:" + Binder.getCallingPid()+ " uid:" + Binder.getCallingUid() + " result:" + result);}@Overridepublic void sendMessage(int type, String value) throws RemoteException {String result = new String("no-data");if (type == GET_ALL_STORGE) {
//           ....}Log.e(TAG, "rev the type : =========== " + type);Log.e(TAG, "rev the value : =========== " + value);sendEventToRemote(type, result);}public void sendEventToRemote(int type, String value) {synchronized (lack) {int count = mCallbackList.getRegisteredCallbackCount();Log.d(TAG, "remote callback count:" + count);if (count > 0) {// 注意: 遍历过程如果存在多线程操作, 需要加锁, 不然可能会抛出异常final int size = mCallbackList.beginBroadcast();for (int i = 0; i < size; i++) {Callback cb = mCallbackList.getBroadcastItem(i);try {if (cb != null) {cb.onMessageReceived(type, value);}} catch (RemoteException e) {e.printStackTrace();Log.d(TAG, "remote exception:" + e.getMessage());}}mCallbackList.finishBroadcast();}}}}

实现Service的地方:

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;import androidx.annotation.Nullable;import com.android.kvservice.Callback;
import com.android.kvservice.KvInterface;
import com.android.server.kvservice.KVService;public class KService extends Service {private KVService kvService = null;@Overridepublic void onCreate() {super.onCreate();if (kvService == null) {kvService = new KVService(this);}}@Nullable@Overridepublic IBinder onBind(Intent intent) {return binder;}private final KvInterface.Stub binder = new KvInterface.Stub() {@Overridepublic void registerCallback(Callback callback) throws RemoteException {Log.e("KService", " registerCallback ============ ");kvService.registerCallback(callback);Log.e("KService", " registerCallback ============ end");}@Overridepublic void unregisterCallback(Callback callback) throws RemoteException {Log.e("KService", " unregisterCallback ============ ");kvService.unregisterCallback(callback);Log.e("KService", " unregisterCallback ============ end");}@Overridepublic void sendMessage(int type, String value) throws RemoteException {kvService.sendMessage(type, value);Log.e("KService", " sendMessage ============ end");}};
}

本地调用Service的代码。


import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;import androidx.annotation.Nullable;import com.android.kvservice.Callback;
import com.android.kvservice.KvInterface;
import com.android.server.kvservice.KVService;public class MainActivity extends Activity {//    private MainBinding binding;private KvInterface kvInterface;private Button btnStart;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);
//        binding = MainBinding.inflate(getLayoutInflater());
//        setContentView(binding.getRoot());setContentView(R.layout.main);btnStart = findViewById(R.id.btnStart);btnStart.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (kvInterface == null) {bindToService();}}});Button btnSend = findViewById(R.id.btnSend);btnSend.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Log.e("TAG", "send 111");registerCallback();if (kvInterface != null) {try {Log.e("TAG", "send 222");kvInterface.sendMessage(1, "");} catch (RemoteException e) {e.printStackTrace();}}}});}private boolean register = false;private Callback.Stub callback = new Callback.Stub() {@Overridepublic void onMessageReceived(int type, String value) throws RemoteException {Log.e("MainActivity", "rev the type:: ==== " + type);Log.e("MainActivity", "rev the value:: ==== " + value);}};private void registerCallback() {if (!register) {register = true;try {if (kvInterface != null) {kvInterface.registerCallback(callback);}} catch (Exception e) {e.printStackTrace();}}}@Overrideprotected void onDestroy() {super.onDestroy();try {if (kvInterface != null) {kvInterface.unregisterCallback(callback);}} catch (RemoteException e) {throw new RuntimeException(e);}}private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {kvInterface = KvInterface.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {kvInterface = null;}};private void bindToService() {
//        bindService(new Intent("com.kingview.qti.service.KService"), serviceConnection, Service.BIND_AUTO_CREATE);bindService(new Intent(this, KService.class), serviceConnection, Service.BIND_AUTO_CREATE);Log.e("TAG","bindToService ======================");}
}

为了第三方App调用,需要再配置文件中做以下声明:

  <serviceandroid:name="com.kingview.qti.service.KService"android:enabled="true"android:exported="true"android:process="com.kingview.qti.service.kservice"><intent-filter><action android:name="com.kingview.service.kservice" /></intent-filter></service>

第三方App调用AIDL服务的代码,发现调用Action并没有什么用,特别注意要输入完整的包名:

  private fun bindToService() {val intent = Intent()intent.setComponent(ComponentName("com.kingview.qti.service","com.kingview.qti.service.KService"))bindService(intent,serviceConnection,BIND_AUTO_CREATE)
//        Log.e("StorageTestActivity", " bindToService ====================== ")}

注册远程回调

 private fun registerCallback() {if (!register) {register = truetry {if (kvInterface != null) {kvInterface!!.registerCallback(callback)}kvInterface?.sendMessage(1, "")} catch (e: Exception) {e.printStackTrace()}}}

反注册callback和 解绑Service。

override fun onDestroy() {super.onDestroy()kvInterface?.unregisterCallback(callback)unbindService(serviceConnection)}

监听实现的操作是这样实现的:

 @Volatileprivate var register = falseprivate val callback: Callback = object : Callback.Stub() {@Throws(RemoteException::class)override fun onMessageReceived(type: Int, value: String) {Log.e("StorageTestActivity", "rev the type:: ==== $type")Log.e("StorageTestActivity", "rev the value:: ==== $value")//解析数据,并显示到界面上if (testKey == KVApplication.TEST_STORAGE) {val strs = value.split("#").filter { v ->v.trim() != ""}for (v in strs) {val bean = Gson().fromJson<StorageBean>(v, StorageBean::class.java)if (bean.description.contains("internal_storage")) {val total = bean.total.toLong() / 1000f / 1000f / 1000f;val used = bean.used.toLong() / 1000f / 1000f / 1000f;val remain = total - usedbinding.txtStatus.post {binding.txtSize.text = " Size: ${total} GB ; Remain ${remain} GB. \n "}}}}}}

通过以上操作你可能会发现,依然无法调用远程的Service。你需要再AndroidManifest里声明一个权限。

 <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"tools:ignore="QueryAllPackagesPermission" />

将App放置在源码工程的的 package/apps/里创建一个文件夹如:XYZService

src/ res/ Androidmanifast.xml 都要从标准App工程里copy出来。

 Android.bp这样写,包含了AIDL文件 :

//
// Copyright (C) 2013 Google Inc.
//package {default_applicable_licenses: ["packages_apps_kvservice_license"],
}// Added automatically by a large-scale-change
// See: http://go/android-license-faq
license {name: "packages_apps_kvservice_license",visibility: [":__subpackages__"],license_kinds: ["SPDX-license-identifier-BSD",],// large-scale-change unable to identify any license_text files
}android_app {name: "XYZServiceTest",defaults: ["platform_app_defaults"],platform_apis: true,certificate: "platform",system_ext_specific: true,privileged: true,srcs: ["src/**/*.java","src/**/*.aidl",],aidl: {local_include_dirs: ["src/aidl"],},manifest: "AndroidManifest.xml",resource_dirs: ["res",],// privileged: true,// sdk_version: "33",// sdk_version: "current",// certificate: "platform",// platform_apis: true,// product_specific: true,static_libs: ["androidx.core_core",// "androidx.annotation:annotation:1.3.0","guava",],
}

 通过阅读Setting源码移植的代码如下:

    public static List<StorageEntry> getAllStorageEntries(Context context,StorageManager storageManager) {final List<StorageEntry> storageEntries = new ArrayList<>();storageEntries.addAll(storageManager.getVolumes().stream().filter(volumeInfo -> isStorageSettingsInterestedVolume(volumeInfo)).map(volumeInfo -> new StorageEntry(context, volumeInfo)).collect(Collectors.toList()));storageEntries.addAll(storageManager.getDisks().stream().filter(disk -> isDiskUnsupported(disk)).map(disk -> new StorageEntry(disk)).collect(Collectors.toList()));storageEntries.addAll(storageManager.getVolumeRecords().stream().filter(volumeRecord -> isVolumeRecordMissed(storageManager, volumeRecord)).map(volumeRecord -> new StorageEntry(volumeRecord)).collect(Collectors.toList()));return storageEntries;}

public class StorageEntry implements Comparable<StorageEntry>, Parcelable  {private final VolumeInfo mVolumeInfo;private final DiskInfo mUnsupportedDiskInfo;private final VolumeRecord mMissingVolumeRecord;private final String mVolumeInfoDescription;public StorageEntry(@NonNull Context context, @NonNull VolumeInfo volumeInfo) {mVolumeInfo = volumeInfo;mUnsupportedDiskInfo = null;mMissingVolumeRecord = null;if (isDefaultInternalStorage()) {// Shows "This device" for default internal storage.mVolumeInfoDescription = "storage_default_internal_storage";} else {mVolumeInfoDescription = context.getSystemService(StorageManager.class).getBestVolumeDescription(mVolumeInfo);}}public StorageEntry(@NonNull DiskInfo diskInfo) {mVolumeInfo = null;mUnsupportedDiskInfo = diskInfo;mMissingVolumeRecord = null;mVolumeInfoDescription = null;}public StorageEntry(@NonNull VolumeRecord volumeRecord) {mVolumeInfo = null;mUnsupportedDiskInfo = null;mMissingVolumeRecord = volumeRecord;mVolumeInfoDescription = null;}private StorageEntry(Parcel in) {mVolumeInfo = in.readParcelable(VolumeInfo.class.getClassLoader());mUnsupportedDiskInfo = in.readParcelable(DiskInfo.class.getClassLoader());mMissingVolumeRecord = in.readParcelable(VolumeRecord.class.getClassLoader());mVolumeInfoDescription = in.readString();}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel out, int flags) {out.writeParcelable(mVolumeInfo, 0 /* parcelableFlags */);out.writeParcelable(mUnsupportedDiskInfo, 0 /* parcelableFlags */);out.writeParcelable(mMissingVolumeRecord, 0 /* parcelableFlags */);out.writeString(mVolumeInfoDescription);}public static final Parcelable.Creator<StorageEntry> CREATOR =new Parcelable.Creator<StorageEntry>() {public StorageEntry createFromParcel(Parcel in) {return new StorageEntry(in);}public StorageEntry[] newArray(int size) {return new StorageEntry[size];}};@Overridepublic boolean equals(Object o) {if (o == this) {return true;}if (!(o instanceof StorageEntry)) {return false;}final StorageEntry StorageEntry = (StorageEntry) o;if (isVolumeInfo()) {return mVolumeInfo.equals(StorageEntry.mVolumeInfo);}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.equals(StorageEntry.mUnsupportedDiskInfo);}return mMissingVolumeRecord.equals(StorageEntry.mMissingVolumeRecord);}@Overridepublic int hashCode() {if (isVolumeInfo()) {return mVolumeInfo.hashCode();}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.hashCode();}return mMissingVolumeRecord.hashCode();}@Overridepublic String toString() {if (isVolumeInfo()) {return mVolumeInfo.toString();}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.toString();}return mMissingVolumeRecord.toString();}@Overridepublic int compareTo(StorageEntry other) {if (isDefaultInternalStorage() && !other.isDefaultInternalStorage()) {return -1;}if (!isDefaultInternalStorage() && other.isDefaultInternalStorage()) {return 1;}if (isVolumeInfo() && !other.isVolumeInfo()) {return -1;}if (!isVolumeInfo() && other.isVolumeInfo()) {return 1;}if (isPrivate() && !other.isPrivate()) {return -1;}if (!isPrivate() && other.isPrivate()) {return 1;}if (isMounted() && !other.isMounted()) {return -1;}if (!isMounted() && other.isMounted()) {return 1;}if (!isVolumeRecordMissed() && other.isVolumeRecordMissed()) {return -1;}if (isVolumeRecordMissed() && !other.isVolumeRecordMissed()) {return 1;}if (getDescription() == null) {return 1;}if (other.getDescription() == null) {return -1;}return getDescription().compareTo(other.getDescription());}/*** Returns default internal storage.*/public static StorageEntry getDefaultInternalStorageEntry(Context context) {return new StorageEntry(context, context.getSystemService(StorageManager.class).findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL));}/*** If it's a VolumeInfo.*/public boolean isVolumeInfo() {return mVolumeInfo != null;}/*** If it's an unsupported DiskInfo.*/public boolean isDiskInfoUnsupported() {return mUnsupportedDiskInfo != null;}/*** If it's a missing VolumeRecord.*/public boolean isVolumeRecordMissed() {return mMissingVolumeRecord != null;}/*** If it's a default internal storage.*/public boolean isDefaultInternalStorage() {if (isVolumeInfo()) {return mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE&& TextUtils.equals(mVolumeInfo.getId(), VolumeInfo.ID_PRIVATE_INTERNAL);}return false;}/*** If it's a mounted storage.*/public boolean isMounted() {return mVolumeInfo == null ? false : (mVolumeInfo.getState() == VolumeInfo.STATE_MOUNTED|| mVolumeInfo.getState() == VolumeInfo.STATE_MOUNTED_READ_ONLY);}/*** If it's an unmounted storage.*/public boolean isUnmounted() {return mVolumeInfo == null ? false : (mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTED);}/*** If it's an unmountable storage.*/public boolean isUnmountable() {return mVolumeInfo == null ? false : mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTABLE;}/*** If it's a private storage.*/public boolean isPrivate() {return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE;}/*** If it's a public storage.*/public boolean isPublic() {return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PUBLIC;}/*** Returns description.*/public String getDescription() {if (isVolumeInfo()) {return mVolumeInfoDescription;}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.getDescription();}return mMissingVolumeRecord.getNickname();}/*** Returns ID.*/public String getId() {if (isVolumeInfo()) {return mVolumeInfo.getId();}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.getId();}return mMissingVolumeRecord.getFsUuid();}/*** Returns disk ID.*/public String getDiskId() {if (isVolumeInfo()) {return mVolumeInfo.getDiskId();}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.getId();}return null;}/*** Returns fsUuid.*/public String getFsUuid() {if (isVolumeInfo()) {return mVolumeInfo.getFsUuid();}if (isDiskInfoUnsupported()) {return null;}return mMissingVolumeRecord.getFsUuid();}/*** Returns root file if it's a VolumeInfo.*/public File getPath() {return mVolumeInfo == null ? null : mVolumeInfo.getPath();}/*** Returns VolumeInfo of the StorageEntry.*/public VolumeInfo getVolumeInfo() {return mVolumeInfo;}
}

Setting关于计算存储卡的代码在

StorageUsageProgressBarPreferenceController 

StorageDashboardFragment文件

这篇关于Android13系统源码内置App并通过AIDL调用获取内置存储卡的真实大小的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1112938

相关文章

SpringBoot服务获取Pod当前IP的两种方案

《SpringBoot服务获取Pod当前IP的两种方案》在Kubernetes集群中,SpringBoot服务获取Pod当前IP的方案主要有两种,通过环境变量注入或通过Java代码动态获取网络接口IP... 目录方案一:通过 Kubernetes Downward API 注入环境变量原理步骤方案二:通过

基于Python实现一个简单的题库与在线考试系统

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系... 目录概述功能特点界面展示系统架构设计类结构图Excel题库填写格式模板题库题目填写格式表核心数据结构

Linux系统中的firewall-offline-cmd详解(收藏版)

《Linux系统中的firewall-offline-cmd详解(收藏版)》firewall-offline-cmd是firewalld的一个命令行工具,专门设计用于在没有运行firewalld服务的... 目录主要用途基本语法选项1. 状态管理2. 区域管理3. 服务管理4. 端口管理5. ICMP 阻断

使用Python实现获取屏幕像素颜色值

《使用Python实现获取屏幕像素颜色值》这篇文章主要为大家详细介绍了如何使用Python实现获取屏幕像素颜色值,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、一个小工具,按住F10键,颜色值会跟着显示。完整代码import tkinter as tkimport pyau

Java调用C#动态库的三种方法详解

《Java调用C#动态库的三种方法详解》在这个多语言编程的时代,Java和C#就像两位才华横溢的舞者,各自在不同的舞台上展现着独特的魅力,然而,当它们携手合作时,又会碰撞出怎样绚丽的火花呢?今天,我们... 目录方法1:C++/CLI搭建桥梁——Java ↔ C# 的“翻译官”步骤1:创建C#类库(.NET

python获取cmd环境变量值的实现代码

《python获取cmd环境变量值的实现代码》:本文主要介绍在Python中获取命令行(cmd)环境变量的值,可以使用标准库中的os模块,需要的朋友可以参考下... 前言全局说明在执行py过程中,总要使用到系统环境变量一、说明1.1 环境:Windows 11 家庭版 24H2 26100.4061

Windows 系统下 Nginx 的配置步骤详解

《Windows系统下Nginx的配置步骤详解》Nginx是一款功能强大的软件,在互联网领域有广泛应用,简单来说,它就像一个聪明的交通指挥员,能让网站运行得更高效、更稳定,:本文主要介绍W... 目录一、为什么要用 Nginx二、Windows 系统下 Nginx 的配置步骤1. 下载 Nginx2. 解压

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

windows系统上如何进行maven安装和配置方式

《windows系统上如何进行maven安装和配置方式》:本文主要介绍windows系统上如何进行maven安装和配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录1. Maven 简介2. maven的下载与安装2.1 下载 Maven2.2 Maven安装2.

C/C++和OpenCV实现调用摄像头

《C/C++和OpenCV实现调用摄像头》本文主要介绍了C/C++和OpenCV实现调用摄像头,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录准备工作1. 打开摄像头2. 读取视频帧3. 显示视频帧4. 释放资源5. 获取和设置摄像头属性