仕兰微 安卓工程师 社招 面试题目

原题目在这里,这里是对大部分题目的个人理解,如有不同意见,请留言交流。


仕兰微Android社招

1. 四大组件是什么

Activity,Service,BroadcastReceiver,ContentProvider

2. 四大组件的生命周期和简单用法

  • Activity

生命周期:onCreate -> onStart -> onResume -> onPause -> onStop -> onDestroy

简单用法:

  1. 创建自定义Activity与相应的布局文件;
  2. 将创建的自定义Activity注册在Manifest文件中。
  • Service
  1. start模式下:

生命周期:onCreate -> onStartCommand -> onDestroy
简单用法:

  1. 创建自定义Service
  2. 将自定义Service注册在Manifest文件中
  3. 在需要使用Service的地方调用 startService 方法启动Service
  1. bind模式下:

生命周期:onCreate -> onBind -> onUnbind -> onDestroy
简单用法:

  1. 创建自定义Service,并在其中创建IBinder接口的自定义类;
  2. 在onBind方法中返回IBinder接口的自定义类对象;
  3. 将Service注册在Manifest文件中;
  4. 在需要使用自定义Service的地方,创建ServiceConnetion对象,并利用ServiceConnetion对象获取到自定义Service对象
  5. 调用bindService方法,将自定义Service与相应组件绑定
  • BroadcastReceiver
  1. 动态注册

生命周期:与注册BroadcastReceiver的组件的生命周期保持一致
简单用法:

MyReceiver rec = new MyReceiver();
IntentFilter filter = new IntentFilter();
intentFilter.addAction("xxx");
registerReceiver(rec, filter);
// ...
unregisterReceiver(rec);
  1. 静态注册

生命周期:在onRceive方法中的任务执行完毕之后的任意时间段进行销毁,不需要手动进行取消注册操作
简单用法:

  1. 自定义BroadcastReceiver
  2. 在Manifest文件中注册自定义BroadcastReceiver
  • ContentProvider

生命周期:执行onCreate方法时创建成功,直到进程被销毁,生命周期结束
简单用法:

  1. 创建自定义ContentProvider类,并实现

onCreate 创建ContentProvider时使用
query 查询指定uri的数据时返回一个Cursor
insert 向指定uri的ContentProvider中添加数据
delete 删除指定uri的数据
update 更新指定uri的数据
getType 返回指定uri中的数据MIME类型

等方法

  1. 在Manifest文件中注册自定义ContentProvider类
  2. 在需要调用数据的地方,自定义ContentResolver类

3. Activity之间的通信方式

  1. Intent与Bundle
  2. 类的静态变量
  3. 使用全局变量Application
  4. 借助数据持久化工具SharedPreferences/sQLite/File
  5. Service
  6. 第三方工具如EventBus等

4. Activity各种情况下的生命周期

  1. 按返回键:

启动:onCreate -> onStart -> onResume
按返回键:onPause -> onStop -> onDestroy

  1. 按HOME键:

启动:onCreate -> onStart -> onResume
按HOME键:onPause -> onStop
启动:onRestart -> onStart -> onResume

  1. 横竖屏切换:

启动:onCreate -> onStart -> onResume
切换:onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume

  1. 横竖屏切换(配置configChanges):
    在Activity标签下配置:android:configChanges="orientation|keyboardHidden|screenSize"

启动:onCreate -> onStart -> onResume
横竖屏切换:onConfigurationChanged

5. 横竖屏切换时,Activity各种情况下的生命周期

6. Activity与Fragment之间生命周期比较

当Activity包含一个Fragment时,Activity与Fragment的生命周期变化:

Activity(onCreate) -> Fragment(onAttach -> onCreate -> onCreateView -> onActivityCreated) ->
Activity(onStart) -> Fragment(onStart) ->
Activity(onResume) -> Fragment(onResume) ->
Fragment(onPause) -> Activity(onPause) ->
Fragment(onStop) -> Activity(onStop) ->
Fragment(onDestroyView -> onDestroy -> onDetach) -> Activity(onDestroy)

7. Android动画框架实现原理

Android动画实现原理

Android提供了三种动画:Frame Animation、Tween Animation、Property Animation

  1. Frame Animation
    使用了Choreographer机制
AnimationDrawable animationDrawable = (AnimationDrawable)> image.getDrawable();
animationDrawable.start();
  1. Tween Animation
    在绘制过程中,尝试获取动画在当前时刻的变换,然后应用到View的绘制中
Animation translateAnimation = new TranslateAnimation(0, 100, 0, 0);
translateAnimation.setDuration(500);
translateAnimation.setInterpolator(new AccelerateInterpolator());
translateAnimation.setFillAfter(true);// 动画结束之后,保持当前状态(即不返回到动画开始时的状态)
imageView.startAnimation(translateAnimation);
  1. Property Animation
    不依赖于Android本身的回调机制,但依赖于Looper的Thread

8. Android各个版本API的区别

9. requestLayout,onLayout,onDraw,drawChild区别与联系

  • requestLayout:当前View发生一些改变,这个改变使得现有的View失效,所以调用requestLayout方法对View树重新布局,过程包括measure和layout过程,但不会调用draw过程,即不会发生重新绘制视图的过程。
  • onLayout:调用onLayout的时机是,View需要给自己设置大小和位置,或者ViewGroup需要设置子View和自身时调用。
  • onDraw:绘制视图时调用。
  • drawChild:ViewGroup绘制自身的子View时会调用dispatchDraw(canvas)方法,这个方法中调用drawChild。

10. invalidate与postInvalidate的区别及使用

invalidate工作在主线程中,在非主线程中可以通过Handler来通知主线程进行界面更新。
postInvalidate在非主线程中被调用。

11. Activity-Window-View三者的差别

原文

Activity

  • Activity不负责视图的控制,它只是控制生命周期和处理事件,真正控制视图的是Window。一个Activity包含一个Window,Window才是真正代表一个窗口。
  • Activity就是一个控制器,统筹视图的添加与显示,以及通过其他回调方法与Window、View进行交互。

Window

  • 表示一个窗口,是所有View的直接管理者,任何视图都通过Window呈现。
  • Window是一个抽象类,具体实现是PhoneWindow,PhoneWindow有一个内部类DecorView,通过创建DecorView来加载Activity中设置的布局。

View

  • DecorView是Android视图树的根节点视图。
  • Activity通过setContentView设置的布局文件就是被加载道DecorView的内容栏的,成为其唯一的子View。

12. 谈谈对Volley的理解

Volley适合频繁的网络通信操作,能同时实现多个网络通信,扩展性强。
用法:使用Volley的通用步骤是通过Volley暴露的newRequestQueue方法,创建RequestQueue对象,接着向RequestQueue对象中添加Request(StringRequest、JsonRequest、ImageRequest或自定义Request)对象。

RequestQueue queue = Volley.newRequestQueue(this);
String url = "";
StringRequest strRequest = new StringRequest(Request.Method.GET,
  url, new Response.Listener() {
      @Override
      public void onRequest(String response) {

      }
  }, new Response.ErrorListener() {
      @Override
      public void onErrorListener(VolleyError error) {

      }
  });
queue.add(strRequest);
queue.start();

步骤有三步:

创建一个RequestQueue对象;
创建一个StringRequest对象;
将StringRequest对象添加到RequestQueue里面。

  • 对于post方式的网络请求:
RequestQueue queue = Volley.newRequestQueue(this);
String url = "";
StringRequest strRequest = new StringRequest(Request.Method.POST,
    url, new Response.Listener() {
        @Override
        public void onResponse(String response) {

        }
    }, new Response.ErrorListener() {
         @Override
        public void onErrorListener(VolleyError error) {

        }
    }){
        @Override
        protected Map getParams() throws AuthFailureError {
            return super.getParams();
        }
    };
    queue.add(strRequest);
    queue.start();

getParams方法获取的数据是用于向服务器提交的参数。

13. Handler机制与底层实现

  • Looper.prepare():实现创建Looper对象、MessageQueue对象的操作。
  • Handler对象调用send或post方法,将Message对象传入MessageQueue中,并将自身传递给名为target的对象。
  • Looper.loop():开始不断从MessageQueue中取出消息并处理消息。
  • 对于来自不同线程的消息,使用ThreadLocal对象区分线程。

14. Handler、Thread和HandlerThread的差别

  • Handler:在Android中负责发送和处理消息,通过它可以实现其他线程与主线程之间消息通信。
  • Thread: Java进程中执行运算的最小单位,即执行处理机调度的基本单位。
  • HandlerThread:一个继承自Thead类的HandlerThread类,这个类对Java的Thread做了很多便利的封装。HandlerThread在start之后可以获得Looper对象,利用这个Looper对象可以方便地创建Handler对象,实现线程间的通信。

15. Handler发消息给子线程,Looper怎么启动

