Android总结

String 和 StringBuffer、StringBuilder的区别

String是不可变对象,StringBuffer和StringBuilder是可变对象
StringBuffer是线程安全的,StringBuilder是非线程安全的
执行速度:StringBuilder>StringBuffer>String(String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,String对象一旦创建后该对象是不可更改的,后两者的对象是变量,是可以更改的)

equals和“==” ,hashCode的区别

==是比较两个基本数据数值是否相等或者两个变量内存地址是否相同
equals 是比较两个独立对象的内容是否相同
hashCode 一个类重写equals()一般比较的复杂,这样效率就很低,而利用hashCode()继续对比,只需要生成一个hash值就可以进行对比
注:hashCode不相同,则说明两个对象不相等,但是hashCode相同时,还需要判断equals(),只有当equals()也相同时,两个对象才相等

Java中浅拷贝和深拷贝的区别

浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递的拷贝
深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容

Java中Exception和Error的区别

Exception:是java程序运行中已知的异常情况,咱们可以获取到这种异常,并且对这种异常进行业务外的处理
Error:是java程序运行中不可预料的异常情况,这种异常发生以后,会直接导致JVM不可处理或者不可恢复的情况。所以这种异常不可能抓取到,比如OutOfMemoryError、NoClassDefFoundError等

什么是反射机制

Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法
应用场景:

  1. 逆向代码,例如反编译
  2. 与注解相结合的框架,如 Retrofit
  3. 单纯的反射机制应用框架,例如 EventBus(事件总线)
  4. 动态生成类框架 例如Gson

BIO、NIO、AIO 有什么区别

BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统IO,它的特点是模式简单使用方便,并发处理能力低。
NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

Java注解

Java注解它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。

List,Set,Map的区别

List:存储的数据是有顺序的,并且值允许重复
Set:存储的数据是无顺序的,并且不允许重复
Map:存储的数据是无序的,它的键是不允许重复的,但是值是允许重复的

ArrayList和LinkedList的区别

  1. ArrayList是基于数组的数据结构,LinkedList是基于链表的数据结构。
  2. ArrayList适用于查询操作,LinkedList适用于插入和删除操作。

线程sleep()和wait()的区别

sleep 是Thread类的方法,wait是Object类的方法
sleep不释放锁,wait释放锁
sleep不需要Synchronized ,wait需要Synchronized
sleep不需要唤醒,wait需要唤醒(除wait(int time))

强、软、弱、虚引用以及它们之间的区别

强引用:强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
软引用:如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。
弱引用:在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
虚引用:如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

Activity的四种启动模式

Standard:标准模式,是系统默认的启动模式,每次启动Activity,无论任务栈中是否已经有这个Activity的实例,系统都会创建一个新的Activity实例
SingleTop:栈顶复用模式,当singleTop模式的Activity已经位于任务栈的栈顶,再去启动它时,不会再去创建新的实例,如果不位于栈顶,就会创建新的实例。
SingleTask:栈内复用模式,SingleTask模式的Activity在同一个Task内只有一个实例,如果Activity已经位于栈顶,系统不会创建新的Activity实例,和SingleTop模式一样。但Activity已经存在但不位于栈顶时,系统就会把该Activity移到栈顶,并把它上面的activity出栈。
SingleInstance:全局单例模式,SingleInstance 模式常应用于独立栈操作的应用,如闹钟的提醒页面,当你在A应用中看视频时,闹钟响了,你点击闹钟提醒通知后进入提醒详情页面,然后点击返回就再次回到A的视频页面,这样就不会过多干扰到用户先前的操作了。

BroadcastReceiver 与LocalBroadcastReceiver 有什么区别

BroadcastReceiver 是跨应用广播
LocalBroadcastReceiver 是应用内广播,通过 LocalBroadcastManager.getInstance(Context).sendBroadcast(intent)发送本地广播

对于 Context,你了解多少

Context 也叫上下文,是有关应用程序环境的全局信息的接口。这是一个抽象类, 它允许访问特定于应用程序的资源和类,以及对应用程序级操作的调用,比如启动活动,发送广播和接收意图等;
Activity, Service, Application 都是 Context 的子类。Context 的具体实现类是 ContextImpl, 还有一个包装类ContextWrapper, ContextWrapper 的子类有 Service,Application,ContextThemeWrapper, Activity 又是ContextThemeWrapper 的子类,ContextThemeWrapper 也可以叫 UI Context,跟UI 操作相关的最好使用此类 Context。
对于 startActivity操作
当为Activity Context则可直接使用;
当为其他Context, 则必须带上FLAG_ACTIVITY_NEW_TASK flags才能使用;因为非 Activity context 启动 Activity 没有 Activity 栈,则无法启动,因此需要加开启新的栈;
另外UI相关要Activity中使用

