您的位置:首页 > 职场人生

魅族 安卓开发面试 问题整理(2017.12.13)

2017-12-15 01:30 267 查看

一. 安卓卡顿的原理是什么?是什么造成的?

Android 系统每 16ms 秒会发出一个 VSYNC 信号,触发对 UI 的渲染,如果每次都渲染成功,就能达到流畅画面要求的 60 fps;如果无法在 16 ms 内完成一次渲染,就会产生卡顿的感觉;

产生的原因:

1. Layout 内容过于复杂

2. Layout 层级过高

3. 动画执行次数过多

4. 大量的 GC 操作

参考资料:

http://hukai.me/android-performance-patterns/

二. 完整读过的安卓模块源码

另开文章补充。

三. 什么原因会造成普通内部类 Handler 不被回收,从而产生 OOM?

Handler 发送了延时消息,在消息处理之前 Activity 结束;

Handler 声明为静态成员。

四. 为何只有主线程能操作 UI?

这句话不正确,Android 是“建议”在主线程操作 UI,并不是“只有”主线程能操作 UI。

首先,Android 中操作 UI 通常涉及到 View 的 invalidate 和 requestLayout 方法,这两个方法最终会调用 ViewRootImpl 的 invalidateChild 和 requestLayout 方法,这两个方法中都调用了 checkThread 方法,该方法判断当前线程是否等于 mThread,如果不是则抛出我们熟悉的异常 CalledFromWrongThreadException(“Only the original thread that created a view hierarchy can touch its views.”);

mThread 是在 ViewRootImpl 的构造方法中赋值的,在 WindowManagerImpl 的 addView 方法中存在 ViewRootImpl 构造方法的调用;

无论是 Activity 还是 Dialog 的显示,还是通过 WindowManager 直接添加窗口,最终都会调用 WindowManagerImpl 的 addView 方法。其中 Activity 对于 addView 的调用被封装起来了(setContentView 仅仅是设置 Activity 对应的视图),因此默认情况下,显示一个 Activity,WindowManagerImpl 的 addView 方法在 UI 线程中执行,mThread 为 UI 线程,因此只能在 UI 线程中操作 Activity。

然而,如果在子线程中调用 WindowManagerImpl 的 addView 方法,那么便可以在子线程中操作该 View:

new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
MyDialog dialog = new MyDialog(MainActivity.this);
// show 方法会调用 WindowManagerImpl 的 addView 方法
dialog.show();
Looper.loop();
}
}).start();


Android 建议在主线程操作 UI 的原因:保证线程同步。

参考资料:

http://blog.csdn.net/junhzhan/article/details/50757302

https://www.zhihu.com/question/24764972

五. Retrofit 结果回调函数在哪个线程执行?

使用默认的 CallAdapter,回调函数在 UI 线程执行;

使用 RxJava,回调函数可自由切换线程执行。

六. Handler 发送消息到 MessageQueue 的动作在哪个线程执行?

Handler 发送消息到 MessageQueue 的 enqueueMessage 方法在创建 Handler 的线程执行。

(不确定)

七. Android 跨进程通信的方式

Bundle

文件共享

Messenger

AIDL

ContentProvider

Socket

八. ListView 的优化方式

优化加载布局

优化加载组件

由于加载 ListView 的每一项都会调用 getView 方法,getView 方法中需要调用 LayoutInflater#inflate 方法加载布局,还需要调用 View#findViewById 方法查找布局中的控件。如果 getView 方法每次都要调用 LayoutInflater#inflate 和 View#findViewById 方法,则会影响 ListView 的性能;

因此,可以利用 View 的缓存 convertView 配合使用 ViewHolder,优化加载的布局和组件:

public View getView(int position, @Nullable View convertView, @NonNull ViewGroup par
aa53
ent) {
Person person = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
viewHolder = new ViewHolder();
viewHolder.nameTv = (TextView) view.findViewById(R.id.name_tv);
viewHolder.powerTv = (TextView) view.findViewById(R.id.power_tv);
view.setTag(viewHolder);
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.nameTv.setText(person.getName());
viewHolder.powerTv.setText(person.getPower());
return view;
}

class ViewHolder {
TextView nameTv;
TextView powerTv;
}


注意,convertView 并不是首次加载 View 之后就存在,而是存在于 ScrapView 之中。

初次加载时,屏幕上可见的 ListView 项是 ActionView,之后通过上拉或下拉而离开屏幕可见范围的项是 ScrapView;

位于 ActionView 中的项不会使用缓存 ConvertView,必须调用 LayoutInflater#inflate 和 View#findViewById 方法;由于上拉或下拉而离开屏幕可见范围的 ScrapView,会将自身作为 ConvertView 参数,加入到 RecycleBin 中,而新进入屏幕可见范围的项,会从 RecycleBin 中获取 ConvertView 作为缓存(如果没有,则需手动加载);

因此,经过优化后的 ListView,最多只需加载 N + 1 次布局和组件(有可能最后一项显示不全,导致第一项还没完全离开之前,新的一项就显示出来了),N 为屏幕能够显示的 ListView 的项数。

参考资料:http://www.cnblogs.com/yuhanghzsd/p/5595532.html

九. 简述HTTP 协议,断点续传

HTTP 是一个属于应用层的面向对象的协议,主要特点可概括如下:

支持客户/服务器模式;

简单快速:客户向服务端发起请求时,只传送请求方式(GET,POST 等)和路径。由于 HTTP 协议简单,使得服务器程序规模小,所以通讯速度很快;

灵活:允许传输任何类型的数据对象;

无连接:限制每次连接只处理一个请求,等到客户的回应后,立即断开连接;

无状态:事务对于事务处理没有记忆能力;

断点续传

指的是在上传/下载时,将任务(一个文件或压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传/下载,如果碰到网络故障,可以从已经上传/下载的部分开始继续上传/下载未完成的部分,而没有必要从头开始上传/下载。可以节省时间,提高速度。

十. Android 绘制的三个过程

measure:确定 View 的大小;

layout:确定 View 的位置;

draw:在屏幕上绘制 View。

十一. 自定义 View 注意事项

直接继承 View 的自定义控件需要重写 onMeasure 方法并设置 wrap_content 时的自身大小,否则在布局中使用 wrap_content 就相当于使用 match_parent。

十二. ViewGroup 的 onMeasure 是先测量自己,还是先遍历测量子 View?

ViewGroup 先遍历测量子 View,然后根据子 View 的结果来测量自己的大小。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: