美文网首页Android开发iT程序人生Android开发
Android高级开发工程师面试问答

Android高级开发工程师面试问答

作者: 疯狂的程序猿丶 | 来源:发表于2018-12-18 22:04 被阅读31次

    结合自己之前去很多大公司的面试经历和自己面别人的一些题,这里做一些总结,Android面试中常见的面试题。

    1.要做一个尽可能流畅的ListView,你平时在工作中如何进行优化的?

    Item布局,层级越少越好,使用hierarchyview工具查看优化。

    复用convertView

    使用ViewHolder

    item中有图片时,异步加载

    快速滑动时,不加载图片

    item中有图片时,应对图片进行适当压缩

    实现数据的分页加载

    2.Android的Handler运行机制?

    要解释Handler的运行机制就要讲几个对象:Message、Handler、Message Queue、Looper。Handler获取当前线程中的looper对象,looper用来从存放Message的 MessageQueue中取出Message,再有Handler进行Message的分发和处理。

    Message Queue(消息队列):用来存放通过Handler发布的消息,通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列
    Handler:可以发布或者处理一个消息或者操作一个Runnable,通过Handler发布消息,消息将只会发送到与它关联的消息队列,然也只能处理该消息队列中的消息
    Looper:是Handler和消息队列之间通讯桥梁,程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的
    Handler:Handler接受到消息后调用handleMessage进行处理
    Message:消息的类型,在Handler类中的handleMessage方法中得到单个的消息进行处理
    在单线程模型下,为了线程通信问题,Android设计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍:

    1. Message
    Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。
    2. Handler
    Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。
    3. Message Queue
    Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
    每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。
    4. Looper
    Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。对于子线程使用Looper,API Doc提供了正确的使用方法:这个Message机制的大概流程:
    1. 在Looper.loop()方法运行开始后,循环地按照接收顺序取出Message Queue里面的非NULL的Message。
    2. 一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到Message Queue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用 该Message的target指向的Hander的dispatchMessage函数对Message进行处理。在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:

    1. Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;
    2. Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;
    3. 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。
      由此可见,我们实现的handleMessage方法是优先级最低的!
      3. Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!
      在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行!
      1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper;

    3.Android的Activity的四种启动模式和用途?

    standerd

    默认模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。应用场景:绝大多数Activity。

    singleTop

    栈顶复用模式,如果要开启的activity在任务栈的顶部已经存在,就不会创建新的实例,而是调用 onNewIntent() 方法。避免栈顶的activity被重复的创建。

    singleInstance

    “singleInstance”独占一个task,其它activity不能存在那个task里;如果它启动了一个新的activity,不管新的activity的launch mode 如何,新的activity都将会到别的task里运行(如同加了FLAG_ACTIVITY_NEW_TASK参数)。

    singleTask

    栈内复用模式, activity只会在任务栈里面存在一个实例。如果要激活的activity,在任务栈里面已经存在,就不会创建新的activity,而是复用这个已经存在的activity,调用 onNewIntent() 方法,并且清空这个activity任务栈上面所有的activity。

    4.解释一下activity、 intent 、intent filter、service、Broadcase、BroadcaseReceiver?

    一个activity呈现了一个用户可以操作的可视化用户界面;一个service不包含可见的用户界面,而是在后台运行,可以与一个activity绑定,通过绑定暴露出来接口并与其进行通信;一个broadcast receiver是一个接收广播消息并做出回应的component,broadcast receiver没有界面;一个intent是一个Intent对象,它保存了消息的内容。对于activity和service来说,它指定了请求的操作名称和待操作数据的URI,Intent对象可以显式的指定一个目标component。如果这样的话,android会找到这个component(基于manifest文件中的声明)并激活它。但如果一个目标不是显式指定的,android必须找到响应intent的最佳component。它是通过将Intent对象和目标的intent filter相比较来完成这一工作的;一个component的intent filter告诉android该component能处理的intent。intent filter也是在manifest文件中声明的。

    5.Serializable和Parcelable的区别?

    在使用内存的时候,Parcelable 类比Serializable性能高,所以推荐使用Parcelable类。
    1.Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
    传智播客武汉校区就业部出品 务实、创新、质量、分享、专注、责任
    32
    2.Parcelable不能使用在要将数据存储在磁盘上的情况。尽管Serializable效率低点,但在这
    种情况下,还是建议你用Serializable 。
    实现:
    1.Serializable 的实现,只需要继承 Serializable 即可。这只是给对象打了一个标记,系统会
    自动将其序列化。
    2.Parcelabel 的实现,需要在类中添加一个静态成员变量 CREATOR,这个变量需要继承
    Parcelable.Creator 接口。

    <span style="font-size:14px;">public class MyParcelable implements Parcelable {  
        private int mData;  
    
        public int describeContents() {  
            return 0;  
        }  
    
        public void writeToParcel(Parcel out, int flags) {  
            out.writeInt(mData);  
        }  
    
        public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {  
            public MyParcelable createFromParcel(Parcel in) {  
                return new MyParcelable(in);  
            }  
    
            public MyParcelable[] newArray(int size) {  
                return new MyParcelable[size];  
            }  
        };  
    
        private MyParcelable(Parcel in) {  
     mData = in.readInt();  
        }  
    }</span> 
    
    
    public class MyParcelable implements Parcelable {
    
        private int mData;
    
        public int describeContents() {
    
            return 0;
    
        }
    
        public void writeToParcel(Parcel out, int flags) {
    
            out.writeInt(mData);
    
        }
    
        public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
    
            public MyParcelable createFromParcel(Parcel in) {
    
                return new MyParcelable(in);
    
            }
    
            public MyParcelable[] newArray(int size) {
    
                return new MyParcelable[size];
    
            }
    
        };
    
        private MyParcelable(Parcel in) {
    
            mData = in.readInt();
    
        }
    
    }
    
    

    6.什么是内存泄漏,android在什么情况下容易产生内存泄漏?

    说到内存泄漏就不得不提内存溢出。

    内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

    内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重。内存溢出导致了内存泄漏。

    在Android中常见的内存泄漏原因:

    1. 资源释放问题
    程序代码的问题,长期保持某些资源,如Context、Cursor、IO流的引用,资源得不到释放
    造成内存泄露。
    2. 对象内存过大问题
    保存了多个耗用内存过大的对象(如Bitmap、XML文件),造成内存超出限制。
    3. static关键字的使用问题
    static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是
    该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费
    过多的实例(Context的情况最多),这时就要谨慎对待了。
    public class ClassName {
    private static Context mContext;
    //省略
    }
    以上的代码是很危险的,如果将 Activity 赋值到 mContext 的话。那么即使该 Activity 已经
    onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放。

    4. 线程导致内存溢出
    线程产生内存泄露的主要原因在于线程生命周期的不可控。

    5,数据库游标忘记回收等

    那么针对上面的问题我们怎么避免呢

    1、图片过大导致OOM
    Android 中用bitmap时很容易内存溢出,比如报如下错误:Java.lang.OutOfMemoryError :
    bitmap size exceeds VM budget。
    解决方法:
    方法1: 等比例缩小图片

    BitmapFactory.Options options = new BitmapFactory.Options();   
    options.inSampleSize = 2; //Options 只保存图片尺寸大小,不保存图片到内存   
    BitmapFactory.Options opts = new BitmapFactory.Options();   
    opts.inSampleSize = 2;   
    Bitmap bmp = null;   
    bmp = BitmapFactory.decodeResource(getResources(), mImageIds[position],opts);   
    //回收  
    bmp.recycle();//  
    
    

    方法2:对图片采用软引用,及时地进行recyle()操作

    SoftReference<Bitmap> bitmap = new SoftReference<Bitmap>(pBitmap);   
    if(bitmap != null){   
        if(bitmap.get() != null && !bitmap.get().isRecycled()){   
            bitmap.get().recycle();   
     bitmap = null; }   
        }  
    
    

    2、查询数据库没有关闭游标
    程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果
    我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会出现内
    存问题,这样就会给以后的测试和问题排查带来困难和风险。

    3、构造Adapter时,没有使用缓存的 convertView

    在使用ListView的时候通常会使用Adapter,那么我们应该尽可能的使用ConvertView。

    为什么要使用convertView?

    当convertView为空时,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象。

    当 convertView 不为空,重复利用已经创建的 view 的时候,使用 getTag()方法获取绑定的

    ViewHolder对象,这样就避免了findViewById对控件的层层查询,而是快速定位到控件。

    4、Bitmap对象不再使用时调用recycle()释放内存

    有时我们会手工的操作Bitmap对象,如果一个Bitmap对象比较占内存,当它不再被使用的时

    候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。

    7. 简述下Android JNI调用过程?

    1. 安装和下载Cygwin,下载AndroidNDK
    2. 在ndk项目中JNI接口的设计
    3. 使用C/C++实现本地方法
    4. JNI生成动态链接库.so文件
    5. 将动态链接库复制到java工程,在java工程中调用,运行java工程即可

    8.对于Android 的安全问题,你知道多少?

    1. 错误导出组件
    2. 参数校验不严
    3. WebView引入各种安全问题,webview中的js注入
    4. 不混淆、不防二次打包
    5. 明文存储关键信息
    6. 错误使用HTTPS
    7. 山寨加密方法
    8. 滥用权限、内存泄露、使用debug签名

    9.如何缩减APK包大小?

    代码

    保持良好的编程习惯,不要重复或者不用的代码,谨慎添加libs,移除使用不到的libs。

    使用proguard混淆代码,它会对不用的代码做优化,并且混淆后也能够减少安装包的大小。

    native code的部分,大多数情况下只需要支持armabi与x86的架构即可。如果非必须,可以考虑拿掉x86的部分。

    资源

    使用Lint工具查找没有使用到的资源。去除不使用的图片,String,XML等等。 assets目录下的资源请确保没有用不上的文件。

    生成APK的时候,aapt工具本身会对png做优化,但是在此之前还可以使用其他工具如tinypng对图片进行进一步的压缩预处理。

    jpeg还是png,根据需要做选择,在某些时候jpeg可以减少图片的体积。 对于9.png的图片,可拉伸区域尽量切小,另外可以通过使用9.png拉伸达到大图效果的时候尽量不要使用整张大图。

    策略

    有选择性的提供hdpi,xhdpi,xxhdpi的图片资源。建议优先提供xhdpi的图片,对于mdpi,ldpi与xxxhdpi根据需要提供有差异的部分即可。

    尽可能的重用已有的图片资源。例如对称的图片,只需要提供一张,另外一张图片可以通过代码旋转的方式实现。

    能用代码绘制实现的功能,尽量不要使用大量的图片。例如减少使用多张图片组成animate-list的AnimationDrawable,这种方式提供了多张图片很占空间。

    10.Android与服务器交互的方式中的对称加密和非对称加密是什么?

    对称加密,就是加密和解密数据都是使用同一个key,这方面的算法有DES。

    非对称加密,加密和解密是使用不同的key。发送数据之前要先和服务端约定生成公钥和私钥,使用公钥加密的数据可以用私钥解密,反之。这方面的算法有RSA。ssh 和 ssl都是典型的非对称加密。

    11.谈谈你对Android中Context的理解?

    Context:包含上下文信息(外部值) 的一个参数. Android 中的 Context 分三种,Application Context ,Activity Context ,Service Context.

    它描述的是一个应用程序环境的信息,通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作,例如:启动一个Activity,发送广播,接受Intent信息等

    12.如何把一个应用设置为系统应用?

    成为系统应用,首先要在 对应设备的 Android 源码 SDK 下编译,编译好之后:

    此 Android 设备是 Debug 版本,并且已经 root,直接将此 apk 用 adb 工具 push 到 system/app 或 system/priv-app 下即可。

    如果非 root 设备,需要编译后重新烧写设备镜像即可。

    有些权限(如 WRITE_SECURE_SETTINGS ),是不开放给第三方应用的,只能在对应设备源码中编译然后作为系统 app 使用。

    以上就列举这么多了,其他的大家可以自行搜索。

    相关文章

      网友评论

        本文标题:Android高级开发工程师面试问答

        本文链接:https://www.haomeiwen.com/subject/eiurkqtx.html