IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Learn-1 -> 正文阅读

[移动开发]Learn-1

点击图标启动:

首先launcher进程通过binder IPC方式向System_server(AMS)发起StartActivity请求

System-server(AMS)通过socket方式向zygote进程发起创建新的APP进程请求

zygotefork出新的APP进程,这个进程通过IPC告诉system-server发起attachApplication请求

system-server(AMS)收到之后,其中的ApplicationThreadProxy又通过ipc的方式给APP进程发送scheduleLaunchActivity请求

APP进程中的binder线程ApplicationThread接收这个请求之后,通过handle向主线程ActivityThread发起LAUNCH_ACTIVITY,主线程通过反射机制创建目标activity,并回调其onCreate方式。

四种场景:

standard,默认模式,每次都会创建新的Activity

SingleTop,如果在栈顶,则不创建,直接调用activity的onNewIntent方法,不在栈顶,则创建新的实例

SingleTask,如果在归属的栈中存在,则将其上面的所有activity出栈,并调用onNewIntent方法,

SingleInstance,每次都创建一个新的栈,然后创建新的activity

ANR场景:

Server TimeOut:前台服务20s,后台服务200s,超时未执行完成

BroadCastQueue TimeOut: 前台广播10s,后台60s,超时未处理完广播

ContentProvider TimeOut: publish 10s没完成

Service?

两种方式

startService

onCreate onStartCommand? onDestroy,如果服务已经开启,多次执行startService,只会调用onStart()-》onStartCommand,销毁直接调用StopService,与调用者无关

bindService

onCreate onbind onUnbind onDestroy,如果服务已经开始,多次执行bindService,不会多次调用onBind()销毁需要调用unBindService 或者 调用者退出了,服务自动调用unBind()-》onDestroy终止。

启动流程:

AMP进程通过binder ipc方式向AMS发起StartService请求,

AMS使用socket向zygote发起新进程的请求

zygote通过socket方式fork出一个新进程Remote Service进程

Remote Service进行通过IPC方式向AMS发起attachApplication请求

AMS通过ApplicationThreadProxy发送scheduleCreateService请求给Remote Service进程中的ApplicationThread这个binder线程,它收到请求之后通过handle向主线程ActivityThread发送CREATE—Service消息

主线程ActivityThread通过反射机制,创建目标Service,并回调其onCreate方法

如果是本地服务或者服务所属的进程已经创建,就跳过zygote这一步。

handle 原理

子线程创建的时候会调用Looper.myLooper()来判断是否looper为null,是的话则报错,所以子线程中需要调用Looper.prepare()之和再创建handle对象。

其中线程切换原理:在主线程中创建handle的时候,会把主线程MainThread和创建的looper绑定,,而在子线程中调用handle发送消息的时候,主线程的looper通过循环拿到消息了,这样通过looper关联的主线程,就可以操作消息了,这样就是handle的线程切换

handle,发送Message消息到MessageQueue,loop会将msg送给绑定的handle中dispatchMessage方法

Message,封装的消息对象,使用Message.obtain创建对象。因为可以检查是否有可以复用的Message,用过复用避免过多的创建、销毁Message对象达到优化内存和性能的目地

MessageQueue,单链表的数据结构

Loop,循环查询MessageQueue里面的消息,一个线程里面只能有一个,创建使用Looper.prepare(),在主线程ActivityThread里面使用

       1. Looper.prepareMainLooper(); //初始化Looper以及MessageQueue

? ? ? ? ? ? ?2.?prepare(false);//消息队列不可以quit

? ? ? ?3.?sThreadLocal.set(new Looper(quitAllowed));//创建Looper并设置给sThreadLocal,这样get的时候就不会为null了

   private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//创建了MessageQueue
        mThread = Thread.currentThread(); //当前线程的绑定
   }

MVC:

view: xml?

model:网络请求,数据库处理

controller:activity

