面试题整理

本文是面试题整理,又从晚上筛选的,还有周围朋友遇到的,顺便让自己温习一下

inflate方法的参数

1
inflate(int resource, ViewGroup root, boolean attachToRoot);
  1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义。
  2. 如果root不为null,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即root。
  3. 如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。
  4. 在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true。

ScrollView嵌套ListVew或者GridView

解决办法:重写 ListVew或者 GridView,网上还有很多若干解决办法,但是都不好用或者很复杂。

1
2
3
4
5
6
7
8
@Override
/** 只重写该方法,达到使ListView适应ScrollView的效果 */
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}

打开套有ListVew的 ScrollView的页面布局,默认起始位置不是最顶部。

方法一:是把套在里面的Gridview 或者 ListVew 不让获取焦点即可。
gridview.setFocusable(false); listview.setFocusable(false);
注意:在xml布局里面设置android:focusable=“false”不生效

方法二:网上还查到说可以设置myScrollView.smoothScrollTo(0,0);

invalidate和postInvalidate和requestLayout的区别

1、android中实现view的更新有几种方法?
你知道吗?其实android中实现view的更新有两种方法,一种是invalidate,另一种是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。
怎么说呢?Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。 鉴于此,如果要使用invalidate的刷新,那我们就得配合handler的使用,使异步非ui线程转到ui线程中调用,如果要在非ui线程中直接使用就调用postInvalidate方法即可,这样就省去使用handler的烦恼。

2、requestLayout在什么时候用呢?
当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view(父类的视图)重新调用他的onMeasure onLayout来重新设置自己位置。特别是当view的layoutparameter发生改变,并且它的值还没能应用到view上时,这时候适合调用这个方法。

整个Android应用进程的体系非常复杂,而ActivityThread 是真正的核心类,它的main方法,是整个应用进程的入口

Android应用程序可以是理解为是 四大组件和各种资源的集合,它需要各种各样的环境资源,当然不能像Java直接跑在main方法里面。

子线程更新UI,会怎么样

子线程更新UI,ViewRoot 是ViewRootImpl 的实现类,ViewRootImpl的checkThread()方法检查是不是主线程 每一次访问UI 都会重新绘制View,往WindowManager中添加DecorView,那现在应该关注的就是WindowManager的addView方法了。而WindowManager是一个接口来的,我们应该找到WindowManager的实现类才行,而WindowManager的实现类是WindowManagerImpl。这个和ViewRoot是一样,就是名字多了个impl。
WindowManagerImpl 里面调用了WindowManagerGlobal的addView方法

ViewRootImpl是在WindowManagerGlobal的addView方法中创建的。
ViewRootImpl的创建在onResume方法回调之后,而我们一开篇是在onCreate方法中创建了子线程并访问UI,在那个时刻,ViewRootImpl是没有创建的,无法检测当前线程是否是UI线程,所以程序没有崩溃一样能跑起来,而之后修改了程序,让线程休眠了200毫秒后,程序就崩了。很明显200毫秒后ViewRootImpl已经创建了,可以执行checkThread方法检查当前线程

安卓性能优化有什么了解

Android手机的内存和cpu不能和pc相比,所以必须要谨慎的去使用内存和CPU资源,因为稍稍不注意可能就会引发诸如OOM、ANR、内存泄漏等问题

布局优化:

布局优化的核心思想就是尽量减少布局文件的层级,层级越少Android在进行布局绘制时工作量也就越少,尽可能使用性高的ViewGroup,View加载 onMeasure(),onLayout,onDraw()这三个方法
比如RelativeLayout和LinearLayout都可以实现,那么要用linearLAyout因为RelativeLayout的onMeasure()方法对子view进行了2次计算,因为是相对的分横向,纵向两次测量

布局可以重用时就封装好进行重用 include如果使用其他的android:layout_xx的属性是android:layout_width,和android:layout_height必须要使用否则android:layout_xx失效

< merge >标签, 这个标签一般是和< include >标签进行配合使用的,比如当前布局是竖直的LinearLayout,而 < include >要引用的布局也是竖直LinearLayout时,就可以把要引入布局include的LinearLayout换成< merge>即可,这样又可以减少了一个布局的层级。

ViewStub 它不参与任何布局和绘制过程,作用在于按需加载所需布局,在我们开发过程中,有些布局是在某些情况下不需要显示的,比如,一个界面需要网络数据时才显示,网络异常时会显示另一个界面,这时候我们就不需要一开始就把异常界面加载进布局,只有当网络异常时才加载这个布局,这时候通过ViewStub就可以做到在使用的时候再加载,提高了程序初始化时的性能。

1
2
3
4
5
6
7
8
9
10
11
<ViewStub
android:layout_width="wrap_content"
android:id="@+id/vs"
android:layout_height="wrap_content"
android:inflatedId="@+id/ll_import"
android:layout="@layout/ll_network_error"
/>
findviewById(R.id.vs).setVisibility(View.VISIBLE);
或者
findviewById(R.id.vs).inflate()

TextView同时显示图片和文字,使用TextView的行间距

用LinearLayout自带的分割线

1
2
android:divider="@drawable/divider"
android:showDividers="middle"

Space 用来做间距

绘制优化

绘制优化是说在View调用onDraw时应该避免大量操作,在onDraw时不要创建新局部对象,onDraw中进行耗时操作,比如上千次循环,每帧的绘制事件不能超过16ms

