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.

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android Framework是指Android操作系统中提供的一套API和工具集合,可以帮助开发者快速构建Android应用程序。底开发主要涉及到Android Framework的源代码和相关文档,本质上是深入了解Android操作系统原理和架构,具有较高的技术门槛。 探索Android Framework开发可以帮助开发者更好地理解Android的运行原理,提高代码质量和效率。具体来说,需要掌握以下几个方面: 1. Java编程语言Android开发是基于Java的,需要掌握Java的语法和开发环境。 2. Android系统架构:了解Android系统的四架构是必要的,包括应用、框架、系统运行库和Linux内核。 3. Android Framework的API:掌握常用的Android Framework的API,如Activity、Service、BroadcastReceiver等。 4. Android框架源代码:深入了解Android框架的源码实现,可以帮助开发者更好地掌握Android系统的工作机制和运行原理。 5. Android开发工具:熟练掌握Android Studio等开发工具,可以提高Android开发的效率和质量。 总之,探索Android Framework开发是一个深入了解Android操作系统原理和架构的过程,需要不断学习和实践才能不断提高自己的技术水平。 ### 回答2: Android开发需要具备一定的计算机基础知识和编程技能。Android 应用程序由 Android FrameworkAndroid 应用程序组成,Android Framework 包含了很多核心组件和类库,例如 Activity、Service 和 ContentProvider 等。 若要深入探究 Android Framework,我们需要从底的 Linux 内核开始了解。因为 Android 基于 Linux 内核设计,所以我们需要了解 Linux 内核的结构和原理,熟悉 Linux 的命令行操作。接着,我们需要掌握 Java 编程语言的基础知识,了解 Android 的四大组件和应用开发模式。 此外,我们还需要了解 Android Framework 最核心部分的 Binder 服务。Binder 运行在 Linux 内核空间中,用于在 Android 应用程序Android Framework 之间传递信息。理解 Binder 的运作原理将对深入理解 Android Framework 有很大帮助。 探索 Android Framework开发需要不断学习和实践。建议参考 Android 官方文档、查阅相关书籍和博客,积极参与开源社区和论坛,不断提高自己的技能和水平。只有积累了足够的经验和技能,才能够开发出高质量的 Android 应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值