2022-03-05

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;

        }
    }

你可能感兴趣的:(2022-03-05)