getApplication()和getApplicationContext() 区别

  1. 对于Activity/Service来说, getApplication()和
    getApplicationContext()的返回值完全相同;
  2. BroadcastReceiver在onReceive的过程,或者ContentProvider中,能使用context.getApplicationContext获取所在Application, 而无法使用getApplication;

startService和bindService的区别,生命周期以及使用场景

执行startService时,Service会经历onCreate-onStartCommand。当执行stopService时,直接调用onDestroy方法。调用者如果没有stopService,Service会一直在后台运行,下次调用者再起来仍然可以stopService。

执行bindService时,Service会经历onCreate-onBind。这个时候调用者和Service绑定在一起。调用者调用unbindService方法或者调用者Context不存在了(如Activity被finish了),Service就会调用onUnbind-onDestroy。这里所谓的绑定在一起就是说两者共存亡了。

多次调用startService,该Service只能被创建一次,即该Service的onCreate方法只会被调用一次。但是每次调用startService,onStartCommand方法都会被调用。

第一次执行bindService时,onCreate和onBind方法会被调用,但是多次执行bindService时,onCreate和onBind方法并不会被多次调用,即并不会多次创建服务和绑定服务。

切换横竖屏时Activity的生命周期

AndroidManifest没有设置configChanges属性

竖屏启动:
onCreate -->onStart–>onResume
切换横屏:
onPause -->onSaveInstanceState -->onStop –
onDestroy -->onCreate–>onStart -->
onRestoreInstanceState–>onResume -->onPause –
onStop -->onDestroy
(Android 6.0 Android 7.0 Android 8.0)

configChanges设置orientation keyboardHidden screenSize,这三项界面不会走Activity的生命周期,只会回调onConfigurationChanged方法。

configChanges属性介绍

android:configChanges是一个Activity的属性,用于指定在配置更改(例如屏幕旋转、键盘显示等)发生时,我们希望自己处理配置更改而不是系统重新创建Activity。
步骤一:为目标Activity添加android:configChanges属性


    ...

步骤二:重写onConfigurationChanged方法

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    // 在这里处理配置更改
}

步骤三:处理配置更改
例如:屏幕旋转

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        // 屏幕变为横向
        setContentView(R.layout.activity_main_landscape);
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        // 屏幕变为纵向
        setContentView(R.layout.activity_main_portrait);
    }
}

configChanges属性

mcc SIM卡唯一标识IMSI(国际移动用户识别码)中的国家代码,由三位数字组成
mnc SIM卡唯一标识IMSI(国际移动用户识别码)中的运营商代码,由两位数字组成
locale 设备的本地位置发生了改变,一般指切换了系统语言
touchscreen 触摸屏发生了改变
keyboard 键盘类型发生了改变,比如使用了外插键盘
keyboardHidden 键盘的可访问性发生了改变
navigation 系统导航方式发生了改变
screenLayout 屏幕布局发生了改变
fontScale 系统字体缩放比例发生了改变
uiMode 用户界面发生了改变,比如使用夜间模式
orientation 屏幕方向发生了改变,经常遇到
screenSize 屏幕尺寸发生了改变,比如横竖屏切换
smallestScreenSize 屏幕的物理大小改变了,如:连接到一个外部的屏幕上
layoutDirection 布局方向发生变化

Activity中onNewIntent方法的调用时机和使用场景?

前提:ActivityA已经启动过,处于当前应用的Activity任务栈中;

当ActivityA的LaunchMode为Standard时:

由于每次启动ActivityA都是启动新的实例,和原来启动的没关系,所以不会调用原来ActivityA的onNewIntent方法

当ActivityA的LaunchMode为SingleTop时:

如果ActivityA在栈顶,且现在要再启动ActivityA,这时会调用onNewIntent()方法 ,生命周期顺序为:

onCreate—>onStart—>onResume—onPause—>onNewIntent—>onResume

当ActivityA的LaunchMode为SingleInstance,SingleTask:

如果ActivityA已经在任务栈中,再次启动ActivityA,那么此时会调用onNewIntent()方法,生命周期调用顺序为:

onPause—>跳转其它页面—>onCreate—>onStart—>onResume—onPause—>跳转A—>onNewIntent—>onRestart—>onStart—>onResume

