Android Framework层开发

1.Android Framework基础

Framework
查看源码工具:SourceInsight
在这里插入图片描述
Instrumentation:可以理解为ActivityThread的一个工具类,在ActivityThread中初始化,一个进程只存在一个Instrumentation对象,在每个Activity初始化时,会通过Activity的Attach方法,将该引用传递给Activity。Activity所有生命周期的方法都有该类来执行。

1.1 系统启动流程:

Init进程(pid=1): 启动和守护系统的关键/核心服务(父pid=1,比如start ServiceManager/SurfaceFlinger/Zygote) :杀不死的服务(打电话,发短信,小米商城等卸载不了的,init的子进程(ServiceManager、SurfaceFlinger、Zygote))
Zygote进程(App-process,2377):通过解析init.rc(任务清单)来启动Zygote进程(启动所有App进程;Zygote是native进程-因为入口函数是C语言),Zygote会开启一个serverSocket(c/s,串行)和别的进程通信,为了降低开启App进程效率(打开10个App,开辟内存100->10G); 作用:加载系统资源, 开启App进程;
-Xzygote /system/bin --zygote --start-system-server
SystemServer(1457):Zygote进程fork出SystemServer进程(这是个java进程),并开启(startBootstrapServices)好多服务(AMS,PMS,WMS)

PMS(运行在开机的时候): 包管理相关操作,

  • 1>.遍历包 /data/app文件夹(scanDirLi 遍历并确定是不是Apk -> 获得路径给第二步);
  • 2>.解压apk fun scanPackageTracedLi -> (ParallelPackageParse.java类操作.parsePackage ->loadApkIntoAssertManager -> 获得cookie给第三步)
  • 3.> DOM解析 AndroidManifest.xml (fun openXmlResourceParser(cookie,MANAFEST_NAME)) -> 最终会得到一个Package对象,里面会包含四大组件的ArrayList<ActivityInfo -> bean对象>集合,并将这个Package对象放到缓存中(ArrayMap<String包名,Package>),以便查找 ; => 提供查询结果;

AMS:Activity管理操作(启动和生命周期的管理):

  • 1. AMS的startActivity会从PMS中获得package信息
    • 1.1 AMS的startActivity方法中 -> 创建ActivityStarter.class对象,并调用execute()方法 -> 创建 ActivityStackSupervisor.class 调用resolveIntent(intent, ) 从而返回ResolveInfo;
    • 1.2 resolveIntent方法中具体细节: 通过ams.getPackageManagerInternal -> PackageManagerInternalImpl(PMS的内部类)-> packageManager.resolveIntent -> 获得package后进一步获得ResolveInfo (包含ActivityInfo,serviceInfo…)
    • 1.3 ActivityStarter中处触发mSupervisor.resolveActivity() (把rInfo转activityInfo)
    • 1.4 activityStarter中调用.startActivity( appInfo) -> resource
  • 2. AMS间接触发appThread.scheduleLaunchActivity(ActivityInfo info) (在at.attachApplication的时候,AMS会获得appThread,注意:appThread是activityThread的内部类)
    • 2.1 AT: sendMessage(case: LaunchActivity) -> handleMessage
  • 3. AT: handleLaunchActivity(activityClientRecord, intent) 被触发在handleMessage中,activityClientRecord是来自ams传过来的信息;
    • 3.1 利用反射从r.activityInfo创建activity对象(instrumentation来搞)
    • 3.2 AT: activity.attach(appContext) 再此会new PhoneWindow() 和构建windowManager管理对象;
    • 3.3 AT: instrumentation.callActivityCreate() (然后把activity对象封装成ActivityClientRecord对象(r.activity = activity),并将其添加到缓存。at.activities: ArrayMap<IBinder, ActivityClientRecord这是个activity的包装类>,记录打开过的Activity. )
    • 3.4 Instrumentation: performCreate() -> activity.onCreate()

1.2 APP首次启动过程(二次启动走startActivity)

  1. 点App图标,Launcher进程从ServiceManage里面获得AMS服务,然后触发startActivity.

  2. 如果没有App进程,则Zygote进程fork出App进程, 触发ActivityThread的main函数 -> new ActivityThead() -> at.attach()

  3. App进程中获得AMS本地代理(AT: ActivityManager.getService()),触发amsProxy.attachApplication(创建application,attachBaseContext 和realStartActivity);
    细节别管
    -> AT: amsProxy.attachApplication(appThread) 其中appThread是IApplicationThread.stub. 为了AMS和APP通信。
    -> AMS: appThread.bindApplication(progressName, appinfo,… ) // AMS拿到appInfo给App进程,然后这些信息组装成一个appBindData -> AT: sendMessage(BIND_APPLICATION, appBindData) -> AT: handleMessage(case Bind_Application) -> AT: activityThread.handleBindApplication(appBindData) (创建app: makeApplication -> app.attachBaseContext -> installContentProvider -> 执行Application的生命周期函数:callApplictionOnCreate 就是这后三步耗时) 最终创建出Application 而且走到application的onCreate方法。

  4. AMS间接触发**appThread.scheduleLaunchActivity(ActivityInfo info)**** (在at.attachApplication的时候,AMS会获得appThread,注意:appThread(IApplicationThread.stub)是activityThread的内部类)

  5. AT: handleLaunchActivity(activityClientRecord, customIntent) (具体加AMS那一部分)

上面四部过程中都是黑白屏(fork app进程之前)的状态 -> 解决方案:给appTheme为一张带有app logo的图片(addSplashscreenContent只能是drawable,aos12之后可以是一个动画);在splashActivity显示的时候,黑白屏终止;
@drawable/icon
广告页(splashActivity):预加载数据;

启动优化:
在attachBaseContext中搞事情:加载dex文件(热修复,加固)
在installContentProvider中搞事情:有的第三方leakCanary写了contentProvider;
在app的onCreate中优化:减少第三方SDK 启动,或者异步启动
在这里插入图片描述

在这里插入图片描述

1.4 WMS

在这里插入图片描述
窗口管理,窗口动画,输入系统中转站和Surface 管理. 如窗口创建、绘制、更新、响应事件等
WindowManagerGlobal 就是专门和WMS通信的,类似于appThread和AMS通信。

WMS是管理所有App的PhoneWindow的,例如:addView,removeView, updateViewLayout;
WindowManagerGlobal(单例,一个App中只有一个): 缓存App中所有的DecorView,给wms提供服务,wms也只和WMG联系;
会通过getWindowManagerService 拿到WMS的binder:sWindowManagerService:IWindowManager.Stub.asInterface

