宁波IT培训
美国上市IT培训机构

0574-87236644

Android面试题集(四)

  • 时间:2019-01-12 20:32
  • 发布: Open软件开发小组
  • 来源:微信公众号

如何做SQLite升级?

数据库升级增加表和删除表都不涉及数据迁移,但是修改表涉及到对原有数据进行迁移。升级的方法如下所示:

1. 将现有表命名为临时表。

2. 创建新表。

3. 将临时表的数据导入新表。

4. 删除临时表。

5. 重写

如果是跨版本数据库升级,可以由两种方式,如下所示:

1. 逐级升级,确定相邻版本与现在版本的差别,V1升级到V2,V2升级到V3,依次类推。

2. 跨级升级,确定每个版本与现在数据库的差别,为每个case编写专门升级大代码。

进程保护如何做,如何唤醒其他进程?

进程保活主要有两个思路:

1. 提升进程的优先级,降低进程被杀死的概率。

2. 拉活已经被杀死的进程。

如何提升优先级,如下所示:

监控手机锁屏事件,在屏幕锁屏时启动一个像素的Activity,在用户解锁时将Activity销毁掉,前台Activity可以将进程变成前台进程,优先级升级到最高。

如果拉活

利用广播拉活Activity。

理解序列化吗,Android为什么引入Parcelable?

所谓序列化就是将对象变成二进制流,便于存储和传输。

· Serializable是java实现的一套序列化方式,可能会触发频繁的IO操作,效率比较低,适合将对象存储到磁盘上的情况。

· Parcelable是Android提供一套序列化机制,它将序列化后的字节流写入到一个共性内存中,其他对象可以从这块共享内存中读出字节流,并反序列化成对象。因此效率比较高,适合在对象间或者进程间传递信息。

如何计算一个Bitmap占用内存的大小,怎么保证加载Bitmap不产生内存溢出?

Bitamp 占用内存大小 = 宽度像素 x (inTargetDensity / inDensity) x 高度像素 x (inTargetDensity / inDensity)x 一个像素所占的内存

注:这里inDensity表示目标图片的dpi(放在哪个资源文件夹下),inTargetDensity表示目标屏幕的dpi,所以你可以发现inDensity和inTargetDensity会对Bitmap的宽高 进行拉伸,进而改变Bitmap占用内存的大小。

Bitmap里有两个获取内存占用大小的方法。

· getByteCount():API12 加入,代表存储 Bitmap 的像素需要的最少内存。

· getAllocationByteCount():API19 加入,代表在内存中为 Bitmap 分配的内存大小,代替了 getByteCount() 方法。

在不复用 Bitmap 时,getByteCount() 和 getAllocationByteCount 返回的结果是一样的。在通过复用 Bitmap 来解码图片时,那么 getByteCount() 表示新解码图片占用内存的大 小,getAllocationByteCount() 表示被复用 Bitmap真实占用的内存大小(即 mBuffer 的长度)。

为了保证在加载Bitmap的时候不产生内存溢出,可以受用BitmapFactory进行图片压缩,主要有以下几个参数:

· BitmapFactory.Options.inPreferredConfig:将ARGB_8888改为RGB_565,改变编码方式,节约内存。

· BitmapFactory.Options.inSampleSize:缩放比例,可以参考Luban那个库,根据图片宽高计算出合适的缩放比例。

· BitmapFactory.Options.inPurgeable:让系统可以内存不足时回收内存。

Android如何在不压缩的情况下加载高清大图?

使用BitmapRegionDecoder进行布局加载。

Android里的内存缓存和磁盘缓存是怎么实现的。

内存缓存基于LruCache实现,磁盘缓存基于DiskLruCache实现。这两个类都基于Lru算法和LinkedHashMap来实现。

LRU算法可以用一句话来描述,如下所示:

LRU是Least Recently Used的缩写,最近最久未使用算法,从它的名字就可以看出,它的核心原则是如果一个数据在最近一段时间没有使用到,那么它在将来被 访问到的可能性也很小,则这类数据项会被优先淘汰掉。

LruCache的原理是利用LinkedHashMap持有对象的强引用,按照Lru算法进行对象淘汰。具体说来假设我们从表尾访问数据,在表头删除数据,当访问的数据项在链表中存在时,则将该数据项移动到表尾,否则在表尾新建一个数据项。当链表容量超过一定阈值,则移除表头的数据。

为什么会选择LinkedHashMap呢?

这跟LinkedHashMap的特性有关,LinkedHashMap的构造函数里有个布尔参数accessOrder,当它为true时,LinkedHashMap会以访问顺序为序排列元素,否则以插入顺序为序排序元素。

DiskLruCache与LruCache原理相似,只是多了一个journal文件来做磁盘文件的管理和迎神,如下所示:

.DiskLruCache

1

1

1

DIRTY 1517126350519

CLEAN 1517126350519 5325928

REMOVE 1517126350519