总的来说,只对SingleTop(且位于栈顶),SingleTask和SingleInstance(且已经在任务栈中存在实例)的情况下,再次启动它们时才会调用,即只对startActivity有效,对仅仅从后台切换到前台而不再次启动的情形,不会触发onNewIntent。

Intent传输数据的大小有限制吗?

Intent传输数据的大小受Binder的限制,上限是1M。不过这个1M并不是安全的上限,Binder可能在处理别的工作,安全上限是多少这个在不同的机型上也不一样。

Intent 中的 Bundle 是使用 Binder 机制进行数据传送的, 数据会写到 Binder 缓冲区域;Binder 的缓冲区是有大小限制的, 有些 ROM 是 1M, 有些 ROM 是 2M。Binder 本身就是为了进程间频繁的通信所设计的, 并不是为了拷贝大量数据。

传递数据序列化

intent 的 bundle 的源码可以看到它们都是实现了 Parcelable ,其实就是通过序列化来实现通信的。

Serializable是Java为我们提供的一个标准化的序列化接口,那什么是序列化呢? 简单来说就是将对象转换为可以传输的二进制流(二进制序列)的过程,这样我们就可以通过序列化,转化为可以在网络传输或者保存到本地的流(序列),从而进行传输数据 ,那反序列化就是从二进制流(序列)转化为对象的过程。

Parcelable 的底层使用了 Parcel 机制。传递实际上是使用了 binder 机制,binder 机制会将 Parcel序列化的数据写入到一个共享内存中,读取时也是 binder 从共享内存中读出字节流,然后 Parcel 反序列化后使用。这就是 Intent 或 Bundle 能够在 activity或者跨进程通信的原理。

关于 Parcelable 和 Serializable 的区别:
Parcelable 和 Serializable 都是实现序列化并且都可以用于 Intent 间传递数据, Serializable 是 Java 的实现方式,可能会频繁的 IO 操作,所以消耗比较大,但是实现方式简单 Parcelable 是 Android 提供的方式, 效率比较高,但是实现起来复杂一些。

HandlerThread的使用场景和用法

HandlerThread 本质上是一个在子线程的handler

// 步骤1:创建HandlerThread实例对象
// 传入参数 = 线程名字,作用 = 标记该线程
   HandlerThread mHandlerThread = new HandlerThread("handlerThread");

// 步骤2:启动线程
   mHandlerThread.start();

// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
  Handler workHandler = new Handler( mHandlerThread.getLooper() ) {
            @Override
            public boolean handleMessage(Message msg) {
                ...//消息处理
                return true;
            }
        });

// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
  // a. 定义要发送的消息
  Message msg = Message.obtain();
  msg.what = 2; //消息的标识
  msg.obj = "B"; // 消息的存放
  // b. 通过Handler发送消息到其绑定的消息队列
  workHandler.sendMessage(msg);

// 步骤5:结束线程,即停止线程的消息循环
  mHandlerThread.quit();

AsyncTask已在API 级别 30 中弃用,建议改用标准 java.util.concurrent 或Kotlin 并发实用程序

Activity.runOnUiThread 的理解

一般是用来将一个runnable绑定到主线程,在runOnUiThread源码里面会判断当前runnable是否是主线程,如果是直接run,如果不是,通过一个默认的空构造函数handler将runnable post 到looper里面,创建构造函数handler,会默认绑定一个主线程的looper对象。

介绍一下Handler机制?

Handler消息机制主要的四个类的功能

Message:信息的携带者,持有了Handler,存在MessageQueue中,一个线程可以有多个
Hanlder:消息的发起者,发送Message以及消息处理的回调实现,一个线程可以有多个Handler对象
Looper:消息的遍历者,从MessageQueue中循环取出Message进行处理,一个线程最多只有一个
MessageQueue:消息队列,存放了Handler发送的消息,供Looper循环取消息,一个线程最多只有一个

Android动画

  1. 补间动画
    补间动画就是我们只需指定开始、结束的“关键帧“,而变化中的其他帧由系统来计算,不必自己一帧帧的去定义

  2. 帧动画
    帧动画指的是一张张静态图片设计成动态画面放映,是一组图片的播放顺序,通常用于动画效果相对单一的场景。

  3. 属性动画
    属性动画是Android中最强大的动画效果之一,直接更改我们对象的属性,它可以对任何一个对象的任何一个属性实现平滑的过渡动画。

谈Android的事件分发机制?

1、Android事件分发是先传递到ViewGroup,再由ViewGroup传递到View的。
2、在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。

Android自定义View / ViewGroup的步骤大致如下:

  1. 自定义属性;
  2. 选择和设置构造方法;
  3. 重写onMeasure()方法;
  4. 重写onDraw()方法;
  5. 重写onLayout()方法;
  6. 重写其他事件的方法(滑动监听等)

测量模式分为以下三种情况:

  1. EXACTLY:当宽高值设置为具体值时使用,如100DIP、
    match_parent等,此时取出的size是精确的尺寸;
  2. AT_MOST:当宽高值设置为wrap_content时使用,此
    时取出的size是控件最大可获得的空间;
  3. UNSPECIFIED:当没有指定宽高值时使用(很少见)。

View获取宽高的几种方法?

相信有很多朋友都有过在 Activity 中通过 getWidth() 之类的方法获取 View 的宽高值,可能在 onCreate() 生命周期方法中,也可能在 onResume() 生命周期方法中。然而,不幸的是,并不能获取所要的结果,宽高值均为 0。

如果对 View 的绘制显示流程熟悉的话,就会知道问题所在。我们知道,在自定义 View 时,通常都要重写 onMeasure、onLayout、onDraw 这几个方法。同理,Activity 的内容显示到设备上时,这些 View 也要经历这些阶段。

所以,当我们在 Activity 的生命周期方法中直接获取 View 的宽高时,View 也许还没执行完 measure 阶段,那么自然获取到的宽高结果为 0。这也提醒我们一点,在 onCreate 方法中只适合做些一些初始化设置工作,使用 View 执行动画或者其他操作时,一定要注意考虑 View 绘制的耗时过程。

获取View宽高的方法
1、OnGlobalLayoutListener获取

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        if (Build.VERSION.SDK_INT >= 16) {
            view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }else {
            view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
        int width = view.getWidth();
    }
});

ViewTreeObserver,顾名思义,视图树的观察者,可以监听 View 的全局变化事件。比如,layout 变化,draw 事件等。可以阅读源码了解更多事件。

注意:使用时需要注意及时移除该事件的监听,避免后续每一次发生全局 View 变化均触发该事件,影响性能。这里用的是 OnGlobalLayoutListener,移除监听时注意 API 的版本兼容处理。
2、OnPreDrawListener获取

view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
        view.getViewTreeObserver().removeOnPreDrawListener(this);
        int width = view.getWidth();
        return false;
    }
});

3、使用View.post()方法

view.post(new Runnable() {
    @Override
    public void run() {
        int width = view.getWidth();
    }
});

4、OnLayoutChangeListener获取

view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        view.removeOnLayoutChangeListener(this);
        int width = view.getWidth();
    }
});

getMeasuredWidth() 与 getWidth() 或者 getMeasuredHeight() 与 getHeight() 的区别

measuredWidth 与 width 分别对应于视图绘制 的 measure 与 layout 阶段。很重要的一点是,我们要明白,View 的宽高是由 View 本身和 parent 容器共同决定的,要知道有这个 MeasureSpec 类的存在。

比如,View 通过自身 measure() 方法向 parent 请求 100x100 的宽高,那么这个宽高就是 measuredWidth 和 measuredHeight 值。但是,在 parent 的 onLayout() 阶段,通过 childview.layout() 方法只分配给 childview 50x50 的宽高。那么,这个 50x50 宽高就是 childview 实际绘制并显示到屏幕的宽高,也就是 width 和 height 值。

如果你对自定义 View 过程很熟练的话,理解这部分内容就比较轻松一些。事实上,开发过程中,getWidth() 和 getHeight() 方法用的更多一些。

invalidate()和postInvalidate()方法的区别和应用场景

  1. invalidate()用来重绘UI,需要在UI线程调用。
  2. postInvalidate()也是用来重新绘制UI,它可以在UI线程调用,也可以在子线程中调用,postInvalidate()方法内部通过Handler发送了一个消息将线程切回到UI线程通知重新绘制,并不是说postInvalidate()可以在子线程更新UI,本质上还是在UI线程发生重绘,只不过我们使用postInvalidate()它内部会帮我们切换线程。

Fragment的生命周期

