1.Android 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)
-
点App图标,Launcher进程从ServiceManage里面获得AMS服务,然后触发startActivity.
-
如果没有App进程,则Zygote进程fork出App进程, 触发ActivityThread的main函数 -> new ActivityThead() -> at.attach()
-
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方法。 -
AMS间接触发**appThread.scheduleLaunchActivity(ActivityInfo info)**** (在at.attachApplication的时候,AMS会获得appThread,注意:appThread(IApplicationThread.stub)是activityThread的内部类)
-
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 关键方法
- loadAllApps(); // 加载手机中所有的app信息; List
1>. 在launcher的onCreate方法中会创建loadTask, 会获取pmi,进而获取List<ResolveInfo>
mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
mLauncherApps.getActivityList(packageName=null,xxx)
1>.获取到 PackageManagerInternal(PackageManagerInternalImpl,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进程会做两件事情:
- 解析init.rc, 启动关键服务(BootAnimation);
- 读取开机动画的解压包,播放帧动画;
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;
- bool类型的值为true或false;
- 定义数组必须至少给出元素个数;
- 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()
- 静态成员变量:进程级生命周期,不属于对象(不占对象的内存), 属于类(通过类访问)
class Person {
static int a;
}
使用:
Person::a = 100; // 类访问
person.a = 200; //对象访问 a被该类所有对象共享
-
静态成员函数, 属于类 可通过类和对象访问
-
常见的头文件
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的不同之处
-
结构体、联合体、枚举在定义变量的时候可以省略struct、union、enum;
结构体中可以定义函数,且在函数内部访问结构体变量不需要 . 或者 ->;
enum是独立类型,和整形之间不可隐式转换(C中就可隐式转换); -
C++有重载,C没有;
-
类型转换
// 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 = 0;
int& foo() {
return ouhou; // 省去了内存的开销
}
// 引用常左值(const修饰的左值),同样的 如果给右值给别名 则用const关键字;
// const类型的引用成为万能引用;可引用非常左值、常左值以及右值;
const int e = 10;
const 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 B: public 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 X: virtual public A { xxx }
直接访问A里面的成员,不会又歧义:
z.m_a = 100;
3.7 多态(利用虚函数)
- 基类类型的指针(即使指向子类对象),只能调用基类的普通成员函数。
- 如果这个指针调用子类特有的方法,则报错;
- 但如果这个指针使指向子类对象,调用的是虚函数,则调用子类的虚函数;
多态的触发:
(1)基类必须要有虚函数,子类必须提供覆盖版本;
(2)必须利用 基类类型指针( 必须指向子类对象 ) 调用 虚函数;
必须利用 基类类型引用( 必须引用子类对象 ) 调用 虚函数;
多态的结果:
最终调用的为 子类覆盖版本虚函数,而非基类原始版本虚函数;
注意:在基类的构造/析构函数中调用虚函数,不会触发多态;应为子类构造中调用基类构造的对象是基类;
class Circle:public 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包:
- framework-res.apk:android系统资源库
- framework.jar:android的sdk中核心代码
- 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服务器文章来源:https://www.toymoban.com/news/detail-764208.html
SystemServer中创建出WMS、IMS,然后serviceManager.addService(Context.WINDOW_SERVICE, wms) // 就是把binder添加到serviceManager.文章来源地址https://www.toymoban.com/news/detail-764208.html
到了这里,关于Android Framework层开发的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!