Looper对象存在于与创建Handler对象所在线程一致的线程中,也就是Looper对象与Handler对象保持线程一致。当Handler对象向子线程发送消息时,消息被保存在MessageQueue对象中,Looper对象会在loop方法中不断检测MessageQueue中是否有消息存在,如果存在则取出、处理并调用发送消息的Handler对象发送消息。

16. 关于Handler,在任何地方new Handler都是什么线程下

  • 不传递Looper对象创建Handler:在哪个线程创建Handler,默认获取的就是该线程的Looper对象,Handler的一系列操作都是在该线程下进行的。
  • 传递Looper对象创建Handler:传递的Looper对象是哪个线程的,Handler对象绑定的就是该线程。

17. ThreadLocal原理,实现及如何保证Local属性

在Thread类的内部有一个成员专门用于存储线程的ThreadLocal数据:ThreadLocal.Values,不同线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自的线程中取出一个数组,然后再从数组中根据ThreadLocal的索引去查找对应的value值。

ThreadLocal的set/get方法所操作的对象都是当前线程的ThreadLocal.Values的对象的table数组,在不同线程中访问同一个ThreadLocal的set/getchafe,它们对ThreadLocal所做的读/写操作仅限于各自线程内部,这就是为什么ThreadLocal可以在多个线程中互不干扰地存储和修改数据。

18. 在单线程模型中Message、Handler、Message Queue、Looper之间的关系

Handler获取当前线程的Looper对象,由Looper对象从存放Message的MessageQueue中取出Message,再由Handler进行Message的分发与处理。

19. View事件传递分发机制

  • 触摸事件有一个down、多个move、一个up组成;
  • 事件的传递是从Activity开始的,Activity -> PhoneWindow -> DecorView -> ViewGroup -> View,主要操作在ViewGroup和View中;
  • ViewGroup类主要调用 dispatchTouchEvent -> onInterceptTouchEvent -> dispatchTransformedTouchEvent ;ViewGroup不直接调用onTouchEvent方法;
    View类主要调用 dispatchTouchEvent -> onTouchEvent -> performClick 。

事件传递顺序:

  1. down从最外层的View向里层的子Viwe传递,如果最里层的View不响应事件,再将事件向父View传递;
  2. 把父View当作普通View对待,如果当前View也不响应事件,继续向父View传递;
  3. 不断重复第2步,整个View的传递与分发过程就完成了。

20. ListView中图片错位的问题是如何产生的

由于复用contentView与异步加载图片造成的。

21. 服务器只提供数据接收接口,在多线程或多进程条件下,如何保证数据的有序到达

  1. 有序,多线程要使用同步,多进程要使用进程通信保障数据传输的顺序
  2. 到达,使用TCP可靠传输,服务器返回传输成功后才能传输下一个

22. 性能优化,如何检测一段代码的执行时间,界面卡顿如何修复

性能优化分类:

  1. 卡顿优化
  2. 内存优化
  3. 电量优化
  4. 网络优化
  5. 启动优化、安装包体积优化

检测一段代码执行时间:

?

界面卡顿如何修复:

过度绘制

去掉不必要的背景色

  1. 设置窗口背景色为通用背景色,去除根布局背景色
  2. 去除与列表背景色相同的Item背景色

布局视图树扁平化

  1. 移除嵌套布局
  2. 使用merge、include标签
  3. 使用性能消耗更小的布局

减少透明色,即alpha属性的使用

通过使用半透明颜色值代替
其他

  1. 使用ViewStub标签,延迟加载不必要的视图
  2. 使用AsyncLayoutInflator异步解析视图

主线程耗时操作

  1. Json数据解析耗时
  2. 文件操作
  3. Binder通信
  4. 正则匹配
  5. 相机操作
  6. 组件初始化
  7. 循环删除、创建View
  8. WebView首次初始化

主线程挂起

  1. 异步线程与主线程竞争CPU资源
  2. 频繁GC使主线程挂起

23. 滑动不流畅怎么处理

导致Android界面滑动卡顿的原因主要有两个:
1.UI线程有耗时操作
2.视图渲染时间过长
对于第一种情况,主线程中不要有耗时操作,耗时操作可以放入Thread中并通过Handler与主线程交互,或者使用AsyncTask来完成耗时操作。
对于第二种情况,?

24. fps如何提高

安卓App帧率优化的一些经验总结

25. 内存泄露怎么检测

使用LeakCanary、Bugly或者AS自带的Profiler