onAttach(Context context):在Fragment和Activity关联上的时候调用,且仅调用一次。在回调中可以将参数 context 转换为 Activity保存下来,避免后期频繁获取 activity。
onCreate():第一次启动的时候调用
onCreateView():在准备绘制Fragment界面时调用,返回值为Fragment要绘制布局的根视图,当然也可以返回null。注意使用inflater构建View时一定要将attachToRoot指明false,因为Fragment会自动将视图添加到container中,attachToRoot为true会重复添加报错。onCreateView并不是一定会被调用,当添加的是没有界面的Fragment就不会调用,比如调用FragmentTransaction的 add(Fragment fragment, String tag)方法。
onActivityCreated:activity 的onCreated 执行完时调用
onStart:可见时调用
onResume:界面可以点击时调用
onPause:界面不可点击时调用
onStop:不可见时调用
onDestroyView:移除 fragment 相关视图时调用
onDestroy:清除 fragmetn 状态是调用
onDetach:和 activity 解除关联时调用

Activity和Fragment之间数据的传递

1、使用 bundle
2、接口回调
3、在创建的时候通过构造直接传入
4、使用 EventBus

什么是同步屏障

这里我们假设一个场景:我们向主线程发送了一个UI绘制操作Message,而此时消息队列中的消息非常多,那么这个Message的处理可能会得到延迟,绘制不及时造成界面卡顿。同步屏障机制的作用,是让这个绘制消息得以越过其他的消息,优先被执行。

什么是同步屏障?

开启同步屏障的第一步需要发送一个特殊消息作为屏障消息,当消息队列检测到了这种消息后,就会从这个消息开始,遍历后续的消息,只处理其中被标记为“异步”的消息,忽略同步消息(所以叫“同步屏障”),相当于给一部分消息开设了“VIP”优先通道。当使用完同步屏障后我们还注意移除屏障。

那么同步屏障如何使用,如何运行的呢,主要分为以下四步:

  • 发送屏障消息(Message中target为空,target的类型是Handler)
  • 发送异步消息(发送被标记为asynchronous的消息)
  • 处理消息
  • 移除屏障消息(通过发送屏障消息时返回的token来删除消息)

ViewDragHelper

系统View拖拽工具类

Android性能优化有哪些

  • 启动优化: application中不要做大量耗时操作,如果必须的话,建议异步做耗时操作
  • 布局优化:使用合理的控件选择,少嵌套。(合理使用include,merge,viewStub等使用)
  • apk优化(资源文件优化,代码优化,lint检查,.9.png,合理使用shape替代图片,webp等)
  • 性能优化,网络优化,电量优化
    避免轮询,尽量使用推送。
    应用处于后台时,禁用某些数据传输
    限制访问频率,失败后不要无限重连
    选用合适的定位服务(GPS定位,网络定位,被动定位)
    使用缓存
  • 内存优化
    循环尽量不使用局部变量
    避免在onDraw中创建对象,onDraw会被频繁调用,容易造成内存抖动。循环中创建大的对象,也是如此。
    不用的对象及时释放
    数据库的cursor及时关闭
    adapter使用缓存
    注册广播后,在生命周期结束时反注册
    及时关闭流操作
    图片尽量使用软引用,较大的图片可以通过bitmapFactory缩放后再使用,并及时recycler。另外加载巨图时不要 使用 setImageBitmap或setImageResourse或BitmapFactory.decodeResource,这些方法拿到的都是bitmap的对象,占用内存较大。可以用BitmapFactory.decodeStream方法配合BitmapFactory.Options进行缩放避免static成员变量引用资源耗费过多实例避免静态内部类的引用

一般什么情况下会导致内存泄漏

  • 资源对象没关闭造成的内存泄漏(如: Cursor、File等)
  • Bitmap 对象不在使用时调用recycle()释放内存
  • 集合中对象没清理造成的内存泄漏(特别是 static 修饰的集合)
  • 广播注册没取消造成的内存泄漏
  • Activity 的 Context 造成的泄漏,可以使用ApplicationContext
  • Handler 造成的内存泄漏问题(一般由于 Handler 生命周期比其外部类的生命周期长引起的)

自定义 Handler 时如何有效地避免内存泄漏问题?

1.自定义的静态handler
2.可以加一个弱引用
3.还有一个主意的就是当你activity被销毁的时候如果还有
消息没有发出去 就remove掉吧
4.removecallbacksandmessages去清除Message和
Runnable 加null 写在生命周的ondestroy()就行

哪些情况下会导致oom问题?

加载大图片
内存泄漏:应用程序中存在未正确释放的对象引用,导致这些对象无法被垃圾回收器回收,最终占用大量内存导致OOM异常。