1>. 在startActivity中:AT: activity.attach(appContext) 的方法里会构建phoneWindow和windowManagerImpl:
2>. View和window的关联
handleResumeActivity中拿到 PhoneWindow和Decor还有wm:WindowManagerImpl(这个里面会持有Activity的phoneWindow)
然后wm.addView(decor); 而WindowManagerImpl里面有global的单例;
因为global要把这个view丢给WMS, 然后wms调用底层来渲染;
global.addView(xxxz) 只干一件事 就是给view(DecorView)一个ViewRootImpl(处理onMesure、 onLayout、 onDraw )
root.setView(xxx)
mWindowSession.addToDisplay(xxx) // Session类是继承于IWindowSession.Stub;
mService.addWindow(xxxx) // mService = wms , addWindow后面会调用WMS的WindowState操作,最后会交给SurfaceFling进行完成。

3>. 绘制
Root: requestLayout() -> scheduleTraversals() ->
mChoreographer.postCallBack (runnalbe) 监听vSync信号,有信号则触发runnable方法:
runnable里面只搞了一件事: doTraversal() // 这个就是真正绘制
->performTraversals()
-> perfromLayout()这个方法里面会就找到DecorView 然后遍历所有view调用onMesure、onLayout、onDraw; 会调用seesion进行绘制。

注意:root中会拿到session,就是surfaceFlinger的本地代理binder
Global:global.getWindowSession( )返回 IWindowSession.stub 就是SF的本地代理的binder。
IWindowSession.stub里面有个属性是SurfaceSession
, 就是surfaceFlinger的surfaceSession
里面其实是:sWindowSession: wmsProxy.openSession()

在这里插入图片描述

1.3 AMS和PMS的作用:

1.3.1 Hook AMS实现集中式登录

Hook点一般在静态变量处(利用反射搞一搞):
通过Hook(反射的方式)拿到AMS的本地代理Proxy,然后去操作AMS里面流程,让Activity跳到自己想要的页面;

1.3.2 Hook PMS实现插件化

Activity/广播 打包-> Apk -> 利用发射拿到PackageParse.java来解压apk解码xml -> Package -> ArrayList<ActivityInfo/receiversInfo> -> 类加载loadClass(activityName).newInstance; -> 注册即可 -> 然后在宿主工程中就可以用了;

1.4. Launcher3定制

SystemServer会启动Launcher

1.4.1 启动流程

1>.systemServer-> startOtherService -> AMS的systemReady函数中调用aTaskManager.startHomeOnAllDisplay(), 此方法中PMS供过Intent获得activityInfo.

Intent(intentAction = Intent.ACTION_MAIN, category = Intent.CATEGORY_HOME)

2>.AMS调用ams.getActivitystartController().startHomeActivity(homeIntent, aInfo) 去开启launcher.

1.4.1 关键方法

  1. loadAllApps(); // 加载手机中所有的app信息; List
1>. 在launcher的onCreate方法中会创建loadTask, 会获取pmi,进而获取List<ResolveInfo>
mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
mLauncherApps.getActivityList(packageName=null,xxx)
1>.获取到 PackageManagerInternalPackageManagerInternalImpl,PMS的内部类)
LocalService.getService(PackageManagerInternal.class)
2>.List<ResolveInfo> apps = pmi.queryInterntActivities(intent=null,xxx)
ResolveInfo(包含ActivityInfo,serviceInfo...3>. 在loadTask中有个回调OnUpdateListener, 会调用rebindAdapter对数据进行填充绑定;
4>. 在viewHolder中会创建点击事件,进而调用startAppShortcutOrInforActivity(), 最终触发Activity的startActivity方法;---->AMS开启Activity的流程;

Launcher负责桌面滑动,App拖动和卸载之类的;
DragLayer: 处理拖拽事件;
ShotcutsInfo: 里面存放图标和标题信息;
AppWidgetProvider extends BroadcastReceiver : 小组件(日期)

1.5. SystemUI

1.5.1 SystemUI概述

SystemUI路径:framework/base/package/systemUI/
SystemServer会启动这个SystemUI的服务:

在startOtherServices中会启动SystemUI.
在这里插入图片描述

SystemUIService中的onCreate方法中:
// 这个里面就会添加systemUIServiceComponents
(SystemUIApplication getApplication()).startServicesIfNeeded();

1.5.2 SystemUI包含范围:

(1)StatusBars
(2)Navigation bars (底部导航)
(3)Notification (左上边下拉)
(4)Lockscreen keyguard_bouncer.xml
(5)Quick settings (右上边下拉之后,打开关闭Wifi的页面)
(6)Recents:Overview(recent task switcher)
(7)VolumeUI (音量大小UI)VolumeDialogControllerImpl去操作 AudioManager, IAudioService
(8)PowerUI (长按关机键的时候弹出的UI)
(9)ToastUI
(10)KeyboardUI

这些功能的类都继承SystemUI这个抽象类,并重写start方法;
在这里插入图片描述

1.6. 开机动画(BootAnimation进程)

开机后init进程会做两件事情:

  1. 解析init.rc, 启动关键服务(BootAnimation);
  2. 读取开机动画的解压包,播放帧动画;

1.7 MediaCodic 编解码

编码:摄像头获取的视频(Camera)/录屏视频(MediaProjectionManager) -> 编码成H254;
拍摄出视频的数据格式(采样格式YUV420SP, 陪一个色度) 可细分为:NV21(只有android手机是这样, Y在前VU交叉排列) 、NV12 (Y在前UV交叉排列)

mediaCodec = MediaCodec.createEncoderByType("Video/AVC");  
MediaFormat mediaFormat = MediaFormat.createVideoFormat(type, width, height);  
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000);  
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);  
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);  
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);  
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);  
mediaCodec.start(); 

数据输入输出:MediaCodec使用ByteBuffer作为输入输出数据的载体,通过configure()方法配置输入输出格式,然后通过queueInputBuffer()方法将输入数据放入队列中,经过编码后,输出数据会被放入输出队列中,通过dequeueOutputBuffer()方法取出。

硬解码: 指的是系统将flv(举个例子)文件分离成H.264(video/avc)视频数据流和aac音频数据流,然后再将H.264视频数据流转交
给DSP芯片进行处理,DSP将处理好的一帧帧画面转交给GPU/CPU然后显示在屏幕上(surfaceview),这就是视频硬解码的过程。

FFmpeg其中包含了先进的音视频解码库 libavcodec 和音视频格式转换库 libavformat。libavfilter滤镜。

2. C(Java是C的一层封装)

2.1 概述

安装mingw,配置环境变量,即可用 gcc(类似Java的JVM)命令编译c文件;
写C的工具:CodeBlocks
预处理指令:#include <stdio.h> 预处理器会对其处理
条件编译指令: #if #else #elif #endif
宏定义:#define PI 3.14
可视化输入: scanf(“%d”, &a)
C是没有GC的!C的碎片管理是依托于OS的碎片整理机制

2.2 数据类型

在这里插入图片描述

// 变量:直接定义的数据会存放在栈里面
int a = 1// 常量:用const修饰
const int a = 10// 字符串
char *pstr = "hello"; // psre保存的是字符串的首地址
char str[] = "hello"char str[] = {
    
    'h','o', '\0'}; // 要有结束符号

2.2.1 指针变量(地址不可更改)

