前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android面试知识笔记:那些年面试官常问的知识点...(附详细解析)

Android面试知识笔记:那些年面试官常问的知识点...(附详细解析)

原创
作者头像
Android技术干货分享
修改2020-08-06 09:50:16
4670
修改2020-08-06 09:50:16
举报
文章被收录于专栏:Android技术分享Android技术分享

有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

广播

注册方式:

1、静态注册 ,在Manifest文件的application节点中配置广播接收者

代码语言:javascript
复制
 <receiver android:name=".MyBroadCastReceiver">  
    <!-- android:priority属性是设置此接收者的优先级(从-1000到1000) -->
    <intent-filter android:priority="20">
    <actionandroid:name="android.provider.Telephony.SMS_RECEIVED"/>  
    </intent-filter>  
</receiver>  

2、动态注册,通过Context对象的registerReceiver方法注册广播

代码语言:javascript
复制
//new出上边定义好的BroadcastReceiver
MyBroadCastReceiver yBroadCastReceiver = new MyBroadCastReceiver();
//实例化过滤器并设置要过滤的广播  
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
//注册广播   
myContext.registerReceiver(smsBroadCastReceiver,intentFilter, 
             "android.permission.RECEIVE_SMS", null);

区别:静态注册的为常驻型广播,即使应用程序关闭了,如果又信息广播来,程序也会被系统调用执行。而动态注册的广播不是常驻型,广播被取消注册或者应用程序关闭后都不能接收

广播的两种类型:

1、有序广播:按照优先级,一级一级向下传递,接收者可以修改广播数据,也可以终止广播事件。 2、无序广播:所有接收者都会接收事件,不能被拦截跟修改。

服务

启动

1、使用Context的startService方法启动

onCreate()--->onStartCommand()--->onDestroy()

2、使用Context的bindService方法启动

onCreate()--->onBind()--->onUnBind()--->onDestroy()

停止

1、在外部使用stopService方法,如果使用bindService的方式启动,则使用unbindService方法停止

2、在Service内部(onStartCommand方法内)使用stopSelf

onStartCommand方法的返回值

1、START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand方法后,服务被异常kill掉,系统不会自动重启该服务

2、START_STICKY:如果Service进程被kill掉,保留Service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建Service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到Service,那么参数Intent将为null。

3、START_REDELIVER_INTENT:重传Intent。使用这个返回值时,系统会自动重启该服务,并将Intent的值传入。

IntentService

继承于Service,启动方式与Service的传统启动方式一样,不同点在于内部有一个线程来处理耗时操作,当任务执行完成时服务会自动停止。

Activity的启动模式

  • standard:标准模式,默认的启动模式,不管是否已经存在实例都会生成新的实例
  • singleTop:栈顶复用模式,如果发现有对应Activity的实例正位于栈顶,则直接打开此页面,不再生成新的实例,同时onNewIntent方法会被执行,onCreate跟onStart方法都不会执行。否则跟standard模式一样继续生成新的实例。
  • singleTask:站内复用模式,如果栈内存在对应Activity的实例就会复用这个Activity,复用时会将它上面的Activity全部出栈,同时onNewIntent方法也会被执行。
  • singleInstance:单例模式,该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。

Activity的启动过程

app启动的过程有两种情况,第一种是从桌面launcher上点击相应的应用图标,第二种是在activity中通过调用startActivity来启动一个新的activity。

1.Luncher.startActivitySafely()

代码语言:javascript
复制
public final class Launcher extends Activity
        implements View.OnClickListener, 
                    OnLongClickListener, 
                    LauncherModel.Callbacks, 
                    AllAppsView.Watcher {
                        
    ......
        
    void startActivitySafely(Intent intent, Object tag) {
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            startActivity(intent);
        } catch (ActivityNotFoundException e) {
            ......
        } catch (SecurityException e) {
            ......
        }
    }
                        
    ......
        
}

2.Activity.startActivity

代码语言:javascript
复制
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks {
 
    ......
 
    @Override
    public void startActivity(Intent intent) {
        startActivityForResult(intent, -1);
    }
 
    ......
 
}

3.Activity.startActivityForResult

代码语言:javascript
复制
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks {
 
    ......
 
    public void startActivityForResult(Intent intent, int requestCode) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode);
            ......
        } else {
            ......
        }
 
 
    ......
 
}

4.Instrumentation.execStartActivity

代码语言:javascript
复制
public class Instrumentation {
 
    ......
 
    public ActivityResult execStartActivity(
    Context who, IBinder contextThread, IBinder token, Activity target,
    Intent intent, int requestCode) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        if (mActivityMonitors != null) {
            ......
        }
        try {
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, intent,
                intent.resolveTypeIfNeeded(who.getContentResolver()),
                null, 0, token, target != null ? target.mEmbeddedID : null,
                requestCode, false, false);
            ......
        } catch (RemoteException e) {
        }
        return null;
    }
 
    ......
 
}

这里的ActivityManagerNative.getDefault返回ActivityManagerService的远程接口,即ActivityManagerProxy接口

5.ActivityManagerProxy.startActivity