ANR 出现的场景以及解决方案?

在Android中,应用的响应性被活动管理器(Activity Manager)和窗口管理器(Window Manager)这两个系统服务所监视。当用户触发了输入事件(如键盘输入,点击按钮等),如果应用5秒内没有响应用户的输入事件,那么,Android会认为该应用无响应,便弹出ANR对话框。而弹出ANR异常,也主要是为了提升用户体验。

解决方案是对于耗时的操作,比如访问网络、访问数据库等操作,需要开辟子线程,在子线程处理耗时的操作,主线程主要实现UI的操作

Android 中的图片优化方案?

1.首先我们可以对图片进行二次采样,从本质上减少图片的内存占用。就是将大图片缩小之后放入到内存中,以实
现减小内存的目的
2.其次就是采用三层缓存架构,提高图片的访问速度。三
层缓存架构是内存-文件-网络。
    内存是访问速度最快的部分但是分配的空间有限,所以不可能占用太多。其中内存缓存可以采用LRU算法(最近最少使用算法),来确定要删除内存中的那些图片,保存那些图片。
    文件就是将图片保存到本地,可以使SD卡中,也可以是手机内部存储中。
    网络就是访问网络下载图片,进行图片的加载。
3.最后一点,也是图片优化最重要的一点。重用Bitmap.
4.不使用bitmap要记得实时回收,减小内存的开销

如何处理大图的加载?

1、首先确定大图的用途,精度需求:
a)完整显示,对精度要求不高,图片本身就很大
b)对精度需求比较高,不需要完整显示
2、解决方案
a)针对第一种的处理图片本身,按需加载(根据显示设备本身大小进行缩放),降低精度加载(改变图片模式,如将ARGB8888改成RGB565,ARGB4444),修改图片格式(png改成webp,jpg)
b)第二种的一般采用局部加载,主要要用到的是BitmapRegionDecoder这个类decodeRegion的方法,读取图片指定大小的数据,然后通过移动来动态改变显示区域的图片

请谈谈如何加载Bitmap并防止内存溢出?

首先我们 要知道bitmap内存是怎么计算的例子:
手机屏幕大小 1080 x 1920(inTarget = 420),加载xhdpi (inDensity = 320)中的图片 1920 x 1080,scale
= 420 / 320,最总我们可以得知 他的占用内存 1418 * 2520 * 4很明显 被放大了。
防止内存溢出:
1.对图片进行内存压缩;
2.高分辨率的图片放入对应文件夹;
3.内存复用
4.及时回收

TCP 、UDP协议的区别

TCP英文叫Transmission Control Protocol,中文叫传输控制协议,它其实就是一种网络传输协议。

汽车在公路上行驶,需要遵守交通规则,同样数据在网络上传输也要遵守一套规则,这个规则就叫协议。

而TCP是一种面向连接的协议,也就是说,在收发数据前,必须和对方确认已经建立了可靠的连接。

两者的区别:

1、TCP是面向连接的,UDP是面向无连接的;

2、UDP程序结构较简单;

3、TCP是面向字节流的,UDP是基于数据报的;

4、TCP保证数据正确性,UDP可能丢包;

5、TCP保证数据顺序,UDP不保证。

android中进程间通信的几种方式

1、使用Bundle

我们都知道Android中三大组件Activity,Service,Receiver都支持在Intent中传递Bundle数据,而Bundle实现了Parcelable接口,所以它可以方便的在不同的进程间进行传输。当我我们在一个进程中启动另外一个进程的Activity、Service、Receiver时,我们就可以在Bundle中附加我们所需要传输给远程进程的信息并通过intent发送出去。这里注意,我们传输的数据必须能够被序列化。

private void startWithIntent(){
    Intent intent = new Intent();
    //制定要打开的程序的包名(必须写全包名,不然会报错)和地址(activity名)
    intent.setComponent(new ComponentName("PackageName", 
                        "PackageName.intentIpcActivity"));
    //通过budle传递数据,可以携带序列化数据
    Bundle bundle = new Bundle();
    bundle.putInt("intextra", 0);
    bundle.putString("stringextra", "测试数据");
    intent.putExtras(bundle);
    try{
        startActivity(intent);
    }catch(Exception e){
        ToastUtils.showMessage("没有找到对应文件");
    }
}

2、使用文件共享
3、使用AIDL