指针:变量的地址;
取地址运算符:&
解引用运算符: * ; *p 就表示 指针变量指向的p变量;
指针运算只支持±,且步长是指针变量的步长;

2.2.1.1 指针在数组中的使用
int arr[] = {
    
    1, 2 , 3 , 4 , 5} // 数组必须初始化, 这个写法int arr[]; 就是错误的
sizeof(arr)/sizeof(arr[0])  // 数组长度


int *p = arr;   // 等价于 int *p = &(arr[0]),  定义的时候 * 表示指针的关键字,使用的时候* 表示取地址中的变量;
那么 arr就是p 都表示第一个元素的地址;p是放地址的,占8个字节,且p只做加减运算;

// 二维数组
typedef char (*PTR_TO_ARR)[30]; // 数组指针
char str[3][30] = {
    
    
    "http://c.biancheng.net",
    "C语言中文网",
    "C-Language"
};
PTR_TO_ARR parr = str;
取值: *(parr+1)  的值就是"C语言中文网",
2.2.1.2 函数指针

类似于数组的指针,C语言中直接获取函数名,就可以得到这个函数的函数指针。
函数指针就是个指针,指向这个函数的内存;
作用:用函数指针可以调用函数;(函数指针可以作为函数参数 就像回调函数-类似闭包)

void test(int a){
    
    
}
test;//这就是一个函数指针,它的类型是void(*)(int)