代码语言:javascript
复制
class ActivityManagerProxy implements IActivityManager
{
 
    ......
 
    public int startActivity(IApplicationThread caller, Intent intent,
            String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
            IBinder resultTo, String resultWho,
            int requestCode, boolean onlyIfNeeded,
            boolean debug) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeTypedArray(grantedUriPermissions, 0);
        data.writeInt(grantedMode);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(onlyIfNeeded ? 1 : 0);
        data.writeInt(debug ? 1 : 0);
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }
 
    ......
 
}

6.ActivityManagerService.startActivity

Context

Context是一个抽象基类,翻译为上下文,也可以理解为环境,提供一些程序运行基础信息。

Context有两个子类,ContextWrapper是上下文功能的封装类,而 ContextImpl 则是上下文功能的实现类。而 ContextWrapper 又有三个直接的子类, ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一个带主题的封装类,而它有一个直接子类就是Activity,所以Activity和Service以及Application的Context是不一样的,只有Activity需要主题,Service不需要主题。Context一共有三种类型,分别是Application、Activity和Service。这三个类虽然分别各种承担着不同的作用,但它们都属于Context的一种,而它们具体Context的功能则是由ContextImpl类去实现的,因此在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。

Activity、Window、View三者之间的关系

  • Activity 构造的时候会初始化一个Window( PhoneWindw )
  • PhoneWindow 有一个 RootView ,这个RootView 是一个ViewGroup,是最初始的根视图
  • RootView 通过 addView 方法来一个个添加 View

View的绘制流程

View的绘制流程:onMeasure -> onLayout -> onDraw

第一步:onMeasure 测量视图大小,从顶层父View到子View递归调用 measure 方法,measure 方法又回调 onMeasure方法。

第二步:onLayout 确定View位置,进行页面布局。从顶层父View向子View递归调用 layout 方法的过程,即父View根据上一步 measure 得到的布局大小和布局参数,将子View放在合适的位置上。

第三步:onDraw 绘制视图。主要步骤为①:绘制背景,②:绘制自己,③:绘制子View,④:绘制滚动条

View、ViewGroup事件分发

ViewGroup 包含 dispatchTouchEvent 、onInterceptTouchEvent 、onTouchEvent三个相关方法,View包含 dispatchTouchEvent、onTouchEvent两个相关方法。

1.当 Activity 接收到Touch事件时,将遍历子View进行Down事件分发,分发的目的是为了找到真正处理本次完整触摸事件的View,这个View会在 onTouchEvent 返回true。

2.当某个子View返回true时,就终止事件分发,并同时在ViewGroup中记录该View,接下来的move事件跟up事件都由该子View直接进行处理。

3.当ViewGroup所有子View都不捕获Down事件时,将触发ViewGroup自身的 onTouchEvent 事件。触发的方式是调用 super.dispatchTouchEvent函数,即调用父View的dispatchTouchEvent方法。

Handler实现原理

Android的主线程不能进行耗时操作,子线程不能进行更新UI,所以就有了Handler,它的作用就是实现线程之间的通信。

Handler整个流程中主要有四个对象:Handler、Message、MessageQueue、Looper。通过将要传递的消息放在Message中,Handler通过 sendMessage 方法将消息放入 MessageQueue 中,Looper 对象会不断的调用loop() 方法不断从 MessageQueue 中取出 Message 交给 Handler进行处理。

Android内存泄露

1.内存泄漏跟内存溢出的区别:

  • 内存泄漏:指程序在申请内存后,无法释放已经申请的内存空间
  • 内存溢出:指程序在申请内存时,没有足够的内存空间供其使用

2.内存泄漏的原因:

  • Handler引起的内存泄漏: 将Handler声明为静态内部类,就不会持有外部类的引用,其生命周期就跟外部类无关。如果Handler内部要使用Context,则可以使用弱引用的方式。
  • 单例模式引起的内存泄漏: Context是ApplicationCotnext,ApplicationCotnext的生命周期与app一致,不会导致内存泄漏.
  • 非静态内部类创建实例引起的: 创建为静态实例
  • 非静态匿名内部类引起的: 将匿名内部类修改为静态的
  • 注册/反注册未成对使用引起的内存泄漏 注册广播接受器、EventBus等,记得解绑
  • 资源对象没有关闭引起的内存泄漏 在这些资源不使用的时候,记得调用相应的类似close()、destroy()、recycler()、release()等方法释放
  • 集合对象没有及时清理引起的内存泄漏 通常会把一些对象装入到集合中,当不使用的时候一定要记得及时清理集合,让相关对象不再被引用

3.内存泄漏检测:LeakCanary

ANR

ANR全名"Application not responding",即应用无响应。产生的原因:

  • 5s内无法响应用户输入事件
  • 广播在10s内无法结束
  • Service在20s内无法结束

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2019-2020面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 广播
  • 服务
    • IntentService
    • Activity的启动模式
    • Activity的启动过程
    • Context
    • Activity、Window、View三者之间的关系
    • View的绘制流程
    • View、ViewGroup事件分发
    • Handler实现原理
    • Android内存泄露
    • ANR
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档