线程优化

避免在程序中使用大量的Thread,而是采用内部的线程池

ListView优化

复用ConvertView

自定义静态类ViewHolder

使用分页加载

使用弱引用(WeakRefrence)引用ImageView对象

避免在getView中执行耗时操作

Bitmap优化

及时回收Bitmap的内存

缓存通用的Bitmap对象

压缩图片

及时关闭资源

内存泄漏优化

内存泄漏优化主要有两个方面,一个是在开发过程中避免写出有内存泄漏的代码,一个是通过分析工具找出可能存在的内存泄漏问题然后解决。

1
2
3
4
5
6
7
8
9
10
11
12
public class MainActivity extends Activity {
private static Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
}
}

Activity无法正常销毁,因为静态变量mContext引用了这个Activity

属性动画没停止导致内存泄漏,
只要在onDestroy调用xx.cancel()停止动画即可。

单例模式导致内存泄漏

比如在一个单例中持有一个Activity的引用,当Activity退出时这个Activity应该被回收,但是单例的生命周期是和Application一致的,这就导致了Activity不能被回收,造成内存泄漏

响应速度优化

Activity5秒无法响应屏幕触摸事件或键盘输入事件就会出现ANR,BroadcastReceiver在10秒内没有执行完毕也会出现ANR

所以当程序出现ANR时,系统会在/data/anr目录下创建一个traces.txt文件,通过分析文件就可以找到ANR的原因了

其他

不要过多使用枚举,因为枚举占用的内存空间比整型大两倍以上;

常量使用static final来修饰;

适当使用软引用和弱引用;

采用内存缓存和磁盘缓存;

尽量采用静态内部类,这样可以避免潜在的由于内部类而导致的内存泄漏。

okHttp与Retrofit区别

OkHttp 是基于http协议封装的一套请求客户端,虽然它也可以开线程,但根本上它更偏向真正的请求,它理解成是一个封装之后的类似HttpUrlConnection的东西

Retrofit是基于OkHttp封装的一套RESTful网络请求框架,Retrofit的封装可以说是很强大,里面涉及到一堆的设计模式,可以通过注解直接配置请求,可以使用不同的http客户端,虽然默认是用http ,可以使用不同Json Converter 来序列化数据,同时提供对RxJava的支持

使用过哪些图片加载框架,有什么优缺点

Fresco

两个内存缓存加上 Native 缓存构成了三级缓存;

支持流式,可以类似网页上模糊渐进式显示图片;

对多帧动画图片支持更好,如 Gif、WebP。

  1. 储在安卓系统的匿名共享内存, 而不是虚拟机的堆内存中, 图片的中间缓冲数据也存放在本地堆内存, 所以, 应用程序有更多的内存使用,不会因为图片加载而导致oom, 同时也减少垃圾回收器频繁调用回收Bitmap导致的界面卡顿,性能更高.

  2. 渐进式加载JPEG图片, 支持图片从模糊到清晰加载

  3. 图片可以以任意的中心点显示在ImageView, 而不仅仅是图片的中心.

  4. JPEG图片改变大小也是在native进行的, 不是在虚拟机的堆内存, 同样减少OOM

  5. 很好的支持GIF图片的显示

缺点:

  1. 框架较大, 影响Apk体积

  2. 使用较繁琐

ImageLoader

支持下载进度监听

可以在 View 滚动中暂停图片加载

默认实现多种内存缓存算法,Size 最大先删除、使用最少先删除、最近最少使用、先进先删除、时间最长先删除

缺点在于不支持GIF图片加载, 缓存机制没有和http的缓存很好的结合, 完全是自己的一套缓存机制

Picasso

(1) 自带统计监控功能
支持图片缓存使用的监控,包括缓存命中率、已使用内存大小、节省的流量等。

(2) 支持优先级处理
每次任务调度前会选择优先级高的任务,比如 App 页面中 Banner 的优先级高于 Icon 时就很适用。

(3) 支持延迟到图片尺寸计算完成加载

(4) 支持飞行模式、并发线程数根据网络类型而变
手机切换到飞行模式或网络类型变换时会自动调整线程池最大并发数,比如 wifi 最大并发为 4, 4g 为 3,3g 为 2。
这里 Picasso 根据网络类型来决定最大并发数,而不是 CPU 核数。

(5) “无”本地缓存
无”本地缓存,不是说没有本地缓存,而是 Picasso 自己没有实现,交给了 Square 的另外一个网络库 okhttp 去实现,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。

缺点

于不支持GIF, 并且它可能是想让服务器去处理图片的缩放, 它缓存的图片是未缩放的, 并且默认使用ARGB_8888格式缓存图片, 缓存体积大.

Glide

优点
(1) 图片缓存->媒体缓存
Glide 不仅是一个图片缓存,它支持 Gif、WebP、缩略图。甚至是 Video,所以更该当做一个媒体缓存。

(2) 支持优先级处理

(3) 与 Activity/Fragment 生命周期一致,支持 trimMemory
Glide 对每个 context 都保持一个 RequestManager,通过 FragmentTransaction 保持与 Activity/Fragment 生命周期一致,并且有对应的 trimMemory 接口实现可供调用。