附内存泄漏相关知识

定义:一个不再被使用的对象,因为另一个正在使用的对象持有对该对象的引用,导致不再被使用的对象无法被回收,而停留在堆内存中,就产生了内存泄漏。
如何解决:

  1. 持有Context造成的内存泄漏

在Android中有两种Context对象:Activity和Application,当给一个类传递Context时经常使用第一种,这就导致了该类持有对Activity的引用;当Activity关闭时,因为其他类持有而导致无法正常被回收,导致内存泄漏。

解决方案:

在给其他类传递Context时使用Application对象,这个对象的生命周期与应用同长,而不依赖Activity的生命周期;
对Context的引用不要超过其本身的生命周期,谨慎对Context使用static关键字。

  1. Handler造成的内存泄漏

解决方案:

  1. 将Handler放在单独的类中,或者使用静态内部类
  2. 如果要在Handler内部调用Activity中的资源,可以在Handler中使用弱引用的方式指向所在的Activity,使用static+WeakReference的方式断开Handler与Activity之间的关系
  1. 单例模式造成的内存泄漏

单例模式的静态特性使得单例模式的生命周期与应用一样长

解决方案:

一般参考持有Context造成的内存泄漏 即可解决

  1. 非静态内部类创建静态实例造成的内存泄漏

在项目中通常为了避免多次的初始化操作,会创建静态对象保存这些对象,这种情况也容易引发内存泄漏。因为非静态内部类默认持有外部类的引用,而创建的静态实例的生命周期与应用的一样长,这就导致该静态实例一直持有该Activity的引用,导致Activity不能正常回收。

解决方案:

  1. 将内部类修改为静态的,这样不再持有外部类的引用
  2. 将该对象抽取出来封装成一个单例
  1. 线程造成的内存泄漏

当使用线程时一般都会使用匿名内部类,而匿名内部类会默认持有外部类引用,当Activity关闭之后,如果线程中的内务还没有执行完毕,就会导致Activity不能正常回收,造成内存泄漏

解决方案:

创建一个静态的类,实现Runnable类,在使用的时候实例化该自定义Runnable类。

  1. 资源没有关闭造成的内存泄漏

对于使用了BroadcastReceiver、ContentProvider、File、Cursor、Stream、Bitmap等资源的代码,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。

  1. 监听器没有注销造成的内存泄漏

在Android程序中存在很多需要register/unregister的监听器,需要确保及时unregister监听器

  1. 集合中的内存泄漏

通常会把一些对象的引用加入道集合容器中,当不再需要该对象时,并没有把对该对象的引用从集合中清理出去,这样这个集合就会越来越大,如果这个集合是static的,那情况就会更严重。
退出程序之前,要将集合中的对象clear,然后设置为null,再退出程序

26. 线程和线程池

线程:程序执行流的最小执行单位,是进程中的实际运作单位
线程池:Java中开辟出的一种管理线程的概念,是若干线程的集合。

27. wait()和sleep()的区别

wait:Object类的方法,线程休眠时会释放对象的同步锁,其他线程可以访问该对象。必须在synchronized的代码块下使用
sleep:Thread类的方法,当在一个synchronized方法中调用sleep时,线程虽然休眠了,但是对象的同步锁没有被释放,其他线程仍然无法访问该对象。

28. 线程池的参数详解

ThreadPoolExecutor是线程池的真正实现,它的构造方法提供了一系列参数来配置线程池。
下面是ThreadPoolExecutor的一个比较常用的构造方法:

public ThreadPoolExecutor(int corePoolSize, int maimumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory)
  • corePoolSize

线程池的核心线程数,默认情况下,核心线程数会在线程池中一直存在,即使它们处于闲置状态

  • maximumPoolSize

线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞

  • keepAliveTime

非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。

  • unit

用于指定keepAliveTime参数的事件单位,这是一个枚举值

  • workQueue

线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储的这个参数中

  • threadFactory

线程工厂,为线程池提供创建新线程的功能。ThreadFActory是一个接口,它只有一个方法:Thread newThread(Runnable r)

ThreadPoolExecutor执行任务时遵循如下规则:

  1. 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
  2. 如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入道任务队列中排队等待执行。
  3. 如果第二步中无法将任务插入到任务队列中,这往往是任务队列已满,这时如果线程数量没有达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
  4. 如果第三步中的线程数量已经达到线程池规定的最大值,那饿就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者。

你可能感兴趣的:(仕兰微 安卓工程师 社招 面试题目)