Android面试-高端技术题

2018-03-08  本文已影响0人  济公大将

本系列出于AWeiLoveAndroid的分享,在此感谢,再结合自身经验查漏补缺,完善答案。以成系统。


图片

图片占用内存.jpeg

http://www.cnblogs.com/guanmanman/p/6922678.html

Glide源码解析一
Glide源码解析二

https://blog.csdn.net/abrazen_zz/article/details/52562153
Glide ARGB565 Picasso ARGB8888
Glide 之加载ImageView大小的 Picasso 加载整张
Glide asGif Picasso 没有gif功能
Glide 源码 > Picasso
Glide 是纯HttpUrlConnection Picasso 如果有Okhttp会用Okhttp

接口-加载 显示
加载 内存--本地--网络--本地--内存
显示 显示规则,是否压缩 显示后回收

android 滚动 拖拽

Scroller源码
VelocityTracker分析
ScrollView源码

https://www.jianshu.com/p/07d717ef0b28

MD学习资料大全

网络和安全机制

网络引擎+自由切换网络请求框架

oom

http://www.cnblogs.com/dolphin0520/p/3919839.html
单一职责,开闭,理式替换,依赖倒置,接口隔离,迪米特法则

https://github.com/jigongdajiang/DesinMode

https://blog.csdn.net/qzcsu/article/details/72861891

https://blog.csdn.net/leewccc/article/details/70225610

https://www.cnblogs.com/ranyonsue/p/5984001.html

报文格式.jpeg

https://www.jianshu.com/p/52d86558ca57
1.1 长连接,多站点,断电续传,

image

https://www.cnblogs.com/alisecurity/p/5939336.html

https://www.cnblogs.com/chengdabelief/p/6883569.html

是HTML5一种新的协议。它实现了浏览器与服务器全双工通信

https://blog.csdn.net/wwd0501/article/details/54582912
Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。
WebSocket

1.防止恶意替换
2.系统强制要求

https://blog.csdn.net/ljheee/article/details/53191397

https://blog.csdn.net/gan303/article/details/50669035
https://blog.csdn.net/zy00000000001/article/details/51956883
安装应用启动应用会解析apk(parsePackage),这时候会解析清单配置文件中的<uses-permission>标签,解析完后会调用updatePermissionsLPw进行权限设定,然后进行grantPermissionsLPw授权,授权的本质是为应用程序授予权限名对应的组id,拥有组id的应用就拥有了相应的权限。

数据库

/**
 * 方法1:检查某表列是否存在
 * @param db
 * @param tableName 表名
 * @param columnName 列名
 * @return
 */
private static boolean checkColumnExist1(SQLiteDatabase db, String tableName
        , String columnName) {
    boolean result = false ;
    Cursor cursor = null ;
    try{
        //查询一行
        cursor = db.rawQuery( "SELECT * FROM " + tableName + " LIMIT 0", null );
        result = cursor != null && cursor.getColumnIndex(columnName) != -1 ;
    }catch (Exception e){
        LogUtil.logErrorMessage("checkColumnExists1..." + e.getMessage());
    }finally{
        if(null != cursor && !cursor.isClosed()){
            cursor.close() ;
        }
    }

    return result ;
}
//如果不存在再执行修改表的语句
alert table ‘tableName’ add ‘clumName’ clumType 

https://www.jianshu.com/p/8287873d97cd

https://blog.csdn.net/u014608640/article/details/52511310
索引 事务 单线程池 其它(StringBulider,记录cloumIndex,少字段查询)

https://blog.csdn.net/it_talk/article/details/47124645
利用事务的原子特性,保证所有的SQL能全部执行完成。主要思路是:首先将原来的表进行改名称rename table(临时表),接着创建新的表create table,再者将旧表内的数据迁移到新表内,最后drop table删除临时表。

算法

https://blog.csdn.net/happy_wu/article/details/51841244

平均时间复杂度是快速排序最快