(4) 支持 okhttp、Volley
Glide 默认通过 UrlConnection 获取数据,可以配合 okhttp 或是 Volley 使用。实际 ImageLoader、Picasso 也都支持 okhttp、Volley。

但代码量大、流转复杂

安卓各个生命周期该干什么

onCreate、onStart、onResume:启动应用程序 onPause、onStop:失去焦点 onRestart、onStart、onResume:重新获得焦点 onPause、onStop、onDestroy :退出应用程序 一般在onCreate里面初始化数据(包括接收别的页面跳转的参数),在onResume里面做刷新界面的处理,onDestroy里面进行一些对象的销毁或者反注册(比如倒计时对象的销毁,eventbus的反注册等之类)

安卓开发有什么比较擅长的

布局优化有什么了解

布局优化的核心思想就是尽量减少布局文件的层级,层级越少Android在进行布局绘制时工作量也就越少,尽可能使用性高的ViewGroup,View加载 onMeasure(),onLayout,onDraw()这三个方法
比如RelativeLayout和LinearLayout都可以实现,那么要用linearLAyout因为RelativeLayout的onMeasure()方法对子view进行了2次计算,因为是相对的分横向,纵向两次测量

布局可以重用时就封装好进行重用 include如果使用其他的android:layout_xx的属性是android:layout_width,和android:layout_height必须要使用否则android:layout_xx失效

< merge >标签, 这个标签一般是和< include >标签进行配合使用的,比如当前布局是竖直的LinearLayout,而 < include >要引用的布局也是竖直LinearLayout时,就可以把要引入布局include的LinearLayout换成< merge>即可,这样又可以减少了一个布局的层级。

ViewStub 它不参与任何布局和绘制过程,作用在于按需加载所需布局,在我们开发过程中,有些布局是在某些情况下不需要显示的,比如,一个界面需要网络数据时才显示,网络异常时会显示另一个界面,这时候我们就不需要一开始就把异常界面加载进布局,只有当网络异常时才加载这个布局,这时候通过ViewStub就可以做到在使用的时候再加载,提高了程序初始化时的性能。

1
2
3
4
5
6
7
8
9
10
11
<ViewStub
android:layout_width="wrap_content"
android:id="@+id/vs"
android:layout_height="wrap_content"
android:inflatedId="@+id/ll_import"
android:layout="@layout/ll_network_error"
/>
findviewById(R.id.vs).setVisibility(View.VISIBLE);
或者
findviewById(R.id.vs).inflate()

TextView同时显示图片和文字,使用TextView的行间距

用LinearLayout自带的分割线

1
2
android:divider="@drawable/divider"
android:showDividers="middle"

Space 用来做间距

mvp适用于什么场景

MVP比较适用于中小型的项目,大型项目慎用 类多。activity里内容比较多需要解耦的是适合mvp模式

onPause和onStop分别该干什么

1)onPause 用于由一个Activity转到另一个Activity、设备进入休眠状态(屏幕锁住了)、或者有dialog弹出时
2)onStop 用于不可见的Activity(有对话框弹出时,这时底下的activity仍然可见,所以此时onStop不会被调用)

startactivityforresult的时候,比如A startB activity,A activity被回收,B回来的时候的A和B的生命周期各自是什么?

A: onCreate–>onStart–>onResume–>onPause(跳转)–>onStop–>onActivityReult(没有执行到)

B: onCreate(跳转)–>onStart(跳转)–>onResume(跳转)–>onPause(返回)

onMeasure中传递的参数是什么

一个MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。
一个MeasureSpec由大小和模式组成。它有三种模式:UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,
子元素可以得到任意想要的大小;EXACTLY(完全),父元素决定自元素的确切大小,
子元素将被限定在给定的边界里而忽略它本身大小;AT_MOST(至多),子元素至多达到指定大小的值

Android的事件传递机制

内存泄露问题,常见的导致内存泄露的原因

是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。memory leak会最终会导致out of memory!

在java里,非静态内部类 和 匿名类 都会潜在的引用它们所属的外部类。但是,静态内部类却不会。

这个内部类就会持有外部类的隐式引用。Handler其实隐式的持有了Activity的引用,只要有消息在队列中,那么handler便无法被回收,如果handler不是static那么使用Handler的Service和Activity就也无法被回收

另外,如果想要在handler内部去调用所在的外部类Activity,那么可以在handler内部使用弱引用的方式指向所在Activity,这样同样不会导致内存泄漏。

匿名Runnable 也会持有外部引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
private static class MyHandler extends Handler {
WeakReference<MainActivity> mActivity;
MyHandler(MainActivity mActivity){
this.mActivity = new WeakReference<MainActivity>(mActivity);
}
@Override
public void handleMessage(Message msg) {
//TODO
}
}

内存溢出问题,常见的导致内存泄露的原因

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

内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。

页面很卡顿,分析一下原因是什么,然后从哪些方面入手解决

界面卡顿影响的页面 :
ListView
ScrollView
有动画的页面
分析步骤
打开调试开发者选项,GPU呈现模式分析
如果蓝色部分比较高,说明是UI线程性能问题
红色部分比较高,应该是DrawList比较复杂,这部分可能跟蓝色部分相关。目前还没想到蓝色部分不高,红色部分搞的案例。
黄色部分搞,也许是GPU太忙,也许是CPU太忙。 GPU太忙,说明DrawList太多,CPU太忙,说明要么主线程性能有问题,要么GPU太忙,来不及通知主线程。
总的来说,三部分是相关的。蓝色部分的高,可以直接导致红色和黄色部分的高,所以,重点还是分析蓝色部分的高。
如何分析主线程性能问题
两种类型的影响因素
全局级别的影响因素:
比如CPU性能低
内存不足,频繁GC
页面级别的影响因素
页面的 measure比较耗时
页面的 layout比较耗时
页面的 draw比较耗时
如何区分是全局影响因素还是页面影响因素
如果所有页面都慢,判定是全局级别因素
如果只有某个页面慢,判定是页面级别的原因
页面级别的影响因素一般原因
有自定义控件,measure, layout, draw效率比较低
View 结构比较复杂或者不合理,导致 measure, layout效率比较低
页面结构设计复杂或者不合理,导致draw效率比较低,过度绘制

java的线程同步问题。new了很多A对象,一个线程池的线程不断去操作A对象被synchronized修饰的方法,会同步操作吗?如果该方法是static的,又会怎么样?

不是静态的不会同步,可以被同时访问到,注意synchronized是“类的当前实例”才会有用, 类的两个不同实例就没有这种约束了,但是static修饰的synchronized表示是限制线程同时访问jvm中该类的所有实例同时访问对应的代码快,所以static会同步操作

软引用和弱引用的区别

如果一个对象只具有软引用,那就类似于可有可物的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用

如果一个对象只具有弱引用,那就类似于可有可物的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存

Bitmap加载过程中OOM了怎么办?回答了可以压缩图片,然后问具体怎么压缩?如果有一张很大size的图片需要加载到手机里怎么办?回答可以局部加载。

有了解什么是IntentService么?

IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。

而且,所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。

那么,用IntentService有什么好处呢?首先,我们省去了在Service中手动开线程的麻烦,第二,当操作完成时,我们不用手动停止Service。

Activity的几种启动模式有了解么?各自的含义是什么?

standard” (默认) “singleTop” “singleTask” “singleInstance

singleTop
可以有多个实例,但是不允许多个相同Activity叠加。即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法。

singleTask
只有一个实例。在同一个应用程序中启动他的时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。
如果是在别的应用程序中启动它,则会新建一个task,并在该task中启动这个Activity,singleTask允许别的Activity与其在一个task中共存,也就是说,如果我在这个singleTask的实例中再打开新的Activity,这个新的Activity还是会在singleTask的实例的task中。

singleTask启动模式启动Activity时,首先会根据taskAffinity去寻找当前是否存在一个对应名字的任务栈

如果不存在,则会创建一个新的Task,并创建新的Activity实例入栈到新创建的Task中去
如果存在,则得到该任务栈,查找该任务栈中是否存在该Activity实例
如果存在实例,则将它上面的Activity实例都出栈,然后回调启动的Activity实例的onNewIntent方法
如果不存在该实例,则新建Activity,并入栈
此外,我们可以将两个不同App中的Activity设置为相同的taskAffinity,这样虽然在不同的应用中,但是Activity会被分配到同一个Task中去。

singleInstance
只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。

启动后在同一个app内,会把后打开的activity压到前一个栈里。在不同app会把之后启动的activity重新创建一个栈

singleInstance的话呢,因为b独占一个栈,而a在别的栈里面,a是最后显示的Activity,长按Home键启动的是a所在的Task,那么显示的是a,跟b没关系,而且如果之前b跳转到过c,在c按返回键也回不到b了,因为不在一个栈里面

如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。

taskAffinity参数

这个参数标识了一个Activity所需任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名
我们可以单独指定每一个Activity的taskAffinity属性覆盖默认值
为一个activity的taskAffinity设置一个空字符串,表明这个activity不属于任何task

allowTaskReparenting设置为true,它进入后台,当一个和它有相同affinity的Task进入前台时,它会重新宿主,进入到该前台的task中

cpu的唤醒,cpu休眠会停止线程xxx

1、通过创建Timer来保持CPU唤醒状态:

Android 的 Timer 类可以用来计划需要执行的任务。但 Timer 的问题是比较消耗手机电量(实现是用 WakeLock 让 CPU 保持唤醒状态);另外一点是:部分厂商将WakeLock也设置了休眠时间,就是说 Timer 很可能和CPU一起处于休眠状态。Timer 类只能解决一小部分问题。

2、通过AlarmManager保持CPU处于唤醒状态:

AlarmManager 是 android 系统封装的用于管理 RTC 的模块,RTC (Real Time Clock) 是一个独立的硬件时钟,可以在 CPU 休眠时正常运行,在预设的时间到达时,通过中断唤醒 CPU。用 AlarmManager 来定时执行任务,CPU 可以正常的休眠,需要运行定位时醒来即可。但部分厂商为了使设备更加省电,将AlarmManager也做出了修改,例如5s一次的响应更改为50s或者是几分钟,有些干脆在CPU休眠后彻底停掉了。

3、通过心跳长链接保持client端CPU处于唤醒状态:

最佳唤醒CPU的方法是通过server端与client端的长链接通信。例如每次长链接保持5分钟时间,每30s通信一次,这样可以有效确保CPU处于唤醒状态。

唤醒方式

待机广播消息和唤醒广播消息。

Wakelock 锁机制。

