Q : A

Q: 为什么需要互斥锁?

  • 多线程并发访问共享资源会导致数据竞争
  • 可能导致数据损坏或未定义行为
  • 确保入队/出队操作的原子性
  • 防止操作被其他线程中断
  • Q: 为什么使用条件变量?

  • 替代自旋等待,降低CPU使用率
  • 线程可以进入休眠状态等待通知
  • 精确的线程通知
  • 避免不必要的线程唤醒
  • Q: 如何处理队列满/空状态?

    1、队列满

    生产者线程状态变化:
    运行态 -> 阻塞态(队列满) -> 就绪态(被唤醒) -> 运行态
    
    1. [运行] 生产者尝试写入数据
    2. [检查] 发现队列已满
    3. [阻塞] 调用wait进入阻塞状态
    4. [等待] 等待消费者消费并唤醒
    5. [恢复] 被唤醒后继续执行
    

    2、队列空

    消费者线程状态变化:
    运行态 -> 阻塞态(队列空) -> 就绪态(被唤醒) -> 运行态
    
    1. [运行] 消费者尝试读取数据
    2. [检查] 发现队列为空
    3. [阻塞] 调用wait进入阻塞状态
    4. [等待] 等待生产者生产并唤醒
    5. [恢复] 被唤醒后继续执行
    

    Q: 如何处理音视频同步问题?

    1、同步策略:

  • 基于PTS的同步
  • 维护音视频PTS
  • 动态调整处理速率
  • 2、缓冲区管理

  • 动态调整缓冲区大小
  • 实现平滑播放
  • Q:音视频播放流程

    graph TD
        A[输入文件] --> B[解复用器 Demuxer]
        B --> C1[视频包队列]
        B --> C2[音频包队列]
        
        subgraph 视频处理流程
            C1 --> D1[视频解码器]
            D1 --> E1[解码帧队列]
            E1 --> F1[格式转换 YUV->RGBA]
            F1 --> G1[渲染缓冲区]
            G1 --> H1[显示 ANativeWindow]
        end
        
        subgraph 音频处理流程
            C2 --> D2[音频解码器]
            D2 --> E2[PCM帧队列]
            E2 --> F2[重采样器]
            F2 --> G2[音频环形缓冲区]
            G2 --> H2[AAudio播放]
        end
        
        I[时钟同步系统] --> G1
        I --> G2
    

    Q:整体架构层次

    graph TD
        A[Java层 - UI/控制] --> B[JNI层 - 接口桥接]
        B --> C[Native层 - 核心功能]
        
        subgraph Java层
            A1[Player.java] --> A2[播放控制]
            A2 --> A3[界面交互]
        end
        
        subgraph JNI层
            B1[native-lib.cpp] --> B2[接口映射]
            B2 --> B3[数据转换]
        end
        
        subgraph Native层
            C1[音视频解码] --> C2[渲染系统]
            C2 --> C3[音频输出]
        end
    

    Q:写一个返回值为void的函数,传入一个参数,在函数体内申请一块内存,将这块内存传出

    void allocateMemory(int **ptr,size_t size){
    	*ptr = new int[size];
    	if(*ptr == nullptr){
    		exit(1);
    	}
    }
    
    int main(){
    	int* array = nullptr;
    	size_t size = 5;
    	allocateMemory(&array,size);
    	for(int i = 0;i < size;i++){
    		array[i] = i + 1;
    	}
    	delete[] array;
    	return 0;
    }
    

    Q:安卓启动流程

    Boot ROM:设备上电后,Boot ROM会被加载并执行,负责初始化硬件并加载Bootloader。
    
    Bootloader:Bootloader负责加载Linux内核,并传递必要的参数。它可以进行一些基本的硬件检测和配置。
    
    Linux内核:内核启动后,会初始化系统资源和驱动程序,完成硬件的基本配置。
    
    init进程:内核启动后,init进程会被创建。它是用户空间的第一个进程,负责启动其他系统服务。
    
    Zygote进程:init进程会启动Zygote进程,Zygote是Android应用的孵化器,负责加载应用程序的类和资源。
    
    System Server:Zygote会启动System Server,负责管理系统服务,如Activity Manager、Window Manager等。
    
    Launcher和System UI:最后,System Server会启动Launcher(桌面应用)和System UI,用户界面就此呈现。
    

    Q:pms的操作流程

    1、启动PMS:

    • PMS作为系统服务在SystemServer进程中启动,负责管理应用程序的安装、卸载和更新。

    2、应用程序安装:

    • 当用户通过Google Play或其他渠道安装应用时,PMS会接收到安装请求。
    • 验证签名:PMS会验证APK的签名,确保其来源可信。
    • 解析清单文件:读取AndroidManifest.xml文件,获取应用的组件、权限等信息。
    • 准备安装位置:确定应用的安装路径,并为其分配存储空间。
    • 复制APK文件:将APK文件复制到指定的安装目录。
    • 解析资源文件:处理应用的资源文件,准备应用的运行环境。
    • 安装应用程序组件:将应用的组件(如Activity、Service等)注册到系统中。
    • 创建快捷方式:在桌面或应用列表中创建应用的快捷方式。
    • 授予权限:根据AndroidManifest.xml中的声明,授予应用所需的权限。
    • 触发安装完成广播:通知系统安装完成,应用可以开始使用。

    3、应用程序卸载:

    • 当用户选择卸载应用时,PMS会处理卸载请求。
    • 删除应用数据:清除与应用相关的所有数据和文件。
    • 更新系统状态:更新系统中应用的状态,确保卸载完成。

    Q:APK主要结构

    1、META-INF:

    • 包含应用的签名信息和清单文件。
    • 主要文件:
    • MANIFEST.MF:列出所有文件及其哈希值。
    • CERT.SF:包含签名的文件。
    • CERT.RSA:应用的数字证书。

    2、res:

    • 存放应用的资源文件,如布局、图片、字符串等。
    • 资源文件按类型分类,如drawable、layout、values等。

    3、lib:

    • 存放应用的本地库(.so文件),通常用于JNI(Java Native Interface)调用。
    • 按平台架构分类,如armeabi-v7a、arm64-v8a等。

    4、assets:

    • 存放应用的原始文件,开发者可以在应用中直接访问这些文件。例如,HTML文件、字体文件等。

    5、AndroidManifest.xml:

    • 应用的清单文件,定义了应用的基本信息,如包名、组件(Activity、Service等)、权限等。

    6、classes.dex:

    • 存放编译后的Java字节码,Android运行时会将其加载到内存中执行。

    7、resources.arsc:

    • 存放编译后的资源索引,包含应用的所有资源的引用和ID。
    全部评论

    相关推荐

    点赞 2 评论
    分享
    牛客网
    牛客企业服务