private static void bubleSort(int[] a){
        int n = a.length;
        int j,k;
        int flag = n;//记录最后交换的位置
        while (flag > 0){//排序未结束标志
            k = flag;// 来记录遍历的尾界
            flag = 0;
            for(j=1;j<k;j++){
                if(a[j-1] > a[j]){
                    //交换
                    int temp;
                    temp = a[j -1];
                    a[j-1] = a[j];
                    a[j] = temp;
                    flag = j;
                }
            }
        }
    }
//算出中轴
    private static int partition(int[] num,int left,int right){
        if(num==null || num.length<=0 || left<0 || right>=num.length){
            return 0;
        }
        int prio=num[left];   //获取数组中间元素的下标
        while (left<right){                 //从两端交替向中间扫描
            //从右往左扫
            while (left<right && num[right]>=prio)
                right--;
            swap(num,left,right);
            //从左往右扫
            while (left<right && num[left]<=prio)
                left++;
            swap(num,left,right);
        }
        return left;
    }
    //交换
    private static void swap(int[] num,int left,int right){
        int temp = num[left];
        num[left] = num[right];
        num[right] = temp;
    }
    //快速排序
    private static void  sort(int num[],int left,int right){
        if (left<right){
            int index=partition(num,left,right); //算出枢轴值
            sort(num,left,index-1);       //对低子表递归排序
            sort(num,index+1,right);        //对高子表递归排序
        }
    }

http://my.csdn.net/uploads/201207/19/1342700879_2982.jpg

https://blog.csdn.net/u014430697/article/details/49968495

插件化、模块化、组件化、热修复、增量更新、Gradle

https://blog.csdn.net/jiangwei0910410003/article/details/48104581

  1. 在android的Dalivk的definClass方法被阉割
  2. DexClassLoader 和 PathClassLoader都是双亲委派模型的本质是没有重写loadclass方法,里面有这么一行
    clazz = parent.loadClass(className, false);
    
  3. 动态加载的本质是使用DexClassLoader和PathClassLoader的loadClass方法进行加载。两者的区别是构造函数所体现的,DexCLassLoader能将源路径文件(可能是apk,jar,zip)进行解压得到dex文件,然后进行进行加载,而PathClassLoader只能直接通过dex文件进行加载,在android中默认的是PathClassLoader,因为apk在安装后会自动解压出我们要用的dex文件存放在/data/dalvik-cache中。
  4. 插件化一般由三个工程 插件接口工程,插件工程和宿主工程。这三的关系如下图


    image

    这里要注意一点就是宿主工程通过libs的方式集成接口工程,而插件工程一定不能通过libs的方式集成接口工程

  5. 资源的动态加载,关键是反射操控AssetManager的addAssetPath将资源的路径改为插件的,然后用这个AssetManager对象去创建自己的Resources对象,我们代码中就是用这个自己的Resources对象去加载资源。
  6. Activity的动态加载,一个重要的问题是保证能正常走生命周期,主要有三种种思路
    a. 通过反射得到ActivityThread中的mPackages,然后通过mPackages得到应用的LoadApk对象,然后将LoadApk对象中的mClassLoader对象替换成我们自己定义的关联了系统PathClassLoader的且加载了插件的DexClassLoader对象。但是这种方式必须要预先在清单配置文件中生命好要加载的插件中的Activity。
    b. 通过反射去将DexClassLoader中的class合并到应用PathClassLoader中的elements数组中
    b. 静态代理的方式,应用中提供一个ProxActivity,插件工程中提供一个只有生命周期壳子的Activity,这个Activity持有一个我们的代理Activity,在代理Activity通过反射去获取插件工程的Activity,然后将此代理给插件Activity,并在代理的各个方法中取调用插件的Activity的方法。