缺点:activity承担了view和controller的角色,解耦程度不够。

mvp:

view:xml,activity

model: 网络请求

present:更新UI

缺点:双向依赖,UI改变,P也要改变,由于P持有activity,导致处理耗时的操作时候,内存泄漏的风险,使用弱引用解决这个问题。

mvvm:

view:xml,activity

model:网络请求

ViewModel:存储UI状态,处理逻辑请求,将数据设置成观察者容器

主要是view可以通过观察者模式,如果viewmodel层有数据更新了,view就自动更新,不需要在viewmodel层写回调方法,解决了mvp中V跟P相互依赖的问题

MVI:

view:

intent:

model:

viewState

Surfaceview

拥有独立的绘图表面,可以在独立的线程中进行UI操作,不会影响主线程,在Surfaceflinger中拥有独立的layer

在Surfaceview中的onAttachToWindow的函数里面会调用mParent.requestTransparentRegion();来告诉activity去设置一块透明的区域,这样才可以看到SurfaceView的图层,因为SurfaceView默认的图层Z坐标小于activity的z坐标。

相当于默认的activity图层在Surfaceview图层。

TextureView

在主线程Activity中绘制的,需要在CPU,GPU这三者之间进行同步,如果同步出错,会出现掉帧,卡顿和抖动的情况,

TextureView可以通过屏幕截图或者getBitmap得到视频画面,而SurfaceView不可以。

GLSurfaceView

提供OpenGL将内容渲染到Surface上,使用单独的GLThread在设置render之后创建,继承自SurfaceView

音频播放SDK

MediaPlayer:适合后台长时间播放本地文件或者在线音频

SoundPool:适合播放端的音频片段,游戏声音,铃声,可以同时播多个音频

AudioTrack:适合低延迟的播放,适合流媒体播放,需要结合解码器使用

音频播放NDK层

OpenSL ES:强大的音效处理,低延迟播放。可以实现实时耳返的功能。

FFmpeg 拼接 裁剪 加速 转场

OpenGL 图像缩放,特效,裁剪,叠加,模糊,滤镜

缩放:在顶点着色器里面添加rotate旋转,scale缩放矩阵,去乘以顶点坐标,就可以

叠加,水印:通过OpenGL的blend混合模式,将两个Texture同时绘制上去,形成叠加状态,也可以用openCV进行像素叠加

滤镜:通过切换不同的shader文件,主要是在片段着色器里面,将顶点着色器传过来的纹理坐标,对应图片纹理上的像素点,通过修改像素点的颜色来设置滤镜

性能优化

包大小:

Proguard 混淆,使用代码混淆,可以去掉无效的代码,并简化代码

lint检查,删除没有用到的资源文件,资源图片进行压缩,选择合适的图片格式,比如webp的有损压缩能比jpg小25-34%

lib优化,去掉不常用的CPU架构库文件,比如arm64-v8a向下兼容v7a的。

启动优化:

Application创建过程优化:设置异步和同步加载两个函数,耗时操作放入异步加载中

MainActivity创建过程优化:同样设置异步加载,如果必须在主线程中的加载,可以设置延迟加载,等首屏绘制完成再去加载。

布局优化,减少布局层级,减少首次加载的view的数量

SDK等资源可以懒加载,需要的时候再去加载。Bitmap复用

view层级复杂,使用ConstarintLayout,如果view层级简单使用LinerLayout,这个比ConstraintLayout和RelativeLayout渲染耗时都要少

使用ViewStub控件,需要的时候才加载

内存优化:

使用线程池创建线程,onDraw避免创建对象

StringBuilder 代替String 拼接字符

rebase 和merge的区别:

merge命令不会保留merge的分支,不会产生commit,合并两个分支为一个新的提交。

rebase 会取消当前分支中的每个提交,然后重新设置基线,最后把自己的之前提交放到这个基线的后面,比如你同事在master更新了提交,你想基于这个提交来更新自己的commit,就可以通过rebase的方式,将自己的提交放在同事的提交的后面。

