Android Studio面试准备
一、onSaveInstanceState方法会在什么时候被执行?
1、当用户按下HOME键时。 这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则 2、长按HOME键,选择运行其他的程序时。 3、按下电源按键(关闭屏幕显示)时。 4、从activity A中启动一个新的activity时。 5、屏幕方向切换时,例如从竖屏切换到横屏时。
二、简述View Touch事件传递机制。
dispatchTouchEvent:进行事件的分发(传递)。返回值是 boolean 类型,受当前onTouchEvent和下级view的dispatchTouchEvent影响
onInterceptTouchEvent:对事件进行拦截。该方法只在ViewGroup中有,View(不包含 ViewGroup)是没有的。一旦拦截,则执行ViewGroup的onTouchEvent,在ViewGroup中处理事件,而不接着分发给View。且只调用一次,所以后面的事件都会交给ViewGroup处理。
onTouchEvent:进行事件处理。
三、为什么在子线程中执行 new Handler() 会抛出异常?
当创建Handler时会先获取当前线程的Looper,若Looper为null则抛出异常。
四、invalidate()和postInvalidate()的区别?
invalidate()与postInvalidate()都用于刷新View,主要区别是invalidate()在主线程中调用,若在子线程中使用需要配合handler;而postInvalidate()可在子线程中直接调用。
五、res目录和assets目录的区别?
res/raw中的文件会被映射到R.java文件中,访问时可直接使用资源ID,不可以有目录结构。
assets文件夹下的文件不会被映射到R.java中,访问时需要AssetManager类,可以创建子文件夹。
六、onTouch()、onTouchEvent()和onClick()关系?
优先度onTouch()>onTouchEvent()>onClick()。因此onTouchListener的onTouch()方法会先触发;如果onTouch()返回false才会接着触发onTouchEvent(),同样的,内置诸如onClick()事件的实现等等都基于onTouchEvent();如果onTouch()返回true,这些事件将不会被触发。
七、android中如何处理耗时操作, 有哪几种方法?为什么子线程不能更新UI?
Android处理耗时的操作基本思路为将耗时的操作放到非UI线程执行。常用的是AsyncTask,Handler和Thread,Loaders.
主要问题是java的线程安全,如果不在主线程更新ui,多个子线程同时给TextView设值,TextView的显示就会出现问题,不知道最终显示哪一个线程的值
八、
请写出下面代码输出结果是怎么样的。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | public class MainThreadTestActivity ``extends AppCompatActivity { ``private static final String TAG = MainThreadTestActivity.``class``.getSimpleName(); ``@Override ``protected void onCreate(Bundle savedInstanceState) { ``super``.onCreate(savedInstanceState); ``setContentView(R.layout.activity_main_thread_test); ``View view = ``new View(``this``); ``view.post(``new Runnable() { ``@Override ``public void run() { ``Log.i(TAG, ``"[view.post] >>>> 1 "``); ``} ``}); ``new Handler(Looper.getMainLooper()).post(``new Runnable() { ``@Override ``public void run() { ``Log.i(TAG, ``"[handler.post] >>>> 2"``); ``} ``}); ``runOnUiThread(``new Runnable() { ``@Override ``public void run() { ``Log.i(TAG, ``"[runOnUiThread] >>>>> 3"``); ``} ``}); ``new Thread(``new Runnable() { ``@Override ``public void run() { ``runOnUiThread(``new Runnable() { ``@Override ``public void run() { ``Log.i(TAG, ``"[runOnUiThread from thread] >>>> 4"``); ``} ``}); ``} ``}).start(); ``} } |
|---|
[runOnUiThread] >>>>> 3
[handler.post] >>>> 2
[runOnUiThread from thread] >>>> 4
[view.post] >>>> 1
九、软引用和弱引用的区别?
软引用关联的对象只有在内存不足时才会被回收,而被弱引用关联的对象在JVM进行垃圾回收时总会被回收。
十、SharedPreference的apply和commit的区别?
1、apply没有返回值,commit会返回一个Boolean值,表明是否修改成功
2、apply是将修改的数据提交到了内存,而后异步真正提交到硬件磁盘;而commit是同步提交到硬件磁盘,因此在多个并发的提交commit的时候,它们会等待正在处理的commit保存到磁盘后再操作,从而降低了效率;而apply只是原子的提交到内容,后面有调用apply的函数,将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率
3、apply方法不会提示任何失败的提示
由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要commit的。
十一、ScrollView下嵌套一个ListView通常会出现什么问题?如何解决?
当ScrollView嵌套ListView时,ListView的高度设置为wrap_content时会产生,一般情况下ListView只显示的第一个Item。
当ListView自身接收到的滑动事件时,使ScrollView取消拦截。ListView区域内的滑动事件由自己处理,ListView区域外滑动事件由外层ScrollView处理。可以系统自带的API来实现:requestDisallowInterceptTouchEvent这一方法。
十二、启动一个SingleTop模式的Activity,然后再次启动一次它,它的生命周期如何变化呢?
onCreate -> onStart -> onResume -> onPause -> onNewIntent -> onResume。
十三、FragmentPagerAdapter和FragmentStatePagerAdapter区别?
在于fragment 存储、恢复、销毁 的方式不同,当Viewpager中fragment数量多的时候用FragmentStatePagerAdapter,反之则用FragmentPagerAdapter。
十四、如何开启一个新的进程?Application在多进程下会多次调用onCreate() 吗?
当采用多进程的时候,比如下面的Service 配置:
| 1 2 3 4 5 | <service ``android:name=``".MyService" ``android:enabled=``"true" ``android:exported=``"false" ``android:process=``":remote" /> |
|---|
android:process 属性中 :的作用就是把这个名字附加到你的包所运行的标准进程名字的后面作为新的进程名称。
这样配置会调用 onCreate() 两次。
十五、什么是OOM?检测OOM的机制是什么?如何避免?
内存泄漏。
WeakReference与ReferenceQueue联合使用,在弱引用关联的对象被回收后,会将引用添加到ReferenceQueue;清空后,可以根据是否继续含有该引用来判定是否被回收;判定回收, 手动GC, 再次判定回收,采用双重判定来确保当前引用是否被回收的状态正确性;如果两次都未回收,则确定为泄漏对象。
如何避免内存泄露:
1.使用缓存技术,比如LruCache、DiskLruCache、对象重复并且频繁调用可以考虑对象池
2.对于引用生命周期不一样的对象,可以用软引用或弱引用SoftReferner WeakReferner
3.对于资源对象 使用finally 强制关闭
4.内存压力过大就要统一的管理内存
十六、请写出广播的两种注册形式。他们区别在哪?
第一种:使用代码进行订阅
- IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
- IncomingSMSReceiver receiver = new IncomingSMSReceiver();
- registerReceiver(receiver, filter);
第二种:在AndroidManifest.xml文件中的节点里进行订阅:
在AndroidManifest中进行注册后,不管改应用程序是否处于活动状态,都会进行监听。
在代码中进行注册后,当应用程序关闭后,就不再进行监听。
十七、如何设计开发一个图片轮播组件?简述要点或写代码。
function$( v ){if(typeofv ==='function'){
window.onload = v; }elseif(typeofv ==='string') {returndocument.getElementById(v); }elseif(typeofv ==='object') {returnv; }
}functiongetStyle( obj, attr ){returnobj.currentStyle ? obj.currentStyle[attr] : getComputedStyle( obj )[attr]; }functionaddZ(iNum){returniNum<10 ?"0"+iNum :""+iNum; }functiondoMove(obj,speed,end,attr,endFn){varoldAttr = parseInt(getStyle(obj,attr)); oldAttr>end ? speed = -speed : speed = speed; clearInterval(obj.timer); obj.timer = setInterval(function(){varnewAttr = parseInt(getStyle(obj,attr)) + speed; if(newAttr>=end&&speed>0||newAttr<=end&&speed<0){
clearInterval(obj.timer); obj.style[attr] = end+"px"; //if(endFn){endFn();} endFn && endFn(); }else{
obj.style[attr] = newAttr +"px"; }
},30);
十八、Kotlin中协程和线程的区别?
线程和协程的目的本质上存在差异:
- 线程的目的是提高CPU资源使用率, 使多个任务得以并行的运行, 所以线程是为了服务于机器的.
- 协程的目的是为了让多个任务之间更好的协作, 主要体现在代码逻辑上, 所以协程是为了服务于人的, 写代码的人. (也有可能结果会能提升资源的利用率, 但并不是原始目的)
在调度上, 协程跟线程也不同:
- 线程的调度是系统完成的, 一般是抢占式的, 根据优先级来分配, 是空分复用.
- 协程的调度是开发者根据程序逻辑指定好的, 在不同的时期把资源合理的分配给不同的任务, 是时分复用的.
作用上的不同:
-
协程确保了代码逻辑是顺序的, 不管同步操作要是异步操作, 前一个完成, 后一个才会开始.
-
线程可以被调度到CPU上执行, 这样代码才能真正运行起来.
-
十九、Activity A 跳转Activity B,Activity B再按back键回退,两个过程各自的生命周期
-
ActivityA跳转ActivityB的过程中,各自生命周期的执行顺序。例如:A.onCreate A.onStart A.onPause A.onStop B.onCreate B.onStart B.onPause B.onStop B.onDestroy?
ActivityA和ActivityB生命周期执行顺序如下: A.onPause -> B.onCreate -> B.onStart-> B.onResume-> A.onStop -
ActivityB 按back键呢回退
按下back键后: B.onPause->A.onRestart->A.onStart->A.onResume->B.onStop->B.onDestory -
二十、聊聊RecyclerView的缓存机制。
RecyclerView是四级缓存。
一级缓存 mAttachedScrap和mChangedScrap 这是优先级最高的缓存,RecyclerView在获取ViewHolder时,优先会到这两个缓存来找。其中mAttachedScrap存储的是当前还在屏幕中的ViewHolder,mChangedScrap存储的是数据被更新的ViewHolder,比如说调用了Adapter的notifyItemChanged方法。可能有人对这两个缓存还是有点疑惑,不要急,待会会详细的解释。
二级缓存 mCachedViews 默认大小为2,通常用来存储预取的ViewHolder,同时在回收ViewHolder时,也会可能存储一部分的ViewHolder,这部分的ViewHolder通常来说,意义跟一级缓存差不多。
三级缓存 ViewCacheExtension 自定义缓存,通常用不到,在本文中先忽略
四级缓存 RecyclerViewPool 根据ViewType来缓存ViewHolder,每个ViewType的数组大小为5,可以动态的改变。
二十一、给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
class Solution {
public``:
``/**
``* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
``*
``* @param number int整型
``* @return int整型
``*/
``int reverse(``int number) {
``int res=0;
``while``(number!=0)
``{
``int temp=number%10;
``number=number/10;
``res=res*10+temp;
``}
``if``(res>INT_MAX||res<INT_MIN) ``return 0;
``return res;
``}
};
二十二、 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
- import java.util.*;
public class Solution {
/**
- 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
- @param s string字符串
- @return string字符串
*/
public String longestPalindrome (String s) {
// write code here
boolean[][] dp = new boolean[s.length()][s.length()];
int left = 0, right = 0, len = 0;
for (int i = 0; i < s.length(); ++i) {
for (int j = 0; j < i; ++j) {
dp[j][i] = (s.charAt(i) == s.charAt(j) && (i - j < 2 || dp[j + 1][i - 1]));
if (dp[j][i] && len < i - j + 1) {
len = i - j + 1;
left = j;
right = i;
}
}
dp[i][i] = true;
}
return s.substring(left, right + 1);
}
}
- 二十三、给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。
- import java.util.*;
public class Solution {
/**
-
代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
-
@param s string字符串
-
@return int整型
*/
public int longestValidParentheses (String s) {
// write code here
int max = 0,start = 0;
if(null == s) return 0;
Stackstack = new Stack<>();
for (int i = 0;i<s.length();i++){
if('(' == s.charAt(i)){
stack.push(i);
continue;
}else {
if(stack.isEmpty()){
start = i + 1;
continue;
}else {
stack.pop();
if(stack.isEmpty()){
max = Math.max(max, i - start+1);
}else {
max = Math.max(max, i - stack.peek());
}
}
}
}
return max;
}
}- 二十四、给你两个二进制字符串,返回它们的和(用二进制表示)。
输入为 非空 字符串且只包含数字 1 和 0。
class Solution {
public``:
``/**
``* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
``*
``* @param a string字符串
``* @param b string字符串
``* @return string字符串
``*/
``string addBinary(string a, string b) {
StringBuffer result = ``new StringBuffer();
``int alen = a.length();
``int blen = b.length();
``int sum = 0;
``for (``int i = alen - 1, j = blen - 1; i >= 0 || j >= 0; i--,j--) {
``if (i >= 0) {
``sum += a.charAt(i) - ``'0'``;
``}
``if (j >= 0) {
``sum += b.charAt(j) - ``'0'``;
``}
``// 求余后,算进位。比如sum=3,则这一位为3%2=1,进位3/2=1
``result.append(sum % 2);
``sum = sum / 2;
``}
``if (sum > 0) {
``result.append(1);
``}
``// 需要反转string哟~
``return result.reverse().toString();
``}
};
二十五、
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
class Solution { public``: ``/** ``* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 ``* ``* @param n int整型 ``* @return int整型 ``*/ ``int climbStairs(``int n) { int dp[n+1]; ``int i=0; ``for``(i=0;i<3&&i<=n;i++) dp[i]=i; ``for``(i=3;i<=n;i++) ``{ ``dp[i]=dp[i-1]+dp[i-2]; ``} ``return dp[n]; ``} }; |
|---|