线程同步synchronized,volatile关键字的用法

synchronized

  1. 一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞

  2. 当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块。

  3. 修饰方法synchronized关键字不能继承。

  4. 在定义接口方法时不能使用synchronized关键字。
  5. 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。
  6. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
  7. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。

volatile

volatile变量对于每次使用,线程都能得到当前volatile变量的最新值。但是volatile变量并不保证并发的正确性。

怎么停止一个线程,waitxxx有用吗?

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

    1. 使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。

    2. 使用interrupt方法中断线程。

非静态内部类引用外部类怎么解决引用问题

变成为静态,并且用WeakReference(弱引用)来包裹外部类

for循环…的线程同步问题

执行for循环的方法加锁synchronized

Dalvik和ART虚拟机的区别

ART

Android对程序只进行一次编译,就是在程序安装时。在安装应用时,ART虚拟机就先将程序的Java语言转化为适合Android系统运行的机器语言,而且是一次编译终身受用,先编译程序,然后直接执行

Dalvik

Dalvik是Google公司自己设计用于Android平台的虚拟机,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik 应用作为一个独立的Linux 进程执行,执行之前临时编译。

Java虚拟机什么时候进行GC,Android虚拟机呢?

JAVA

先判断对象是否存活,数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉。这个也是java比较占内存的主要原因,程序需要更多额外内存或应用程序空闲时垃圾回收机制才会进行垃圾回收;只有一个对象处于不可达状态,系统才会真正回收该对象所占有的资源(堆内存和方法区)

Android

那么GC操作会从一个叫作Roots的对象开始检查,所有它可以访问到的对象就说明还在使用当中,应该进行保留,而其它的对象就表示已经不再被使用了

当我们应用程序的堆内存快要满的时候,系统会自动触发GC操作来释放内存

当我们的应用程序需要分配更多内存,可是现有内存已经不足的时候,系统会进行GC操作来释放内存

System.gc()主动回收

handler的机制

Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。
Looper是一个轮询器,它的作用不断轮询MessageQueue,当如果有新的消息就交给Handler处理,如果轮询不到新的消息,那就自身就处于阻塞状态。

Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息。主要功能就是发送和接受处理

3) Message Queue(消息队列):用来存放线程放入的消息。

4)线程:UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。

Handler创建消息时会首先查询消息池有没有消息存在,如果有直接从消息池里面取得,没有的话初始化一个,这样的好处是消息不被使用时,并不作为垃圾回收,提高了复用。

Handler创建消息,如果UI线程创建过Looper则直接使用,没有的话通过ThreadLocal创建一个Looper,Looper初始化的时候会创建一个消息队列MessageQueue。

Looper循环查询消息队列发现有消息时取出,然后把消息分发到指定的handler

在非UI线程中创建handler时候加上Looper.prepare()。

RxJava的好处

会让代码整洁,链式写法,减少嵌套层级

线程调度比较方便灵活,复杂的线程切换

EventBus的内部实现原理是什么?

订阅者模式,register 一个订阅者中的所有订阅方法,返回一个 List集合,首先从缓存中获取如果不存在,再通过findSubscriberMethodsWithReflection方法找然后存进缓存。然后过滤掉系统的类, 通过反射查找所有该类中所有方法,然后过滤掉不是public的
参数只有一个,如果有@Subscribe注解判断是不是一个参数,是不是public,找到所有函数之后,遍历找到所有事件处理函数并调用subscribe 方法将事件处理函数注册到Event
bus中,如果有事件处理函数设置了“sticky = true”,则立即处理该事件

我们再回到post方法,首先会将event对象添加到事件队列eventQueue中。然后判断是否有事件正在post,如果没有则会遍历eventQueue中每一个event对象,并且调用postSingleEvent方法post该事件。发送事件的时候还是要用handler来发送,接收也是一样

取消事件注册很简单,只是将register过程注册到EventBus的事件处理函数移除掉。

MAT和LeakCanary。MAT和LeakCanary的各自优缺点是什么?

一个类中声明private static Bitmap会引起什么问题

主要考察的是内存泄露,static变量所指向的内存引用,如果不把它设置为null,GC是永远不会回收这个对象的,必要的时候可以不用activity的context用application的

listview的卡顿分析和优化

getView方法里面convertView没有使用setTag和getTag方式;

每次getView都要执行 findViewById, 这是相当耗时的

布局里面有大图片或者背景所致;

单个item的布局太复杂, 或者 加载大图片, 或者从网上获取图片

listview 被多层嵌套,多次的onMessure导致卡顿

Adapter多余或者不合理的notifySetDataChanged;

listview加载图片会有什么问题,如果引起图片错位是什么原因导致的?怎么解决?

加载图片可能很卡或者错位,
很卡的话在滑动的时候停止加载图片,

错位的和可以使用第三方的加载框架解决,也可自己实现比如给ImageView增加一个tag就是图片的url,每次加载的时候判断一下tag和url是否一致

自己设计一个Picasso应该考虑哪些方面。

图片缓存(DiskLruCache,LruCache)、压缩、listview导致的错位、线程池的数量等

如何保证后台Service不被杀掉

本题解析:先大体介绍一下android的Service以及他的生命周期,其二 介绍出现哪些手机出现service被杀掉的问题,
比如红米手机,service运行一段时间后很容易就被杀掉问题,然后你如何解决Service不被杀掉的方法。
http://blog.csdn.net/iispring/article/details/47689819
回答:参考 http://blog.csdn.net/mad1989/article/details/22492519

为什么主线程可以使用handler

应为handler运行需要messagequeue 和looper 的支撑 使用handler必须要有looper 而主线程 也就是我们说的UI线程 它就是activityThread,ActivityThread被创建的时候就会初始化Lopper,ActivityThread被创建的时候就会初始化Lopper

为什么不允许在在子线程访问UI

因为UI控件是不线程安全的,如果在多线程中并发访问会导致UI控件处于不可预期状态 ?为什么系统不对UI控件的访问加锁呢?

  1. 加锁会造成UI控件访问复杂
  2. 加锁之后降低UI访问速度 ,锁会阻塞某些进程

Object的hashCode()与equals()的区别和作用

equals():反映的是对象或变量具体的值,即两个对象里面包含的值–可能是对象的引用,也可能是值类型的值

hashCode():计算出对象实例的哈希码

JDK中默认提供了哪些线程池,有何区别

newFixedThreadPool

创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

newCachedThreadPool

创建一个可缓存的线程池。这种类型的线程池特点是:
1).工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
2).如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

newSingleThreadExecutor

创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(我觉得这点是它的特色)。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的 。

newScheduleThreadPool

创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。(这种线程池原理暂还没完全了解透彻)

线程sleep和wait的区别

sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,

wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lockpool),如果线程重新获得对象的锁就可以进入就绪状态

StringBuilder和StringBuffer的区别

二者在执行速度方面的比较:StringBuilder > StringBuffer

StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的

StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

AsyncTask、Handler的使用

AsyncTask对Handler进行了封装

onPreExecute(主线程准备)

doInBackground(工作线程开始执行耗时操作)

onProgressUpdate(主线程 更新执行进度)

onPostExecute(主线程 成功返回结果)

dpi、sp、px的区别以及转换关系

mdpi 1dp=1px
hdpi 1dp=1.5px
xhdpi 1dp=2px
xxhdpi 1dp=3px

px=Density*dp

sp=dp?约等于

raw和assets文件夹的作用,二者有何区别

两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制

  1. res/raw中的文件会被映射到R.Java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。

  2. res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹

Android系统如何在多个资源文件夹下查找匹配最合适的资源

根据资源限定符,如果在正确分辨率下没有找到则找最符合的文件夹下,然后缩放

Android有哪些动画的实现方式

View Animation(视图动画)这种动画之后点击事件还在原位,它的位置根本没有变化,只是画它的时候进行了Matrix处理,使得它看起来变化了

属性动画要修改的属性一定要有set get方法 如果没有的话可以封装一下。ValueAnimation是两个数值,ObjectAnimation是目标view,和属性,还有数值,起始值是当前的值

帧动画多张图片

ViewAnimation与属性动画有什么区别

View Animation(视图动画)这种动画之后点击事件还在原位,它的位置根本没有变化,只是画它的时候进行了Matrix处理,使得它看起来变化了

属性动画的实现原理

根据传递数据的差值然后handler去实现逐帧更新数值去set属性的值

一般项目中如何加载大图

使用BitmapFactory.Options 中的inSampleSize(采样率)进行压缩

图片缓存技术

图片压缩的方式

降低图片质量RGP565

缩放法压缩(martix)

采样率压缩

质量压缩(不会改变大小尺寸)

如何不压缩图片加载高清图

加载大图 类似地图那种使用BitmapRegionDecoder主要用于显示图片的某一块矩形区域,分区域加载

图片占用内存大小分析

  1. 在对手机进行屏幕适时,可以只切一套图适配所有的手机。

但是如果只切一套小图,那在高屏幕密度手机上,会对图片进行放大,这样图片占用的内存往往比切相应图片放在高密度文件夹下,占用的内存还要大。

那如果只切一套大图放在高幕文件夹下,在小屏幕密度手机上,会缩小显示,按道理是行得通的。但系统在对图片进行缩放时,会进行大量计算,会对手机的性能有一定的影响。同时如果图片缩放比较狠,可能导致图片出现抖动或是毛边。

所以最好切出不同比便的图片放在不同幕度的文件夹下,对于性能要求不大高的图片,可以只切一套大图;

  1. 一张图片占用内存=图片长 图片宽 / (资源图片文件密度/手机屏幕密度)^2 每一象素占用字节数,所以图片占用内存跟图片本身大小、手机屏幕密度、图片所在的文件夹密度,图片编码的色彩格式有关;

  2. 对于网络图片,在不同屏幕密度的手机上加载出来,占用内存是一样的。

  3. 对于网络或是assets/手机本地图片加载,如果想通过设置 Options 里的 inDensity 或是 inTargetDensity 参数来调整图片的缩放比,必须两个参数均设置才能起作用,只设置一个,不会起作用。

  4. drawable 和 mipmap 文件夹存放图片的区别,首先图片放在 drawable-xhdpi 和 mipmap-xhdpi 下,两者占用的内存是一样的, Mipmaps 早在Android2.2+就可以用了,但是直到4.3 google才强烈建议使用。把图片放到 mipmaps 可以提高系统渲染图片的速度,提高图片质量,减少GPU压力。其他并没有什么区别。