void test1(int a){
    
    
	printf("测试\n");
}
int main(){
    
    
    //这里将void(*)(int)类型的函数指针重命名为P
	typedef void(*P)(int);
    //并将void(*)(int)类型的函数指针test1赋值给p(初始化)
	P p = test1;  	<=>  P p1 = &test1;
    //调用函数操作
	p(1);   <=> (*p)(1);
2.2.1.3 指针函数

指针函数,说的就是函数,函数的特征点就是返回值为指针。
无法返回局部变量的地址 (因为函数执行完 这个变量就被释放了),可以给局部变量加static修饰;
作用:可以通过指针函数 访问代码顺序后面定义的 变量;

int* getPpos(int pos, int (*pstu)[4]) //第二形参为一个数组指针
{
    
    
	int *p;
	p = *(pstu+pos); //由于pstu为面向父数组的指针,为了解决p和pstu类型不匹配,所以进行*取得面向子数组的指针
	return p; //返回一个指向子数组的指针,即地址
}
2.2.1.4 数组指针

指向数组的指针;

int a = {
    
    6,7,8}
int (*p)[3] = a; // p指向的是个数组;且指向的是三个int元素的数组; p+1 就会加3x4个字节

int a[3][4] = {
    
    1, …… 12}int (*p)[4] = a; // 一个数组指针 指向了4个int元素的数组
2.2.1.4 指针数组

数组中全是指针;

2.2.2 结构体(里面只可以包含变量)和枚举

2.2.2.1 结构体

Struct自定义类型;相当于面向对象中的类:
strcpy可将常量字符串进行copy.
在这里插入图片描述

struct  Student *p = 0; *p 表示student内存中的第一个地址。
访问: *p.name = "Jonathan";    strcpy(p->name, "Lewis")   

2.2.2.2 枚举
enum COLOR {
    
    RED, BLUE};
则: RED = 0, BLUE = 1

2.3 动态内存分配(存放在堆中)

必须包含头文件: #include <stdlib.h>
在这里插入图片描述

1. free释放动态空间
free(p);

2. malloc表示申请一个num个字节的动态内存
3. realloc表示重新申请动态内存; 第一个参数表示起始空间,方便copy里面的内容
4. calloc 表示申请n个num个字节的动态内存;

2.4 文件读写

2.4.1 文本模式进行读写

在这里插入图片描述
在这里插入图片描述
读写操作
在这里插入图片描述

2.4.2 二进制模式进行读写

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3. C++(面相对象)

注意:任何基本类型的数据,都可以隐式转为bool;
在这里插入图片描述

  1. bool类型的值为true或false;
  2. 定义数组必须至少给出元素个数;
  3. string类对象:
string s1("str"); // 定义s1 并调用s1.string("str")
string s2 = s1;   // 定义s2 并调用s2.string(s1)
string s3(s1);    // 定义s3 并调用s3.string(s1)
string s4 = "str" // 定义匿名对象,并调用 匿名对象.string("str")
string s5;       // 定义s5 并调用s5.string()
  1. 静态成员变量:进程级生命周期,不属于对象(不占对象的内存), 属于类(通过类访问)
class Person {
    
    
  static int a;
}

使用:
Person::a = 100; // 类访问
person.a = 200; //对象访问 a被该类所有对象共享
  1. 静态成员函数, 属于类 可通过类和对象访问

  2. 常见的头文件
    cstdio —> stdio.h
    cstdlib —> stdlib.h
    cstring —> string.h

3.1 概述

C++的编译环境是G++(类似C的GCC),如果在C++的函数前加extern “C”,那么此函数会在GCC中编译;
C++在C的基础上映入class和面向对象 和java写法一样:
C++中空类会占1个字节;
this表示当前对象,是一个指针,调用属性必须用箭头:this->name = “lewis”;

#include <stdio.h> 
//通过class关键字类定义类 
class Student{
    
     
  public:
  //类包含的变量 
    char *name; 
    int age; 
    float score; 
    //类包含的函数 
    void say(){
    
    
      printf("%s的年龄是 %d,成绩是 %f\n", name, age, score); 
    }
};

int main(){
    
    
  //通过类来定义变量,即创建对象
  class Student stu1; //也可以省略关键字class //为类的成员变量赋值
  stu1.name = "小明";
  stu1.say();
  return 0;
}

3.1.0 C++和C的不同之处

  1. 结构体、联合体、枚举在定义变量的时候可以省略struct、union、enum;
    结构体中可以定义函数,且在函数内部访问结构体变量不需要 . 或者 ->;
    enum是独立类型,和整形之间不可隐式转换(C中就可隐式转换);

  2. C++有重载,C没有;

  3. 类型转换

// 1.任何基本类型之间 都可以隐式转换
int a; double b;
a = b;

// 2.显示转换(强转),除了void *之外,其它类型之间的转换必须强转;
C:(目标类型)变量
C++: 目标类型(变量)

// 3.静态类型转换(隐式类型转换的逆转换|自定义类型转换)就是温和转换:
//编译器先看法方向能不能隐式转换,能转则转,不能转就失败(可惜的是:除了void *之外,其它类型之间的转换必须强转);
static_cast<目标类型>(变量)
void* pv = &a;
int* pa = static_cast<int*>(pv);

// 4.常类型转换(去除指针或者引用上的const属性)
const_cast<目标类型>(变量)
const int* cpa = pa;
pa = const_cast<int*>(cpa);

// 5.重解释类型转换:
reinterpret_cast<目标类型>(变量)
就是任意类型指针之间的转换或者引用之间的转换;
任意类型的指针和整数之间的转换;
pa = reinterpret_cast<int*>(pb);

// 6.动态类型转换:虚函数

// 7.自定义转换:转换构造函数

3.1.1 命名空间

C++有命名空间(方便管理),双冒号来访问命名空间中的类容:
C++内置库的命名空间是 std;

namespace aaa {
    
    
 int a;
}

int main() {
    
    
aaa:: a = 10
}

3.1.2 关于类、new和this

// new
book b0; // 会给b0分配内存空间,且里面属性值是随机数; 会调用b0的构造函数;
book b = * new book();   // new book()  表示new出来一块地址,并返回一个指针(地址),所以加*取值;
b.name = "C++"

book* a = new book();  // 类指针
a->name = "C++" // 必须得用指针操作符号

// this (当前类的指针)
代码区: 就是函数,只读常量区;
数据区:初始化的全局变量和静态变量;
BBS区:未初始化的全局变量和静态变量;
堆区:动态内存分配区域;// new 出来的对象就保存在这里; 
栈区:局部变量,函数参数及返回值;// 用定义语句生成的对象放在栈区;

对象和函数:
非常对象优先选择非常函数,也可选择常函数;
而常对象只能选择常函数;

定义栈中对象: Person p;  Person p(10,20);
 Person p[2];  Person p[2] = {
    
     Person(10,20), Person(1,10)}

定义堆中对象:Person *p = new Person; Person* p = new Person(10,20); delete 对象指针;
Person* p= new Person[2]; Person* p = new Person[2]{
    
    Person(10,20), Person(1,10)}
delete[] 对象数组指针;

3.1.3 动态(堆)内存分配

动态分配内存:用new/delete操作符(好处就是可以给初始值)来分配和释放内存,代替C中的malloc/free函数;

int *pi = new int; // *pi初始值为0

int *pi = new int(1); // *pi初始值为1
delete pi;

int* pi = new int[2]{
    
    1, 2};
delete[] pi;

int(*p)[4] = new int[3][4]; // new 会返回第一个元素的地址,就是指向一个一维数组的地址;
delete[] p;

// 注意不能delete已经释放过内存的对象,但释放空指针(p = NULL)是安全的;

3.1.4 类type_info

typeid操作符,可以获取对象的类型信息;
typeid操作符,无法获取对象本身的常属性信息;
在这里插入图片描述
typeid操作符,实现了==和!=;

const int n;

typeid(m) == typeid(n); // 返回true;

3.2 引用(就是一块内存别名)

左值:能用&取地址; 右值:不能用&取地址,比如函数返回值、常量,右值占得内存是匿名内存;

引用必须初始化(指针可以不用初始化),且不能更换目标,且没有引用的引用;
引用不占内存;
常用: const int& a; // 常引用->万能引用,可引用左值或者右值;

// **引用就是内存的别名,对引用的操作,就是对原来地址的操作**
int a = 10// a就是一块内存,内存可以取地址,所以a是左值; 非常左值:不是常数的左值;
int& b = a; // &前面有类型,就表示b是个引用,b就是个别名;b就是a的别名; 注意**引用不占内存**;
const int& c = a; // 别名可以先定的更加严格;

// 作用1:可作为形参,避免复制的开销
void swichBei(const int& a, int& b) {
    
     // 常引用参数可防止对实参进行修改 而且还可接受常量行实参;
 int c = a;
 a = b;
 b = c;
}

// 作用2:可作为返回值(不能返回局部变量的引用,因为局部变量在作用域外就被释放了)
int ouhou = 0int& foo() {
    
    
  return ouhou; // 省去了内存的开销
}


// 引用常左值(const修饰的左值),同样的 如果给右值给别名 则用const关键字;
// const类型的引用成为万能引用;可引用非常左值、常左值以及右值;
const int e = 10const int& g = e; // 引用常属性必须和目标的常属性一致; 


foo() = 10//则10其实就是赋给ouhou;

C语言中 只要涉及到数据的传递(例如:初始化、赋值、传参、返回值),都是值传递(将数据复制一份给别人).

  int main( void ) {
          int a = 10; // 初始化
          a = 20; // 赋值
          foo( a ); // 传参
          /*| 800 |*/ bar( ); // 返回值
   }

C++语言 因为有了 引用这个语法特性,所以在C++程序中可以不做值传递。

void swap(int& x, int&y) { // 给实参起了两个别名。操作x,y就等于操作实参,没有copy操作,效率高;
  int z = y;
  y = x;
  x = z;
}
// 当然函数里面不需要改变实参,加上const即可 const int&b;
int& bar(){
    
    
  static int sValue = 0; // 在类加载的时候就会被加载。且只会执行一次,不是每次调用bar函数都回执行;
  cout << sValue << endl;
  return sValue;
}

bar() = 200;
bar(); // 此时sValue = 200; 

注意:不能返回局部变量的别名,出函数被销毁了;

3.3 函数

3.3.1 哑元函数

参数只有类型,无形参;
目的:向下兼容, 之前需要传递数据,后面不用传递了 也能实现功能;

void foo (int) {  // 不能获取形参数据  }


int main(void) {
  foo(10)
  return 0;
}

3.3.2 默认参数的函数

只需注意:只能在函数声明的时候给默认参数;

3.3.3 内联(inline)函数

内联函数运行策略和宏一样, 是编译器的一种优化方式(防止函数调用发生的跳转 带来的时间开销);
作用:调内联函数,直接将二进制代码放到调用的地方,类似宏的调用,省去函数跳转;
简单频繁调用的函数适合做成内联函数;

// C中的宏定义
#define MAX(X, Y) (X>Y ? X : Y )
// 内联函数
inline void ouHou() {
    
    }

3.3.4 常函数

就是在函数体之前加个const,表示默认的this被const修饰;

void getInfo(/* const Integer* this */) const {
    
    }

3.3.5 友元函数

写法(位置不限):在函数前加friend关键字(授权这个函数可以访问当前类的私有成员变量), 且以参数的方式指出这个函数归谁所有;
作用:破坏封装,共享私有属性,可以让函数在定义域之外来实现,函数内部不能使用this,但可以直接使用定义域里面的变量;
注意:不止可以友元全局函数,还可以友元里一个类的函数,甚至可以友元一个类; 且友元函数不隶属于授权类,不能用授权类的this调用。

class Amount{
    
    
private:
    double total,rate;
public :
    Amount(double t,double r)
    {
    
    
        total = t;
        rate = r;
    }
    // 友元函数,这个函数(全局函数)一般在这个类的外面,里面也就没必要设为friend了;
    friend void test(Amount &a)};

void test(Amount &a)
{
    
    
    std::cout << a.rate << std::endl;
}

// 在main中直接调用
Amount a(1.0, 1.0);
test(a);

3.3.5 虚(virtual)函数(继承体系下 重写的问题)

写法:普通函数之前加个virtual
如果一个类有虚函数,在此类中就会有一个指针(8个字节),这个指针并指向这个类对应的虚函数表;
子类继承父类的时候,会将父类的虚函数表copy一份,并放到子类里面;但如果子类重写了父类的虚方法(子类的这个方法也是虚函数,无论有无virtual),则子类虚表里面放的是子类的方法;
多继承情况下,继承几个,就开辟几个虚表;

在这里插入图片描述

#include <iostream>
using namespace std;

class Animal
{
    
    
private:
    int height;
public:
    //抽象方法
    virtual void say()  //虚函数在基类中可以有定义
    {
    
    
        cout << "i am animal"<<endl;
    }
    virtual void run() = 0;  //纯虚函数在基类中无定义,表示子类**必须**实现此方法
};

class Cat :public Animal
{
    
    
private:
    int color;
public:
    void say()  //重写虚函数
    {
    
    
        cout << "i am a cat"<<endl;
    }
    void run()  //必须重写纯虚函数
    {
    
    
        cout << "cat run" << endl;
    }
};

下图中Bse为父类,里面有个int变量,child1为子类,里面有个int child1变量;且各有三个虚函数;
在这里插入图片描述
如果子类重写了父类的f()方法,则子类的虚函数表中只会存自己的函数:
在这里插入图片描述

3.3.5.1 纯虚函数/抽象方法

形式: virtual void foo() = 0
拥有纯虚函数的类称为纯虚类、接口。

3.3.5.2 动态类型转换(运行时转换)

作用:将基类类型的指针或者引用转为其子类类型的指针或者引用;
而子类类型的指针或者引用可以直接转为基类类型的指针或者引用;

条件:基类必须有个虚函数;

class Bpublic A {
    
    };

B b;
A* pa = &b;
B* pb = dynamic_cast<B*>(pa); // pa->B对象的内存空间->虚表指针->B的虚函数表->B

3.4 类和对象

在这里插入图片描述
在这里插入图片描述
类的缺省访问控制属性为私有 private;
结构体的缺省访问控制属性为公有public;

3.4.1 常对象

被const修饰的对象、对象指针或者对象的引用,都称为常对象;

const User user;
const User* cptr = &user;
const User& cref = user;

3.4.2 类中方法的声明和实现

在这里插入图片描述

3.5 构造函数和析构函数

构造函数:开辟空间,初始化数据;
析构函数:释放空间,释放空间的同时,提供一个特定位置,对于class内部数据进行释放(手动!);main函数调用结束之后才会调用析构函数;

3.4.1 构造函数

构造函数无返回值,默认有个无参构造(里面会初始化成员变量(默认值随机),不以程序员的意志为转移),一旦写一个构造函数,将不自动提供午餐构造;
常规构造: Test t; (调用无参构造) Test t(10); (调用有参构造) Test t = *new Test();
在这里插入图片描述

Human h3[];
Human h4[4] = {
    
     Human(), Human(),Human(),Human() } // 大括号里面是匿名对象;

在这里插入图片描述

Human* h = new Human[3];
delete[] h;
h = NULL;

Human* h = new Human[3]{
    
     Human(), Human(), Human()};
3.4.1.1 拷贝构造

拷贝构造(对象克隆):默认会有一个拷贝构造函数;
作用:利用一个已经定义的对象,来定义其同类型的副本对象;

  • 对于指针类型成员变量 只是浅拷贝(只拷贝地址 不拷贝指针指向的内容);
Human(const Human& that): m_age(that.m_age){
    
    
  // 【int m_age = that.m_age】 定义m_age, 并赋初始值 
}

Human s3(s2);
3.4.1.2 拷贝赋值函数

拷贝赋值函数

void operator = (Human that) {
    
     xxx }

String s3; 
s3 =s2; // 拷贝赋值// s3.operator = (s2)
3.4.1.3 初始化表

作用:给基

  • 本数据类型的成员变量赋初始值;
  • 类中的**常量型(const)引用型(int&)**成员变量,必须在初始化表中显示初始化,因为二者定义的时候就必须被初始化;
  • 初始化成员变量的顺序是按照成员变量声明的顺序,而不是初始化表中的顺序;

有了初始化表,成员变量的初始值直接就被赋值,而不是随机数;
在这里插入图片描述

3.4.1.3 类型转换构造函数

作用:
利用一个已经存在的对象,构造一个不同类型的对象;
实现原类型到目标类型的隐式类型转换的目的;

// 形式
class 目标类型{
    
    
   目标类型(const 原类型& src) {
    
     …… }
}
// 例子
class Cat {
    
    
  xxx
  private:
  string m_name;
  friend class Dog; // 目的是为了在Dog中访问Cat的成员变量;
};

// 其中explicit表示需要显示转换
explicit Dog(const Cat& that): m_name(that.m_name) {
    
     xx }

Dog dog = (Dog)cat; // 定义匿名的dog对象,然后利用匿名的dog对象.Dog(cat) -> 出发类型转换构造函数;

3.4.2 析构函数

写法 ‘~类名(){}’,无返回值,五参数
调用时机:在销毁对象之前的一刻将自动调用,且只会被调用一次;

  • 对象离开作用域,针对栈对象;
  • delete操作符,针对堆对象;
    作用销毁对象的各个成员变量;如果自定义析构函数,系统在自定义析构里面还会销毁各个成员变量;
// 类默认会有一个默认的析构函数:
- 对基本数据变量,什么也不做;
- 对类类型的成员变量,调用相应的析构函数;
- 销毁对象的各个成员变量;
~Person() {
    
    
  // 对于类类型成员变量会调用其析构函数
  delete p;
  // 系统在此还会销毁person的各个成员变量;
}

注意:
通常情况,对象没有持有动态分配的资源(new 出来的对象),可以不定义析构函数。自带的就帮忙帮事情搞定了;

3.4.2.1 虚析构函数

写法:virtual ~A(){ }
作用:基类的析构函数是虚函数,当delete一个基类类型的指针(指向子类对象),那么实际调用了子类的析构函数;(子类对象先释放自己的成员,然后里面自动调用基类的析构,完美释放)
一句话,就是为了调到子类析构;
在这里插入图片描述

3.4.3 static

事实上,类的静态成员变量和静态成员函数,更象是普通的全局变量和全局函数,只是多了一层类作用域和访问控制限定符的约束,相当于具有成员访问属性的全局变量和全局函数。

在这里插入图片描述

在这里插入图片描述
单例设计步骤(使用之前学过的知识):
第1,6步:让(拷贝)构造方法私有化,不让用户定义对象;
第2(声明),3(定义)部:给一个静态的成员变量;
第4,5部:定义个方法,获取上面的静态成员变量;
在这里插入图片描述
在这里插入图片描述

3.6 继承封装

3.5.1 继承(多继承)

格式:class Lewis : public Person, public Function { } //其中public为访问控制符的重新标记;
继承方式:公有继承public, 保护继承protected, 私有继承private;

  • 当利用子类对象,在类外面访问父类的成员的时候,编译器就要查看相应子类的重新标记;
  • 而在子类内部访问父类成员的时候,不用看访问控制符的重新标记,而看直接父类的访问控制标记;

继承的访问控制标记决定访问的上限,因此下面的main函数中,d就无法访问bar()和hum()
在这里插入图片描述

3.5.2 子类访问父类方法

(1) 只有在公有继承下,子类对象在类外可以访问基类的公有成员,其他继承不可以
 d.Base:foo();
 // 在子类内部调用父类方法:Parent::method(paramlist); 
(2) 只有再公有继承下,子类类型的指针或引用  可以和  基类类型的指针或引用 进行类型转换(其他继承不可以)
  Human* ph = &s; // 子类类型指针 可以 隐式转换为 基类类型指针
  Human& rh = s;   // 子类类型引用 可以 隐式转换为 基类类型引用
  // 以上两行代码,编译器认为访问范围缩小,是安全的(向上造型)

3.5.3 子类析构方法

在这里插入图片描述
其中执行析构代码,是程序员自定义析构函数中的代码;而后面两步是系统帮忙调用的;

3.5.4 钻石继承

一个子类继承多个基类,这些基类又源自同个祖先;
在这里插入图片描述
Z中会有多个A的对象;
可在外部这样访问:

z.Y::m_a = 100

在这里插入图片描述

3.5.4 虚继承 (解决钻石继承的问题)

写法: 在中间子类加virtual

class Xvirtual public A {
    
     xxx }

直接访问A里面的成员,不会又歧义:
z.m_a = 100;

在这里插入图片描述

3.7 多态(利用虚函数)

  • 基类类型的指针(即使指向子类对象),只能调用基类的普通成员函数。
  • 如果这个指针调用子类特有的方法,则报错;
  • 但如果这个指针使指向子类对象,调用的是虚函数,则调用子类的虚函数;

多态的触发:
(1)基类必须要有虚函数,子类必须提供覆盖版本;
(2)必须利用 基类类型指针( 必须指向子类对象 ) 调用 虚函数;
必须利用 基类类型引用( 必须引用子类对象 ) 调用 虚函数;
多态的结果:
最终调用的为 子类覆盖版本虚函数,而非基类原始版本虚函数;

注意:在基类的构造/析构函数中调用虚函数,不会触发多态;应为子类构造中调用基类构造的对象是基类;

class Circlepublic Shap {
    
      xx  }

Shap* s = &c;
s.draw(); // 如果draw是虚函数,则会调用c中的draw方法,否则调用s中的draw方法。

3.8 运算符重载

就是自定义运算符作用效果:
在这里插入图片描述

#include<iostream>
using namespace std;
//加号运算重载
class Person
{
    
    
public:
    //1、成员函数重载+号
    // 注意:单目运算符左右值均可为常或者非常,返回结果为右值
    Person operator+(const Person& p) const
    {
    
    
        Person temp;
        temp.m_a=this->m_a+p.m_a;
        temp.m_b=this->m_b+p.m_b;
        return temp;
    }


    // 2.赋值类双目运算符的左值不能为常左值;返回结果为自身
    Person& operator+=(/* Person* this */ const Person& p) // 这个const不能加,应为双目运算左值不能为常左值
    {
    
    
        xxxxx
        return *this; // 所以不能给this加const;
    }
    // 3.比较累双目操作符> < >= <= ,其左右值均可为常或者非常;返回结果为bool
    bool operator==(/* Person* this */ const Person& p) const
    {
    
    
        xxxxx
        return *this; // 所以不能给this加const;
    }
    
    //4.输出操作符 返回值为cout自身;
    ostream& operator<<(ostream& os, const Person& that) {
    
    
        os << "ouHou" << endlp;
    }
    // 使用: cout << person << endl;

    //5. 前++,-- 操作非常左值,返回自身;
    Person& operator++(){
    
      this->m_age +=1 ; return &this;  }
    
    //6.小括号操作符
    int operator()(int x, int y){
    
    
      return x>y ? x: y;
    }

    int m_a;
    int m_b;
};
void test01()
{
    
    
    Person p1;
    p1.m_a=10;
    p1.m_b=10;
    Person p2;
    p2.m_a=10;
    p2.m_b=10;
    Person p3=p1+p2;
}
int main()
{
    
    
    test01();
    return 0;
}

这几个运算符不能重载:
在这里插入图片描述

3.8.1 智能指针

在这里插入图片描述
// 常规指针:new出来的对象,需要手动调用delete,才能触发析构,释放堆内存;
在这里插入图片描述
自定义智能指针类(就是加了两个操作函数):
在这里插入图片描述
标准库提供的智能指针:#include
auto_ptr

auto_ptr<A> a(new A);   // 这样不用调用A的delete,只要出了A的作用域,就可自动调用;
a->foo();

auto_ptr<A> a1 = a; 
a1->foo(); // a就失效了

3.8.2 类型转换操作符函数

作用:将类类型转为基本类型;基本类型->类类型,需要用类型转换构造;

private:
  int m_i;
// 注意 类型转换操作符函数 无参无返回值,但是有return;
operator int(/* const Integer* this  */) const{
    
     
  return this->m_i;
}

// 使用
int m = integer; // 类型转换操作符函数就会被调用

3.9 String类

在这里插入图片描述

如果在做初始化,并且“=”两边的类型完全一致,那么=xxx 和(xxx)无差别;
如果在做赋值,并且“=”两边类型完全一致,那么将触发operator=函数的调用;
无论是初始化 还是 赋值 , 只要“=”两边的类型不一致,编译器将“=”右边的类型 转换为和“=”左边的类型一致;

3.10 异常处理

如果程序中 没有捕获异常的代码,一旦异常被抛出,最终将被操作系统捕获,操作系统将我们程序杀死;
如果程序中 有捕获异常的代码,一旦异常被抛出,层层退出右花括号,直到异常被捕获为止,程序回归正常流程;

在这里插入图片描述

void foo( int x ) {
    
    
    if( x>=0 && x<=10 ) {
    
    
          ....
    } else {
    
    
         throw invalid_argument();
    }
}

int main( ){
    
    
    try {
    
    
         foo( 11 ); 
    }
    catch( ... ) {
    
     // 表示忽略异常,捕获但不处理;
    }
    catch( ... ) {
    
    
    }
    catch( ... ) {
    
    
    }
}

建议:抛出匿名临时对象 A();
catch用引用对象;避免拷贝;

3.10.1 异常说明

写在:在函数体前面: throw(int , double)
在这里插入图片描述
注意:没有异常说明,则表示可以抛出各种异常;

3.11 IO流

输入流:输入设备流向内存对象: cin >> student;
输出流:内存对象流向输出设备(显示器);
缓冲区:介于IO设备和内存对象之间的内存缓冲区;键盘输入的内容先到键盘缓冲区,按回车,则到输入缓冲区,再通过流操作符>>, 进入内存对象;当先显示器输出时,先通过流操作符<<从内存对象进入输出流缓冲区,直到缓冲区满或者遇到换行符,才将其中的数据灌注到显示器;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

C++标准库封装的 流对象(例如:cout/cin…),允许我们 将其放置在bool上下文中,可以实时判断 IO操作(打开操作/读操作/写操作等)是否成功;

class ifstream {
    
    
public:
     ifstream( const char* path, ... ) {
    
    
          m_f = open( path, ... );
          if( m_f == -1 ) 
               m_state = ios::failbit; // 4
          else
               m_state = ios::goodbit; // 0
     }
     operator bool( ) const {
    
    
            return m_state==0;
      }
      ifstream& operator>>(int/double/float/string..... data ) {
    
     // 有大量的operator>>函数,形参都不同,相互之间重载关系
            if( m_state != ios::goodbit ) 
                  return ...;
            int ret = read(m_f, ....);
            if( ret == -1 ) 
                   m_state = ios::failbit; // 4
            else
                   m_state = ios::goodbit; //0
      }
private:
      int m_f; // 保存文件描述符
      int m_state; // 保存状态值
};
// 以上代码模拟C++标准库中的ifstream类
// --------------------------------------------
// 以下代码模拟用户
int main( void ) {
    
    
      ifstream ifs2("./file", ios::ate); // 定义ifs2,利用 ifs2.ifstream("./file", ios::ate)
      if( !ifs2 ) {
    
     // ! ifs2.operator bool()
          cerr << "ifs2流对象状态错误--打开文件失败" << endl;
      }   
      int ii; double dd;  string ss1,ss2;
      ifs2 >> ii >> dd >> ss1 >> ss2; // ifs2.operator>>(ii).operator>>(dd).operator>>(ss1).operator>>(ss2)
      if( !ifs2 ) {
    
     // ! ifs2.operator bool()
          cout << "ifs2流对象状态错误--读文件失败" << endl;
      }   
}

3.12 函数模板(写类型通用的代码)

3.12.1 宏解决类型通用问题

// 缺点:丧失了对数据类型的检查
#define Max(x, y) (x>y ? x :y)

// 宏+预处理器
#define MAX(T) T max_##T(T x, T y) {
      
      \
           return x>y?x:y;\
         }
MAX(int) // 宏实例化: 预处理器生成函数 int max_int(int x, int y) { xxx }
cout << max_int(x,1) << endl;

3.12.2 模版函数

在这里插入图片描述
在这里插入图片描述

3.12.3 函数模版隐式推断类型

在这里插入图片描述

3.13 类模版

在这里插入图片描述
类模版使用的时候必须实例化:
在这里插入图片描述

3.13 STL

3.13.1 智能指针

// this (当前类的指针)
代码区: 就是函数,只读常量区;
数据区:初始化的全局变量和静态变量;
BBS区:未初始化的全局变量和静态变量;
堆区:动态内存分配区域;// new 出来的对象就保存在这里;需要手动释放;
栈区:局部变量,函数参数及返回值;// 用定义语句生成的对象放在栈区; C++分配和回收;

问题:堆内存的释放; 方案:智能指针;
在这里插入图片描述

// 0. auto_Str被弃用
    std::auto_ptr<string> p1(new string("data1"));
    std::auto_ptr<string> p2;
    p2 = p1;    //编译器认为合法,但后续对p1继续使用,程序运行时出错,因为p1不再指向有效数据。
    
// 1. unique_ptr的使用
    //初始化方式1
    std::unique_ptr<int> up1(new int(123));
    //初始化方式2
    std::unique_ptr<int> up2;
    up2.reset(new int(123));
    
    //初始化方式3:官方推荐的声明方式 和new int无区别,只是不想看到new
    std::unique_ptr<int> up3 = std::make_unique<int>(123);
        
    //保证唯一
    std::unique_ptr<string> p3(new string("data2"));
    std::unique_ptr<string> p4;
    p4 = p3;    // 编译器认为非法,避免p3不再指向有效数据问题。 用move来解决
      
    //unique_ptr主要核心目的是为了确保数据的唯一性
    //nullptr 是否是空指针
    //move主要的目的是要保证数据只有一份,清空原来的,将数据保存到下一个!
    std::unique_ptr<int> up1(std::make_unique<int>(123));
    std::unique_ptr<int> up2(std::move(up1));  //通过移动实现了复制操作,就相当于:up2 = up1;up1会被清空
    std::cout << ((up1.get() == nullptr) ? "up1 is NULL" : "up1 is not NULL") << std::endl;

    std::unique_ptr<int> up3;
    up3 = std::move(up2);    //通过移动实现了复制操作 up2会被清空
    std::cout << ((up2.get() == nullptr) ? "up2 is NULL" : "up2 is not NULL") << std::endl;

// 2. shared_ptr的使用,引用计数的方案,让同一份资源共享给多个
   //初始化方式1
    std::shared_ptr<int> sp1(new int(123));
    //初始化方式2
    std::shared_ptr<int> sp2;
    sp2.reset(new int(123));
    //初始化方式3
    std::shared_ptr<int> sp3 = std::make_shared<int>(123);   //make_shared 去初始化;

        // 拷贝构造
        std::shared_ptr<A> sp2(sp1);
        std::cout << "use count: " << sp1.use_count() << std::endl;
        
        //主动释放SP2的所有引用计数!
        sp2.reset();
        std::cout << "use count: " << sp1.use_count() << std::endl;

        {
    
    
            std::shared_ptr<A> sp3 = sp1;
            std::cout << "use count: " << sp1.use_count() << std::endl;
        } // 走出这个大括号  引用计数-1

        std::cout << "use count: " << sp1.use_count() << std::endl;

// 3.weak_ptr: 和shared_ptr一样,只是少了引用计数的操作;引用计数一直是1
    //创建一个std::shared_ptr对象
    std::shared_ptr<int> sp1(new int(123));
    std::cout << "use count: " << sp1.use_count() << std::endl;

    //通过构造函数得到一个std::weak_ptr对象
    std::weak_ptr<int> sp2(sp1);
    std::cout << "use count: " << sp1.use_count() << std::endl;

    //通过赋值运算符得到一个std::weak_ptr对象
    std::weak_ptr<int> sp3 = sp1;
    std::cout << "use count: " << sp1.use_count() << std::endl;

    //通过一个std::weak_ptr对象得到另外一个std::weak_ptr对象,防止循环引用(互相引用)
    std::weak_ptr<int> sp4 = sp2;
    std::cout << "use count: " << sp1.use_count() << std::endl;

3.13.1 STL之线程管理

使用线程需要导包:#include <pthread.h>
//线程信息
pthread_t tid;
// 功能 线程
// 创建 pthread_create()
// 退出 pthread_exit()
// 等待 pthread_join()
// 取消 pthread_cancel()
// 获取ID pthread_self()
// 调度策略 SCHED_OTHER、SCHED_FIFO、SCHED_RR
// 通信机制 信号、信号量、互斥锁、读写锁、条件变量

//函数指针去指定要运行的代码位置
void* MyThreadStrat(void* arg)
{
    
    
    // 现成分离的操作:线程空间回收,当执行完这个线程;
    int err_code = pthread_detach(pthread_self());
    printf(" err_code:%d\n",err_code);
    // 两种退出方法,退出则不会打印下方内容
    pthread_exit(NULL);
    pthread_cancel(pthread_self());
    printf("MyThreadStrat :%s\n", (char*)arg);
    return NULL;
}

int main()
{
    
    
    pthread_t tid;
    void *ref;
    int ret = pthread_create(&tid, NULL, MyThreadStrat, NULL);
    
    //在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,
    // 主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,
    // 也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到pthread_join()方法了。
    pthread_join(tid,&ref);
    printf("----------ret------:%d\n",ret);
    if(ret != 0)
    {
    
    
        perror("pthread_create");
        return 0;
    }

    while(1)
    {
    
    
        printf("i am main thread\n");
        sleep(2);
    }
    return 0;
}

3.14 C++ 11标准

3.14.1 类型推导(auto关键字)

注意:类的成员变量不能类型推导;

auto i = 10; // 自动推导出类型为 int ;
int a = 100;
auto& b = a; // 自动推断类型为 int& ;

// 注意函数的形参不能自动推导;C++14 可以;

3.14.2 类型计算(比类型推断更精确)

在这里插入图片描述
在这里插入图片描述

// 标识符
int a = 10;
decltype(a) c = a; // c的类型是int,而且不是引用;

// 函数表达式,用函数的返回值作为最终计算的类型
decltype(foo(10, 10))  d = a; // d为foo方法返回值的类型;不会实际调用这个函数;

// 其它表达式
decltype(++a) e = a; // 左值: e的类型就int&, 所以e是a的别名; 
decltype(a++) f = a; // 右值; e的类型是int;

3.14.3 列表初始化

在这里插入图片描述
在这里插入图片描述

 Human h{
    
    20,"赵云"}; 等同于  Human h(20,"赵云")     // 定义h,利用h.Human(...) 
 Human{
    
    32,"刘备"};  等同于 Human(32,"刘备"); // 定义匿名Human类对象,利用匿名Human类对象.Human(...)
 Human* ph{
    
     new Human{
    
    25,"关羽"} };  等同于  Human* ph = new Human(25,"关羽")

3.14.4 Lamda表达式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

捕获值的Lambda表达式:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. JNI(Java native interface)

4.1 概述

JNI就是提供了一套Java字节码调用C/C++的解决方案的技术:
Android NDK(Native Develope Kit) 是一组允许您将 C 或 C++(“原生代码”)嵌入到 Android 应用中的工具。使用ndk-build编译生成so文件
在这里插入图片描述

4.2 Android的native工程简单介绍

在android工程中指定cmake(CMake 则是一个跨平台的编译工具,它并不会直接编译出对象,而是根据自定义的语言规则
(CMakeLists.txt)生成 对应 makefile 或 project 文件,然后再调用底层的编译)入口去编译C++代码(创建C++工程,会自动生成下面这些配置
在这里插入图片描述
在这里插入图片描述

4.3 开搞JNI

4.3.1 Java调用C (JNI接口)

在android工程中定义一个native接口,去调用C/C+的方法:
右键即可为这个JNI接口创建 JNI function:
在这里插入图片描述
在这里插入图片描述
Java可调用C代码,而不能调用C++,所以加extern “C”,C中不支持重载;

红框里面的代码解释:
1.JNIEXPORT 是个宏定义,表示开放权限,类似java的public、private等;有default和hidden两个值;
2.JNI函数名静态注册
Java_ + 包名(com.example.auto.jnitest)+ 类名(MainActivity) + 函数名(func1)
3.JNIEnv 作用(就是个转换器):
 1>.访问Java成员变量和成员方法; 可把java的类型转为C类型:char * str = env->GetStringUTFChars(data, 0); // data的类型为jstring。
把C类型转为java类型:
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str()); //返回值为jstring
 2>.获得建Java对象等。
    jclass class = env->GetObjectClass(this);
  3>.获得java属性、方法;
    env->GetMethodID(obj, "funName", "(I)V" )
    env->Callxxx
4. 第二个参数
可以是jobject类型,可以是jclass(如果Java中定义的JNI接口是static类型的时候)

动态注册

//JNI_OnLoad   java
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    
    
//    手机app   手机开机了
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
    
    
        return JNI_ERR;
    }
    jint result = RegisterNatives(env);
    return JNI_VERSION_1_6;
}

