framework
resource
//resource
@NonNull
public String getString(@StringRes int id) throws NotFoundException {
return getText(id).toString();
}
@NonNull public CharSequence getText(@StringRes int id) throws NotFoundException {
CharSequence res = mResourcesImpl.getAssets().getResourceText(id);
if (res != null) {
return res;
}
throw new NotFoundException("String resource ID #0x"
+ Integer.toHexString(id));
}
//AssetManager
@UnsupportedAppUsage
@Nullable CharSequence getResourceText(@StringRes int resId) {
synchronized (this) {
final TypedValue outValue = mValue;
if (getResourceValue(resId, 0, outValue, true)) {
return outValue.coerceToString();
}
return null;
}
}
// Primitive resource native methods.
private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
@NonNull TypedValue outValue, boolean resolveReferences);
问题来了,既然resource下的资源直接来自assetmanager,那么为什么不直接存在assetmanager里呢?
1,res下资源会自动生成id
2,xml可以通过id直接访问res下资源
什么时候创建的contextImpl
//ActivityThread
performLaunchActivity{
ContextImpl appContext = createBaseContextForActivity(r);
}
static ContextImpl createActivityContext{
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ContextParams.EMPTY,
attributionTag, null, activityInfo.splitName, activityToken, null, 0, classLoader,
null);
//设置resource
context.setResources(resourcesManager.createBaseTokenResources(activityToken,
packageInfo.getResDir(), //目录
splitDirs,
packageInfo.getOverlayDirs(),
packageInfo.getOverlayPaths(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
classLoader,
packageInfo.getApplication() == null ? null
: packageInfo.getApplication().getResources().getLoaders()));
}
ResourcesManager
private @Nullable ResourcesImpl createResourcesImpl{
final AssetManager assets = createAssetManager(key, apkSupplier);
}
private @Nullable AssetManager createAssetManager{
final AssetManager.Builder builder = new AssetManager.Builder();
final ArrayList apkKeys = extractApkKeys(key);
for (int i = 0, n = apkKeys.size(); i < n; i++) {
final ApkKey apkKey = apkKeys.get(i);
//吧资源添加进去,添加进个集合里,这有点像pathlist里的dexElements了
//如果是这样,那我我们的也添加进这个目录里,会不会能直接使用id加载到插件的资源
builder.addApkAssets(
(apkSupplier != null) ? apkSupplier.load(apkKey) : loadApkAssets(apkKey));
}
return builder.build();
}
资源id格式是什么样子的
0x7f010001,0x7f010002
0x7f020001,0x7f020002
0x7f0f0001,0x7f0f0002
0x7f 这个其实app的id,统一都是0x7f开头的
01,02~1f(目前最多到11)都是 都类型id,比如anim为01,xml为11
后面的000x就是你在对应资源类型里的排名啦,这个有点区别,他是从0开始,不是1开始
依照这种规则,那插件话肯定资源冲突呀,怎么解决资源冲突
先叉开问一个问题,插件话dexElements 先在加载宿主,还在加载插件
注意,插件化是先加载宿主的,因为我们肯定不希望插件影响宿主原来的功能
但热修复是先加载插件的,所以要根据业务场景,区分插件是放在前面还是后面加载
最简单办法,放两个文件夹就行了
好了,插件怎么解决资源冲突,根据下面观察,要么资源id不同,要么获取资源的类不同,即你有你的resourse,我有我的
1, 资源id不同
这个通过aapt的方式在编译期吧前面的7f改成70~7e,最好不要变成6,因为系统资源在下面
然后就用宿主的resource.assets.addAssetpath即可 (好像改为setApkAssets了,直接设置集合)
至于修改前缀id的地方就在resourcetable.cpp
2,用不同类去加载,你加载你的,我返回的
1,生成一个自己的 assetmanager(addassetpath自己的apk目录)和resource
2,在插件的baseactiviy(不是全部啊)里复写getresource方法,返回我们自己的resource
千万注意你插件的resource不能影响宿主 (千万注意,这个只能是针对Activity,针对AppcompatActivity的在后面)
还有个小细节,我们需要通过原来的resouce获取一些display之类的属性,传入到新的resource里,获取原来的resource必须application的,不然会循环获取导致崩溃(就是你context.getresource,不是又掉回baseAct的getresource啦),同时也会内存泄漏,这里也看出act context和application的区别,长生命周期用application
打包流程
1,先把对应的java文件生成出来,比如aapt生成r.java,aidl生java文件
2,javac 编译成class文件
3,dx 将我们的class文件和三方的资源库里的一些文件打包成dex
4,apkbuilder dex+ resource+其他资源生成apk文件
5,sign签名
6,zipalign ,apk文件优化 4字节对齐,对齐 (1)以后运行更快,mmap (2)没对齐的话内存消耗更大
viewgroup findviewbyid
首先 view.findviewbyid 就是和自己的id做比对
所以vp的findviewbyid 就是遍历自己和子view的findviewbyid做比对
进行插件话资源加载的时候AppCompatActivity会有什么问题
这个其实属于非系统统一,大家都有用,都打进包了,classloader机制只在加载第一个,导致的id与插件资源不匹配
1,根据我们dex学到的,我们代码里所继承的AppCompatActivity 只有一个,要么是宿主,要么是插件,我们假设都是宿主的
2,我在代码代码里写的R.id.xx其实在编译期就变成0x7f....的固定数字了
3,那在AppCompatActivity 的AppCompatDelegateImpl 会加载很多资源,这些资源已经是宿主类的id了,在我们自己提供的resource可能都找不到(为什么activity没问题,activity加载decorView那些也有id呀,其实很简单,因为Activity再frameowork大家都用的一样的,运行时从手机获取,AppCompatActivity 不是,这个是打进我们apk里的)
解决办法,getresource还是用用宿主的,保证appcompat id对应 获取成功,只有我们自己的资源采用自己的resource,这里涉及一个事情,就是setContextView(id),AppcompatAct会用默认用宿主context进行layoutInflater ,而前面说了这个context 实现的assetmanager是指向宿主,并不是 自身apk的,因此我们要直接走setContextView(view),view是通过我们自己的resource获取
问题又来了,如何让LayoutInflater指向插件自己的资源呢,其实就吧context里的resource里的assetManager所指向的路径进行修改即可,所以我们要有自己的assetmanager,自己的resource,自己的ContextImpl,自己的ContextThemeWrapper
1,我们生成新的assetmanager
2,我们生成新的resource
3, 我们生成新的ContextImpl
5,我们生成新的ContextThemeWrapper
6,然后直接setContentView(LayoutInflater.from(ContextThemeWrapper).inflate(R.layout.xxx,null))
view的id 是怎么获取到的
解析xml获取到的,在编译期就吧xml id资源换成对应resource.arsc里的id号了
如
在oncreate里调用onDestory会走onStart onStop吗
测试下来都不会直接oncreate - > onDestory
SurfaceView
简单用法就是在 surfaceCreated 获取一个 canvas 进行绘制
在surfaceDestroyed 进行至空
GlSurfaceView 继承surfaceview
SurfaceView的一种用法,帮我们初始化了EGL 一些东西
EGL是什么,一套opengl的api,用来原声设备系统进行通信
TextureView 继承 View
持有SurfaceTexture,需要在硬件加速的情况下使用
热修复
类修复的解决方案肯定是重启app才能生效,因为classloader加载过老的,不会加载新的了
andfix :修改artMethod 属性
roBust:美团的框架,抖音在用,这个原来就是用字节码插桩,每个类给你生成一个静态变量接口,假设是A ,你所有方法执行的时候,他都判断A 是不是为空,为空执行你自己的方法,不为空执行 吧你的对象,方法,参数传入A,
你要做的重写接口,并进行对应的操作
tinker:bsdiff,pathdiff,主要原理,通过比较俩个dex文件区别生成补丁包
bsdiff 1.dex 2.dex patch 会生成差异包patch
bspatch 1.dex 2.dex patch 将1.dex 和patch生成为2.dex
这个除了可以做热修复还可以做增量更新,直接比较apk文件
loadapk类的重要作用
他在里面调用ApplicationLoader那个类创建了我们的程序自己的classLoader对象,也就是PathClassLoader,然后ActivityThread 也就是系统类的classLoader传做parent,这个classLoader是bootClassLoader
okhttp
1,支持http2.0,http2.0支持 头部压缩,多路复用,主动推送
http1.x因为是基于文本的,只能串行顺序发送
http2.0 是二进制数据帧,有顺序标识,因此可以并行发送
2,默认开启keep-alive,这个是在header的connection字段里,如果你请求头里connection = keep-alive,服务器返回的也是connection = keep-alive,那么长链接建立成功,如果服务器返回connection = close就不行
3,默认开启gzip
4, 默认不开启缓存,但可以在okhttp builder 时候设置cache目录打开
驱动及文件
linux一切文件
面试官技巧
问你原理的时候,你可以说解决了什么工程问题
比如问你binder原理,你要提到使用mmap解决了什么工程问题
binder 怎么设置回调
类似applicationThread
设置一个aidl接口进去
package com.qx.wz.aidl;
import com.qx.wz.aidl.PlayCallbak;
interface Player{
void start();
void stop();
void setCallBack(PlayCallbak callback);
}
package com.qx.wz.aidl;
interface PlayCallbak{
void startSuccess();
void stopSuccess();
void error();
}
//客户端
bindService(intent,object :ServiceConnection{
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val playCallbak = Player.Stub.asInterface(service);
playCallbak.setCallBack(object :PlayCallbak.Stub(){
override fun startSuccess() {
//这个好奇怪,我试了下,运行在server里肯定是非主线程
//但在server里如果不重新开线程回调,这边接到是主线程接到
//调用线程是binder xxx
//如果新开个额外的线程,这边回调就会在异步里
Log.d("wwwwwwwww","startSuccess:"+Thread.currentThread())
}
override fun stopSuccess() {
Log.d("wwwwwwwww","stopSuccess")
}
override fun error() {
Log.d("wwwwwwwww","error")
}
})
playCallbak.start()
}
// service那边
@Override
public IBinder onBind(Intent intent) {
return new PlayImpl();
}
class PlayImpl extends Player.Stub{
private PlayCallbak callbak;
@Override
public void start() throws RemoteException {
if (callbak!=null){
callbak.startSuccess();
}
}
@Override
public void stop() throws RemoteException {
if (callbak!=null){
callbak.stopSuccess();
}
}
@Override
public void setCallBack(PlayCallbak callback) throws RemoteException {
this.callbak = callback;
}
}