注:这里的缓存目录是应用的缓存目录/data/data/pckagename/cache,未root的手机可以通过以下命令进入到该目录中或者将该目录整体拷贝出来:

//进入/data/data/pckagename/cache目录adb shell

run-as com.your.packagename

cp /data/data/com.your.packagename///将/data/data/pckagename目录拷贝出来adb backup -noapk com.your.packagename

我们来分析下这个文件的内容:

· 第一行:.DiskLruCache,固定字符串。

· 第二行:1,DiskLruCache源码版本号。

· 第三行:1,App的版本号,通过open()方法传入进去的。

· 第四行:1,每个key对应几个文件,一般为1.

· 第五行:空行

· 第六行及后续行:缓存操作记录。

 第六行及后续行表示缓存操作记录,关于操作记录,我们需要了解以下三点:

1. DIRTY 表示一个entry正在被写入。写入分两种情况,如果成功会紧接着写入一行CLEAN的记录;如果失败,会增加一行REMOVE记录。注意单独只有DIRTY状态的记录是非法的。

2. 当手动调用remove(key)方法的时候也会写入一条REMOVE记录。

3. READ就是说明有一次读取的记录。

4. CLEAN的后面还记录了文件的长度,注意可能会一个key对应多个文件,那么就会有多个数字。

PathClassLoader与DexClassLoader有什么区别?

· PathClassLoader:只能加载已经安装到Android系统的APK文件,即/data/app目录,Android默认的类加载器。

· DexClassLoader:可以加载任意目录下的dex、jar、apk、zip文件。

WebView优化了解吗,如何提高WebView的加载速度?

为什么WebView加载会慢呢?

这是因为在客户端中,加载H5页面之前,需要先初始化WebView,在WebView完全初始化完成之前,后续的界面加载过程都是被阻塞的。

优化手段围绕着以下两个点进行:

1. 预加载WebView。

2. 加载WebView的同时,请求H5页面数据。

因此常见的方法是:

1. 全局WebView。

2. 客户端代理页面请求。WebView初始化完成后向客户端请求数据。

3. asset存放离线包。

除此之外还有一些其他的优化手段:

· 脚本执行慢,可以让脚本最后运行,不阻塞页面解析。

· DNS与链接慢,可以让客户端复用使用的域名与链接。

· React框架代码执行慢,可以将这部分代码拆分出来,提前进行解析。


Java和JS的相互调用怎么实现,有做过什么优化吗?

jockeyjs:https://github.com/tcoulter/jockeyjs

对协议进行统一的封装和处理。

JNI了解吗,Java与C++如何相互调用?

Java调用C++

1. Java中声明Native方法(即需要调用的本地方法)

2. 编译上述 Java源文件javac(得到 .class文件) 3。 通过 javah 命令导出JNI的头文件(.h文件)

3. 使用 Java需要交互的本地代码 实现在 Java中声明的Native方法

4. 编译.so库文件

5. 通过Java命令执行 Java程序,最终实现Java调用本地代码


C++调用Java

1. classpath路径下搜索ClassMethod这个类,并返回该类的Class对象。

2.  获取类的默认构造方法ID。

3. 查找实例方法的ID。

4. 创建该类的实例。

5. 调用对象的实例方法。

JNIEXPORT void JNICALL Java__jnilearn_AccessMethod_callJavaInstaceMethod  

(JNIEnv *env, jclass cls)  

{  

    jclass clazz = NULL;  

    jobject jobj = NULL;  

    jmethodID mid_construct = NULL;  

    jmethodID mid_instance = NULL;  

    jstring str_arg = NULL;  

    // 1、从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象  

    clazz = (*env)->FindClass(env, "/jnilearn/ClassMethod");  

    if (clazz == NULL) {  

        printf("找不到'.jnilearn.ClassMethod'这个类");  

        return;  

    }  

    // 2、获取类的默认构造方法ID  

    mid_construct = (*env)->GetMethodID(env,clazz, "<init>","()V");  

    if (mid_construct == NULL) {  

        printf("找不到默认的构造方法");  

        return;  

    }  

    // 3、查找实例方法的ID  

    mid_instance = (*env)->GetMethodID(env, clazz, "callInstanceMethod", "(Ljava/lang/String;I)V");  

    if (mid_instance == NULL) {  

        return;  

    }  

    // 4、创建该类的实例  

    jobj = (*env)->NewObject(env,clazz,mid_construct);  

    if (jobj == NULL) {  

        printf("在.jnilearn.ClassMethod类中找不到callInstanceMethod方法");  

        return;  

    }  

    // 5、调用对象的实例方法  

    str_arg = (*env)->NewStringUTF(env,"我是实例方法");  

    (*env)->CallVoidMethod(env,jobj,mid_instance,str_arg,200);  

    // 删除局部引用  

    (*env)->DeleteLocalRef(env,clazz);  

    (*env)->DeleteLocalRef(env,jobj);  

    (*env)->DeleteLocalRef(env,str_arg);  

}  

了解插件化和热修复吗,它们有什么区别,理解它们的原理吗?

· 插件化:插件化是体现在功能拆分方面的,它将某个功能独立提取出来,独立开发,独立测试,再插入到主应用中。依次来较少主应用的规模。

· 热修复:热修复是体现在bug修复方面的,它实现的是不需要重新发版和重新安装,就可以去修复已知的bug。

 利用PathClassLoader和DexClassLoader去加载与bug类同名的类,替换掉bug类,进而达到修复bug的目的,原理是在app打包的时候阻止类打上CLASS_ISPREVERIFIED标志,然后在 热修复的时候动态改变BaseDexClassLoader对象间接引用的dexElements,替换掉旧的类。

目前热修复框架主要分为两大类:

· Sophix:修改方法指针。

· Tinker:修改dex数组元素。


如何做性能优化?

1. 节制的使用Service,当启动一个Service时,系统总是倾向于保留这个Service依赖的进程,这样会造成系统资源的浪费,可以使用IntentService,执行完成任务后会自动停止。

2. 当界面不可见时释放内存,可以重写Activity的onTrimMemory()方法,然后监听TRIM_MEMORY_UI_HIDDEN这个级别,这个级别说明用户离开了页面,可以考虑释放内存和资源。

3. 避免在Bitmap浪费过多的内存,使用压缩过的图片,也可以使用Fresco等库来优化对Bitmap显示的管理。

4. 使用优化过的数据集合SparseArray代替HashMap,HashMap为每个键值都提供一个对象入口,使用SparseArray可以免去基本对象类型转换为引用数据类想的时间。


如果防止过度绘制,如何做布局优化?

1. 使用include复用布局文件。

2.  使用merge标签避免嵌套布局。

3. 使用stub标签仅在需要的时候在展示出来。


如何提交代码质量?

1. 避免创建不必要的对象,尽可能避免频繁的创建临时对象,例如在for循环内,减少GC的次数。

2. 尽量使用基本数据类型代替引用数据类型。

3. 静态方法调用效率高于动态方法,也可以避免创建额外对象。

4. 对于基本数据类型和String类型的常量要使用static final修饰,这样常量会在dex文件的初始化器中进行初始化,使用的时候可以直接使用。

5. 多使用系统API,例如数组拷贝System.arrayCopy()方法,要比我们用for循环效率快9倍以上,因为系统API很多都是通过底层的汇编模式执行的,效率比较高。

有没有遇到64k问题,为什么会出现这个问题,如何解决?

· DEX文件中,method、field、class等的个数使用short类型来做索引,即两个字节(65535),method、field、class等均有此限制。

· APK在安装过程中会调用dexopt将DEX文件优化成ODEX文件,dexopt使用LinearAlloc来存储应用信息,关于LinearAlloc缓冲区大小,不同的版本经历了4M/8M/16M的限制,超出 缓冲区时就会抛出INSTALL_FAILED_DEXOPT错误。

· 解决方案是Google的MultiDex方案,具体参见:配置方法数超过 64K 的应用。

MVC、MVP与MVVM之间的对比分析?

· MVC:PC时代就有的架构方案,在Android上也是最早的方案,Activity/Fragment这些上帝角色既承担了V的角色,也承担了C的角色,小项目开发起来十分顺手,大项目就会遇到 耦合过重,Activity/Fragment类过大等问题。

· MVP:为了解决MVC耦合过重的问题,MVP的核心思想就是提供一个Presenter将视图逻辑I和业务逻辑相分离,达到解耦的目的。

· MVVM:使用ViewModel代替Presenter,实现数据与View的双向绑定,这套框架最早使用的data-binding将数据绑定到xml里,这么做在大规模应用的时候是不行的,不过数据绑定是 一个很有用的概念,后续Google又推出了ViewModel组件与LiveData组件。ViewModel组件规范了ViewModel所处的地位、生命周期、生产方式以及一个Activity下多个Fragment共享View Model数据的问题。LiveData组件则提供了在Java层面View订阅ViewModel数据源的实现方案。

W文章转载于作者open软件开发小组

en

有兴趣的小伙伴可以关注我们日后的干货分享哦!当然,你想了解更多关于Java的知识,

可以填写下方的表单,有利于咨询老师更快的找到你哦!我们下次见~

预约申请免费试听课

怕钱不够?就业挣钱后再付学费!    怕学不会?从入学起,达内定制课程!     担心就业?达内多家实践企业供你挑选!

上一篇:Android面试题集(三)
下一篇:毕业后,你和别人的差距为什么这么大?

2020年Web前端面试问题总结之JS问题

2020年Web前端面试问题总结之VUE问题

2020年Web前端面试问题总结之React问题

2020年Web前端笔试题汇总

选择城市和中心
江西省

贵州省

广西省

海南省