jint RegisterNatives(JNIEnv *env) {
    
    
    jclass activityClass = env->FindClass("com/maniu/jnimaniu/MainActivity");
    if (activityClass == NULL) {
    
    
        return JNI_ERR;
    }
    JNINativeMethod methods_MainActivity[] = {
    
    
            {
    
    
                "setAntiBiBCallback",
                        "(Lcom/maniu/jnimaniu/IAntiDebugCallback;)V",
                    (void *) regist
            }

    };
  return  env->RegisterNatives(activityClass,methods_MainActivity,
                         sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]));
}

void regist(JNIEnv *env, jobject thiz, jobject jCallback) {
    
    
    LOGD("--动态注册调用成功-->");
}

4.3.2 C/C++调用Java(反射)

在这里插入图片描述
在这里插入图片描述

1.1 Android系统Frameworks目录结构:

在这里插入图片描述

1.2 Android进程之间的关系:

在这里插入图片描述

1.3 主要的Jar包:

  1. framework-res.apk:android系统资源库
  2. framework.jar:android的sdk中核心代码
  3. services.jar:框架层服务端的编译后jar包
    在这里插入图片描述

1.4 通过调用堆栈查看Activity启动原理

Log.i(“test1”,“oncreate”,new Exception());
在这里插入图片描述