AIDL (Android Interface Definition Language)是一种IDL语言,用于生成可以在Android设备上两个进程之间进行进程间通信(IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。

4、使用ContentProvider

ContentProvider(内容提供者)是Android中的四大组件之一,为了在应用程序之间进行数据交换,Android提供了ContentProvider,ContentProvider是不同应用之间进行数据交换的API,一旦某个应用程序通过ContentProvider暴露了自己的数据操作接口,那么不管该应用程序是否启动,其他应用程序都可以通过接口来操作接口内的数据,包括增、删、改、查等。ContentProvider分为系统的和自定义的,系统的也就是例如联系人,图片等数据。

5、使用广播

请谈谈你对Binder机制的理解?

1、为了保证进程空间不被其他进程破坏或干扰,Linux中的进程是相互独立或相互隔离的。
2、进程空间分为用户空间和内核空间。用户空间不可以进行数据交互;内核空间可以进行数据交互,所有进程共用一个内核空间。
3、IPC, 全称为"Inter-Process Communication,IPC, 全称为"Inter-Process Communication", 是指进程间通信的技术。它允许不同进程之间共享数据,并使它们能够相互协作完成任务。
4、Binder机制相对于Linux内传统的进程间通信方式:
(1)性能更好;Binder机制只需要拷贝数据一次,管道、消息队列、Socket等都需要拷贝数据两次;而共享内存虽
然不需要拷贝,但实现复杂度高。(2)安全性更高;Binder机制通过UID/PID在内核空间添加了身份标识,安全性更高。
5、Binder跨进程通信机制:基于C/S架构,由Client、Server、Server Manager和Binder驱动组成。
6、Binder驱动实现的原理:通过内存映射,即系统调用了mmap()函数。
7、Server Manager的作用:管理Service的注册和查询。
8、Binder驱动的作用:(1)传递进程间的数据,通过系统调用mmap()函数;(2)实现线程的控制,通过Binder
驱动的线程池,并由Binder驱动自身进行管理。
9、Server进程会创建很多线程处理Binder请求,这些线程采用Binder驱动的线程池,由Binder驱动自身进行管理。
一个进程的Binder线程池默认最大是16个,超过的请求会阻塞等待空闲的线程。
10、Android中进行进程间通信主要通过Binder类(已经实现了IBinder接口),即具备了跨进程通信的能力。

谈谈 AIDL?

AIDL 是一种辅助工具,不用AIDL ,一样可以实现跨进程通讯,AIDL 的原理是binder,真正有跨进程通讯能力的也是Binder,所以 AIDL 只是一个能帮你少写代码,少出错的辅助工具,由于设计的太好,使用太方便,所以非常常用就像 retrofit 和okhttp 关系一样, retrofit 提供 更加友好的api,真正的网络请求还是由 okhttp发起的

SharedPreferences 是线程安全的吗?它的 commit 和 apply 方法有什么区别?

context.getSharedPreferences()开始追踪的话,可以去到ContextImpl的getSharedPreferences(),最终发SharedPreferencesImpl这个SharedPreferences的实现类,在代码中可以看到读写操作时都有大量的synchronized,因此它是线程安全的

commit 和 apply 区别
1、 所有commit提交是同步过程,效率会比apply异步提交的速度慢,但是apply没有返回值,永远无法知道存储是否失败。
2、commit提交有boolean的返回值,apply提交没有返回值

Serializable和Parcelable的区别?

Android中序列化有两种方式:Serializable以及Parcelable。
其中Serializable是Java自带的,而Parcelable是安卓专有的。Seralizable相对Parcelable而言,好处就是非常简单,只需对需要序列化的类class执行就可以,不需要手动去处理序列化和反序列化的过程,所以常常用于网络请求数据处理,Activity之间传递值的使用。
Parcelable是android特有的序列化API,它的出现是为了解决Serializable在序列化的过程中消耗资源严重的问题,但是因为本身使用需要手动处理序列化和反序列化过程,会与具体的代码绑定,使用较为繁琐,一般只获取内存数据的时候使用。

谈谈安卓apk构建的流程?

  1. 使用aapt处理资源文件,如编译AndroidManifest.xml,编译生成resources.arsc,生成R.java等
  2. 使用javac等工具编译java文件,生成class格式文件
  3. 使用dx等工具将.class和项目依赖的jar编译成.dex
  4. 将生成的这些文件压缩进一个zip中
  5. 签名

这只是最简单的过程,实际还会涉及到multidex,使用如proguard的工具处理生成的字节码,需要依赖aar文件,需要编译kotlin,使用jack,使用jni,使用d8/r8等情况~

请简述从点击图标开始app的启动流程?

涉及到四个进程之间的通信,分别是Laucher进程(桌面APP),SystemServer进程(AMS所属进程),Zygote进程(系统和所有APP的创建进程),APP进程。
1.点击app图标,Launcher进程使用Binder IPC向systemserver进程发起startActivity请求;
2.systemserver进程收到1中的请求后,向zygote进程发送创建新进程的请求;
3.zygote进程fork出新的App进程
4.App进程通过Binder IPC向systemserver进程发起attachApplication请求;
5.systemserver进程收到4中的请求后,通过Binder IPC向App进程发送scheduleLauncherActivity请求;
6.App进程的ApplicationThread线程收到5的请求后,通过handler向主线程发送LAUNCHER_ACTIVITY消息;
7.主线程收到6中发送来的Message后,反射创建目标Activity,回调oncreate等方法,开始执行生命周期方法,
我们就可以看到应用页面了。

EventBus的原理?

1、 register方法将对象实例用软引用包裹,保存到一个map缓存集合中
2、post方法 传入一个对象进去,然后遍历map里面多有的对象,找到所有的带有 @subscribe注解的并且方法参数与post的对象是同一类型的Method。 并通过反射执行Method。
3、Subscribe线程调度 执行method方法的时候会去获取注解上标记得线程,然后切换到指定线程。
4、unregister取消订阅 从第一步中的缓存map中移除对应注册的对象实例

谈一谈Glide的缓存机制?

Glide的三级缓存机制是由内存缓存、磁盘缓存和网络缓存组成。当通过URL加载图片时,Glide会先尝试在内存缓存中查找,然后再查询磁盘缓存。如果仍然找不到,则会从网络中下载图片。

Android的几种存储方式?

SharedPreferences方式存储
数据库存储
本地文件存储
网络存储

MVP中你是如何处理Presenter层以防止内存泄漏的?

首先 MVP 会出现内存泄漏是因为 Presenter 层持有 View
对象,一般我们会把 Activity 做为 View 传递到
Presenter,Presenter 持有 View对象,Activity 退出了但
是没有回收出现内存泄漏。
解决办法:
1.Activity onDestroy() 方法中调用 Presenter 中的方法,
把 View 置为 null
2.使用 Lifecycle
3.使用 MVVM

Android单例模式

借助类加载机制,可以在不使用synchronized等内容的情况下,最高效的实现单例。

public class Singleton {
    public static Singleton getInstance() {
        // 1. 调用该方法时,才会访问LazyHolder.INSTANCE这个静态类的静态变量
        return LazyHolder.INSTANCE;
    }

    private static class LazyHolder {
        // 2. 访问 LazyHolder.INSTANCE才会触发Singleton的初始化
        static final Singleton INSTANCE = new Singleton();
    }

    private Singleton() {
    }
}

说说项目中用到的设计模式和使用场景

单例模式
常见应用场景:网络请求的工具类、sp存储的工具类、弹窗的工具类等

工厂模式
常见应用场景:activity的基类等

建造者模式
常见应用场景:Dialog的创建

适配器Adapter模式
常见应用场景:RecyclerView的Adapter

观察者模式
View.OnClickListener 接口:View.OnClickListener 是 Android 提供的一个用于处理视图点击事件的接口,它使用观察者模式来接收和处理视图的点击事件。

命令模式
Handler 类:Handler 是 Android 提供的一个用于发送和处理消息的类,它使用命令模式来实现将消息封装成命令对象,然后将命令对象添加到消息队列中等待处理。

代理模式
Service 代理模式:在 Android 中,Service 是一个常用的组件,它可以在后台运行长时间的任务。Android 系统提供了一种基于 Binder 机制的 Service 代理模式,即通过 IBinder 接口来实现 Service 的远程调用。客户端通过代理对象调用远程 Service 的方法,代理对象会将请求发送到服务端的 Binder 对象,然后返回结果给客户端。

策略模式
Exoplayer 库:Exoplayer 是 Android 提供的一个用于播放音频和视频的库,它使用策略模式来实现不同的播放策略,例如播放本地文件、HLS、DASH、SmoothStreaming 等。

Kotlin 中 List 与 MutableList 的区别?

Kotlin中List、Set、Map与Java中的List、Set、Map有一些不同,kotlin中为只读,只能访问集合中的内容,不能进行修改操作。 如过需要进行添加修改操作需使用Mutable(可变的)前缀

你可能感兴趣的:(Android,android)