https://www.jianshu.com/p/e61a4d10e122
《深入探索Android热修复技术原理7.3Q.pdf》
主要分两种级别,一种是方法修复,代表Andfix,原理是改变虚拟机中方法的索引指向。一种是类级别修复,利用的是类的动态加载中改变应用BootClassLoader中的elements数组,代表是Tinker

  • Sophix 修复方法
    本质是native层整体替换ArtMethod。这个方法的局限是修改不能有方法和字段的增减,以及不能修复反射调用的非静态方法。局限场景如下
    1. 外部类访问内部类的private,或者内部类访问外部类的private,本质是编译时会为private增加一个access**方法。导致方法数增加,解决方法,就是有内部类是双方不使用private修饰属性和方法
    2. 匿名内部类的前插入和删除。原因是匿名内部类编译会按顺序生成一个带有外部类名和序号的名字。
    3. 不支持<clinit>修复,注意 final static 的 基本类型和 String不会被收集到<clinit>方法中
    4. 混淆导致的方法内联(没用的,简单的,只有一个地方用的会被内联)和裁剪(声明了参数,但是没有用该参数会被裁剪)。会影响最终的方法数。解决方案是在配置混淆文件是加上-dontoptimize参数
    5. switch case 连续为packed_switch 不连续 spare_switch ,资源id为packed_switch 这样会导致不完全,所以解决方案就是反编译将packed_switch指令改为spare_switch指令。
    6. 泛型的桥接(重写泛型父类方法时编译器自动生成的一个桥接方法,以此来解决泛型的类型擦除而导致与多态冲突的问题)会导致方法数增加
    7. Lambda 会引起方法数的增加和减少,原因是Lambda会将函数式接口编译成一个静态内部方法,同时还会生成一个新的辅助类
  • Sophix冷启动修复方案:
    davlik虚拟机全量Dex方法(去掉原来变动的class,这里不是真删而是移除定义的入口)
    art虚拟机 补丁为classes.dex 改原来的为classes1.dex...
  • Sophix资源修复方案:
    增加一个packageId为0x66的纯补丁资源包,配合代码层id的引用实现资源的修复。在AssetManager上则是通过析构,重新初始化,重新增加path的方式保证不用到处替换原有的AssetManager
  • Sophix的so库修复方案


    so修复方案.jpeg

https://www.cnblogs.com/yyangblog/p/6249715.html

https://blog.csdn.net/jsqfengbao/article/details/52103439

https://www.cnblogs.com/baronzhang/p/6861258.html

https://blog.csdn.net/guiying712/article/details/55213884

https://blog.csdn.net/zheng548/article/details/54864765

build.jpeg
image

架构设计和设计模式

https://segmentfault.com/p/1210000008721369/read

  • 六大设计原则
    1. 一个类只负责一个职责-单一职责
    2. 基类引用可以透明的换为子类对象-理式替换
    3. 抽象不依赖细节,细节依赖抽象-依赖倒置
    4. 客户端不应该依赖其不需要的接口-接口隔离
    5. 最少知道-迪米特法则
    6. 对扩展开发,对修改关闭-开闭原则
框架模式.jpeg

https://www.cnblogs.com/chentingk/p/6497107.html

    //核心属性定义
    private volatile boolean isRunning = true;
    private BlockingQueue<PCData> queue;// 内存缓冲区
    private static AtomicInteger count = new AtomicInteger();// 总数 原子操作

https://blog.csdn.net/u012401711/article/details/52475327

https://blog.csdn.net/zhang31jian/article/details/50538000

https://blog.csdn.net/zxt0601/article/details/61614799

https://blog.csdn.net/beyond_liyy/article/details/52273740

  • 本地
    abd shell am start -W com.gjg/com.gjg.SplashActivity
    totaltime 进程 和 Activity的时间 不包含pause时间
  • 线上
    Application attachBaseContext 起点 Activity的onWindowFocusChanged终点

性能优化

https://www.cnblogs.com/tester-l/p/6045524.html

https://blog.csdn.net/u011240877/article/details/54347396

https://juejin.im/post/5874bff0128fe1006b443fa0

UncaughtExceptionHandler

NDK、jni、Binder、AIDL、进程通信有关

framework层、ROM定制、Ubuntu、Linux之类的问题

上一篇 下一篇

猜你喜欢

热点阅读