如何进行用户行为收集

给每个域分配一定的ID,那么在用户使用某个功能时 我们动态记录这个ID(比如登陆和注册一般属于用户中心(001),登陆和注册输入两种功能,分别给03,04标记,登陆属于用户主动 那么可以给 01,注册被动跳转给02), 最后写入到本地保存,那么用户打开用户中心登陆产生的的数据信息就0010201 ,这样服务器能知道,我们只要在某个时间点将文本数据上传即可,即使没网络情况下我们也不怕,等设备有网的情况下 我们偷偷上报即可,那么我们也可以在用户登陆的时候时侯同时就上传这些数据,这个策略视具体功而定。
一般一个APP统计有模块域 ,功能域,事件域,由大到小分配而来,也有按页面区分的,具体看实际的需求场景而定

如何统一处理错误崩溃

写一个类继承UncaughtExceptionHandler,可以在程序崩溃后收集报错日志,然后保存下来,选择时候发送给服务器起做收集

是否自己开发过SDK?

设计模式在Android源码中的应用

单例模式

(1)单例模式一般没有接口,扩展很困难,若要扩展,只能修改代码来实现。
(2)单例对象如果持有Context,那么很容易引发内存泄露。此时需要注意传递给单例对象的Context最好是Application Context。

Builder模式

AlertDialog.Builder

责任链模式:事件分发

工厂方法模式 BitmapFactor

观察者模式 BaseAdapter

代理模式:ActivityManagerProxy代理类
ActivityManager是Android中管理和维护Activity的相关信息的类,为了隔离它与ActivityManagerService,有效降低二者的耦合,在这中间使用了ActivityManagerProxy代理类,所有对ActivityManagerService的访问都转换成对代理类的访问,这样ActivityManager就与ActivityManagerService解耦了。

组合模式 View和ViewGroup的嵌套组合

适配器模式 ListView的Adapter

装饰模式

MVC在android中的应用,利弊

逻辑都在activity里 耦合太重了,但是使用简单,明了

abstract 和 interface 的区别?

A. interface需要实现,要用implements,而abstract class需要继承,要用extends。

B. 一个类可以实现多个interface,但一个类只能继承一个abstract class。

C. interface强调特定功能的实现,而abstractclass强调所属关系。

D. 尽管interface实现类及abstrct class的子类都必须要实现相应的抽象方法,但实现的形式不同。interface中的每一个方法都是抽象方法,都只是声明的(declaration,没有方法体),实现类必须要实现。而abstractclass的子类可以有选择地实现。

ANR 产生的原因和解决步骤。

  1. 在5秒内没有响应输入的事件(例如,按键按下,屏幕触摸)
  2. BroadcastReceiver在10秒内没有执行完毕

所以当程序出现ANR时,系统会在/data/anr目录下创建一个traces.txt文件,通过分析文件就可以找到ANR的原因了

请解释安卓为啥要加签名机制

  1. 应用程序升级:如果你希望用户无缝升级到新的版本,那么你必须用同一个证书进行签名。这是由于只有以同一个证书签名,系统才会允许安装升级的应用程序。如果你采用了不同的证书,那么系统会要求你的应用程序采用不同的包名称,在这种情况下相当于安装了一个全新的应用程序。如果想升级应用程序,签名证书要相同,包名称要相同!
  2. 应用程序模块化:android系统可以允许同一个证书签名的多个应用程序在一个进程里运行,系统实际把他们作为一个单个的应用程序,此时就可以把我们的应用程序以模块的方式进行部署,而用户可以独立的升级其中的一个模块
  3. 代码或者数据共享:Android提供了基于签名的权限机制,那么一个应用程序就可以为另一个以相同证书签名的应用程序公开自己的功能。以同一个证书对多个应用程序进行签名,利用基于签名的权限检查,你就可以在应用程序间以安全的方式共享代码和数据了。

你觉得安卓开发最关键的技术在哪里?

底层了解和性能优化测试

ANR 具体产生的类型有哪些,具体说下其产生的最大超时时间。

  1. 在5秒内没有响应输入的事件(例如,按键按下,屏幕触摸)
  2. BroadcastReceiver在10秒内没有执行完毕

多线程多点下载的过程。

(1)首先获得下载文件的长度,然后设置本地文件的长度。

(2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。

如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M

谈下对 Java OOP 中多态的理解。

父类的引用可以指向本类

父类的引用可以指向子类对象

1) 创建本类对象时,调用的方法为本类方法

2)创建子类对象时,调用的方法为子类重写的方法或者继承的方法

继承是实现多态的基础。

Activty 和 Fragmengt 之间怎么通信,Fragmengt 和 Fragmengt 怎么通信?

Fragmengt 和 Fragmengt 通信

通过Activity实现fragmen的接口并传给另一fragment

getActivity()