cherrypick 用于拷贝某一次的提交。

malloc 和new的区别:

new 是操作符在调用的时候,先分配内存,再调用构造函数,delete的时候调用析构函数。new? 可以被重载,只可以分配对象所占内存的整数倍大小,内存分配在自由存储区,可以是堆,也可以是其他地方

malloc 是函数,直接分配内存,调用free释放,可以分配任意大小的字节,内存分配都在堆里面

System.Load加载流程

Java层调用System.loadlibrary("ffmpeg"),会调用Runtime中的loadlibrary,这个里面首先调用findlibrary去查找so文件是否存在,如果存在则调用nativeLoad.

findlibrary过程,首先在应用内查找so文件,data/app/packagename/lib/arm,如果没有找到,则去系统里面寻找,vendor/lib或者system/lib 如果zygote收到AMS需要fork出64位的进程的话,则去vendor/lib64或者system/lib64里面寻找

然后通过nativeLoad 加载so文件,先判断是否加载过so,如果加载过,则判断是否是同一个ClassLoader,如果一致,返回上次的加载结果,不一致则报错。

然后通过dlopen 打开so,使用dlsym 获取jni_on_load函数指针地址,最后通过jni_on_load返回加载是否成功。

最后可以通过dlopen返回的句柄来调用so里面的函数。

Boost 里面 c++ 智能指针:

scoped_ptr, 不支持拷贝和赋值的操作,防拷贝的实现原理:将拷贝构造函数和赋值运算符进行重载,只声明私有 不实现,在C++11里面自带unique_ptr 跟这个效果一样,不共享对象,如果转移到另外一个unique_ptr则原始的不再拥有对象,使用reset绑定对象,move转移对象拥有权,release释放拥有权

shared_ptr,共享指针,里面有引用计数,有循环引用问题,C++11里面自带shared_ptr效果一样

weak_ptr,弱指针,配合shared_ptr使用,解决循环引用问题,因为weak_prt不拥有这个空间,所以不会增加shared_ptr的引用计数

instrusive_ptr,类似share _ptr,需要自己实现指针计数

scope_array,针对数组

share_array,针对数组

C++里面没有这两个针对数组的智能指针,需要采用定制删除器的方式:

template<class T> struct Delete { void operator()(T* ptr) { delete ptr; } };

jni层内存泄漏检测工具:

MemoryLeakDetector,https://github.com/bytedance/memory-leak-detector/blob/master/README_cn.md

进程间通信:

binder,只需要一次拷贝,Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。

socket,zygote和AMS通信就是通过socket。

contentprovider,基于binder

AIDL,基于binder

TCP

三次握手解释:因为TCP是全双工的,client需要给server发数据,而server也要给client发数据。所以第一次是client向server监听的端口发送连接请求,服务器同意连接的同时,还需要给client发送连接请求,所以这是第二次,最后client给服务器发送确认连接的消息之后,双方都进入已连接状态。

okhttp

rxjava

eventbus:通过创建单例模式,在注册的时候通过反射查找方法,使用观察者模式,内部使用hashmap保存订阅的列表

socket

rtmp:

首先进行client和server握手,client发送C0,C1,C2三个有序的chunk,server发送S0,S1,S2三个chunk

异常处理机制

音频混音原理:

线性相加:通过把同一声道的数值相加,如果16bit整数,两个字节能够表示的有符号整型数的范围是-32768~32767。如果相加溢出了,直接使用最大正数32767或者最大负数-32768

平均调整权重:通过把同一声道的数值相加之和,除以通道数

归一化:通过把同一声道的数值相加之和去乘以归一化因子

activity缓存:

如果系统未经用户允许,停止activity的时候,会调用onSaveInstanceState 让用户去保存一些临时数据,重新onCreate的时候,可以通过onSaveInstanceState的bundle获取保存的数据,也可以通过onRestoreInstanceState去获取

C++指针和引用的区别:

引用不能为null,指针可以

指针的内容是指向这块内存的地址,而引用是这块内存的别用名

指针使用++ 则会指向这块内存的邻居下一个内存的地址,不再指向这块内存了,而引用这是将这块内存中的值自增,如果这块内存保存的值为1,则变成2

动态代理:运行的时候通过反射创建代理类,可以根据需要创建不同的代理类?

静态代理:直接在编译之前就创建代理类。

Android 类加载机制:

核心是父类加载器能否胜任加载工作,如果不能则继续往上递归,如果最终所有的父类都不能完成加载,则自己才加载。代码流程:

findLoadedClass(className)-》失败则判断parent是否为null 否则调用parent.loadClass(className); 是则调用findBootstrapClassOrNull(className)

如果上面都失败,则自己来找调用findClass(className),这个方法调用在BaseDexClassLoader里面的findClass,主要是DexPathList.findclass,而DexPathList则是在dexElement数组里面寻找,dexElement数组是在maxDexElements这个函数赋值的

而这个函数第一个参数是APK编译打包的Class.dex的路径。所以热修复就是把打包好的补丁包patch.dex通过maxDexElements合并到dexElement数组里面去。

热修复:流程就是通过反射拿到类加载里面的elements数组,然后通过maxDexElements方法解析打包的patch.dex文件,拿到里面的elements数组,然后跟原来的dexElements数组进行合并,放到原来的前面。Android中PathClassLoader用于加载APP的类,bootClassLoader加载系统的类,而热修复一般是DexClassLoader可以指定加载的dex文件目录,而pathclassloader只能加载系统默认位置,Android8。0开始 DexClassLoader也只能加载系统默认位置。

Android onCreate里面获取view的宽高:

通过调用view.post(new Runnable)

Surfaceview 显示Camera流程

C++值传递和引用传递
首先值传递,比如传指针,当把值当做参数传入到函数里面,编译系统会在调用这个函数的时候拷贝一份实参给函数的形参,这个会导致在函数内修改参数的值并不会修改实参本身,只是修改了副本的值,而且使用这种模式,在传递结构体或者class等类型的时候,会需要额外一份副本的内存开销。

示例:

int FunA(const int m) // 值传递,
main(){
    int a = 1;
    FunA(a);
}
这里实际上是代表:int m = 1;对m初始化值为1,所以函数里面改变m的值 并没有改变a的值
代码块
PlainText

复制

int FunA(const int m) // 值传递,
main(){
    int a = 1;
    FunA(a);
}
这里实际上是代表:int m = 1;对m初始化值为1,所以函数里面改变m的值 并没有改变a的值
int FunA(const int m) // 值传递, main(){ int a = 1; FunA(a); } 这里实际上是代表:int m = 1;对m初始化值为1,所以函数里面改变m的值 并没有改变a的值

然后是引用传递,编译系统会在调用函数的时候,把实参的内存地址(指针)传给形参,那么形参和实参指向同一块内存,改变形参的值,就是改变实参的值,int FunB(const string & strTest)

int FunA(const int &m) // 引用传递
main(){
    int a = 1;
    FunA(a);
}
这里实际上是初始化int &m = a; m代表的是a,改变m指向的内存数据就是改变a指向的内存数据
代码块
PlainText

复制

int FunA(const int &m) // 引用传递
main(){
    int a = 1;
    FunA(a);
}
这里实际上是初始化int &m = a; m代表的是a,改变m指向的内存数据就是改变a指向的内存数据
int FunA(const int &m) // 引用传递 main(){ int a = 1; FunA(a); } 这里实际上是初始化int &m = a; m代表的是a,改变m指向的内存数据就是改变a指向的内存数据
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-11-05 00:39:30  更:2022-11-05 00:42:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 21:50:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码