ActivityThread.scheduleLaunchActivity
ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
ActivityThread.performLaunchActivity(ActivityThread.java:2731)
Instrumentation.callActivityOnCreate(Instrumentation.java:1214)

1.5 打印当前展示的Activity

在这里插入图片描述
接着可用dumpsys package ‘包名’ 查看包信息

2. Framework操作

2.1 跳过启动页

在这里插入图片描述

2.2 aosp中内置app且不可卸载

系统应用就在package/apps/目录底下放着了:
在这里插入图片描述

3.开机动画

3.1 开机动画总体介绍

帧动画

OpenGL动画

4. Launcher启动专题

App进程,以及系统服务system_server进程都是由Zygote孵化的。并且所创建的进程会自动创建Java虚拟机;

4.1 Zygote启动脚本

在这里插入图片描述

Zygote fork出新的进程systemServer;
systemServer中会启动一些系统服务,还会把一些核心的服务加到binder的serviceManager中,这样才能给第三方应用提供服务;

serviceManager是跨进程调用的 binder的dns服务器

SystemServer中创建出WMS、IMS,然后serviceManager.addService(Context.WINDOW_SERVICE, wms) // 就是把binder添加到serviceManager.

猜你喜欢

转载自blog.csdn.net/weixin_41246366/article/details/131225599