怎么让自己的进程不被第三方应用杀掉,系统杀掉之后怎么能启动起来。

  1. Service设置成START_STICKY,kill 后会被重启(等待5秒左右),重传Intent,保持与重启前一样
  2. 通过 startForeground将进程设置为前台进程,做前台服务,优先级和前台应用一个级别,除非在系统内存非常缺,否则此进程不会被 kill
  3. 双进程Service:让2个进程互相保护,其中一个Service被清理后,另外没被清理的进程可以立即重启进程
  4. QQ黑科技:在应用退到后台后,另起一个只有 1 像素的页面停留在桌面上,让自己保持前台状态,保护自己不被后台清理工具杀死
  5. 在已经root的设备下,修改相应的权限文件,将App伪装成系统级的应用(Android4.0系列的一个漏洞,已经确认可行)
  6. Android系统中当前进程(Process)fork出来的子进程,被系统认为是两个不同的进程。当父进程被杀死的时候,子进程仍然可以存活,并不受影响。鉴于目前提到的在Android-Service层做双守护都会失败,我们可以fork出c进程,多进程守护。死循环在那检查是否还存在,具体的思路如下(Android5.0以下可行)用C编写守护进程(即子进程),守护进程做的事情就是循环检查目标进程是否存在,不存在则启动它。在NDK环境中将1中编写的C代码编译打包成可执行文件(BUILD_EXECUTABLE)。主进程启动时将守护进程放入私有目录下,赋予可执行权限,启动它即可。
  7. 联系厂商,加入白名单

说下平时开发中比较注意的一些问题

自定义view 效率高于xml定义吗?说明理由。

  1. xml定义应该指的是组合已有的控件达到某种效果吧
  2. 自定义View 减少了ViewGroup与View之间的测量,包括父量子,子量自身,子在父中位置摆放,当子view变化时,父的某些属性都会跟着变化.
  3. 效率就是一个View的 测量,摆放,的次数问题,与多个View的.

广播注册一般有几种,各有什么优缺点?

1)代码中注册 第一种不是常驻型广播,也就是说广播跟随程序的生命周期。

2) androidmanifest.xml中注册 第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
使用这样的方法注册弊端:它会始终处于活动状态,毕竟是手机开发,cpu和电源资源比较少,一直处于活动耗费大,不利。

你知道的安全加密有哪些?

http://blog.csdn.net/axi295309066/article/details/52491077

java的高内聚和低耦合

高内聚,是指让一个类或者一个方法让他专注去做一件事情。低耦合:这个又要求对象,类之间减少耦合性,更通俗的说比如你一个类的方法引用了很多的别的类,而你是直接new出来的,这样就会出现,你修改某个类之后,这个方法就会出现异常错误。

面试题:如何实现应用内多语言切换?

我们知道Android的多语言实现很简单,可以在不同的语言环境下使用不同的资源。在不同的res/value-xx下放置不同语言的strings.xml实现字符的本地化,而这个value-xx目录的选择是根据Resource中的Configuration.Locale这项的值来决定的。如zh中文,就会选择value-zh目录,如果没有匹配到(即APK中没有value-zh目录)就使用默认的value目录中的字符资源。

其实最终实现字符串的选择都是在Assets这个类中,通过Native的方法来加载相应的字符串资源。

然而,我们还是会有一些业务场景需要不根据Android系统的Locale配置就改变应用的语言。实现的方式也很简单,直接调用Android开放的接口Resources.updateConfiguration:

public static void changeSystemLanguage(Context context, String language) {
    if (context == null || TextUtils.isEmpty(language)) {
        return;
    }

    Resources resources = context.getResources();
    Configuration config = resources.getConfiguration();
    if (Locale.SIMPLIFIED_CHINESE.getLanguage().equals(language)) {
        config.locale = Locale.SIMPLIFIED_CHINESE;
    } else {
        config.locale = new Locale(language);
    }
    resources.updateConfiguration(config, null);
}

上面的代码,我们可以在应用内通过language的值指定是显示哪种语言,当然language值我们需要保存在Preferences或者数据库中。

好像很简单,我们的项目为什么还会出现问题呢?而且大家都不知道如何下手,因为在Android N之前的版本都是可以正常切换语言的。后来我跟了一下,发现在MainActivity和SplashActivity这些Activity有继承了自已扩展的BaseActivity,而这个BaseActivity有这样一段代码:

@Override
public Resources getResources() {
    Resources res = super.getResources();
    Configuration config = new Configuration();
    config.setToDefaults();
    res.updateConfiguration(config, res.getDisplayMetrics());
    return res;
}

config.setToDefaults会将Locale的值设为null,而再调用updateConfiguration可能会根据Android系统的语言重新设置Resources中的Locale。好吧,只是假设,还没有看到Android N的源代码。不过去掉这段代码后,在Android N(Preview)手机上切换语言正常了。

static、final

final类不能被继承,没有子类,final类中的方法默认是final的。

final方法不能被子类的方法覆盖,但可以被继承。

final成员变量表示常量,只能被赋值一次,赋值后值不再改变。

final不能用于修饰构造方法。

static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念。

被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。

用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象市,不生成static变量的副本,而是类的所有实例共享同一个static变量。

static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用(当然也可以在非静态成员方法中使用–废话),但是不能在其他类中通过类名来直接引用,这一点很重要。实际上你需要搞明白,private是访问权限限定,static表示不要实例化就可以使用,这样就容易理解多了。static前面加上其它访问权限关键字的效果也以此类推。

如何通过广播拦截和abort一条短信;广播是否可以请求网络;广播引起anr的时间限制

ApplicationContext和ActivityContext的区别

Application(getApplicationContext()) Context是和应用程序关联的,在应用的生命周期内总是一样的。

Activity(this) Context是关联在某个获得上的随着Activity的销毁而被销毁多次。