Android知识点总结,面试整理

作者:计算机网络

3.3 在Fragment中调用另外一个Fragment的方法

这个可就需要一定的思维性了,首先要想调用Fragment A的方法,除了这个Fragment A自身可以调用外,这个Fragment A所属的Activity也可以调用,要想另外一个Fragment B调用此Fragment A的方法,Fragment B可以间接通过Activity来进行调用,也就是3.1 和 3.2 的结合。

3.配置lint

1.配置lint.xml
2.java代码和xml布局文件如何忽略lint检查
3.自定义lint
1)原因

三十三、Kotlin

Binder面试题

1、Linux内核的基本知识

  • 进程隔离/虚拟地址空间:进程间是不可以共享数据的,相当于被隔离,每个进程被分配到不同的虚拟地址中
  • 系统调用:Linux内核对应用有访问权限,用户只能在应用层通过系统调用,调用内核的某些程序
  • binder驱动:它负责各个用户的进程,通过binder通信内核来进行交互的模块

2、为什么使用Binder

  • 性能上,相比传统的Socket更加高效
  • 安全性高,支持协议双方互相校验

3、Binder通信模型

图片 1

  • Service服务端通过Binder驱动在ServiceManager的查找表中注册Object对象的add方法
  • Client客户端通过Binder驱动在ServiceManager的查找表中找到Object对象的add方法,并返回proxy的add方法,add方法是个空实现,proxy也不是真正的Object对象,是通过Binder驱动封装好的代理类的add方法
  • 当Client客户端调用add方法时,Client客户端通过Binder驱动将proxy的add方法,请求ServiceManager来找到Service服务端真正对象的add方法,进行调用

4、AIDL

  • 客户端通过aidl文件的Stub.asInterface()方法,拿到Proxy代理类
    通过调用Proxy代理类的方法,将参数进行封包后,调用底层的transact()方法
  • transact()方法会回调onTransact()方法,进行参数的解封
    在onTransact()方法中调用服务端对应的方法,并将结果返回
主线程和工作线程的区别?

主线程和工作线程的关系如下
1.主线程结束前向工作线程发送结束消息,通知工作线程退出。
2.主线程立即进入等待工作线程死亡状态WaitforSingleObject,WaitforMultipleObjects....
3.工作线程收到退出消息,退出(因为工作在一个大的WairforSingleObject(退出通知)中)。
4.主线程等烦了(等待超时),直接杀死工作线程。
5.程序退出。
可以说一下:主线程无法更新UI,造成线程阻塞。子线程即工作线程

2.2 帧动画优缺点

缺点:效果单一,逐帧播放需要很多图片,占用控件较大优点:制作简单

5.三级缓存

二十三、UI卡顿问题

内存泄漏面试题

1、Java内存泄漏引起的主要原因

  • 长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏

2、Java内存分配策略

  • 静态存储区:又称方法区,主要存储全局变量和静态变量,在整个程序运行期间都存在
  • 栈区:方法体的局部变量会在栈区创建空间,并在方法执行结束后会自动释放变量的空间和内存
  • 堆区:保存动态产生的数据,如:new出来的对象和数组,在不使用的时候由Java回收器自动回收

3、Android解决内存泄漏的例子

  • 单例造成的内存泄漏:在单例中,使用context.getApplicationContext()作为单例的context
  • 匿名内部类造成的内存泄漏:由于非静态内部类持有匿名外部类的引用,必须将内部类设置为static
  • Handler造成的内存泄漏:使用static的Handler内部类,同时在实现内部类中持有Context的弱引用
  • 避免使用static变量:由于static变量会跟Activity生命周期一致,当Activity退出后台被后台回收时,static变量是不安全,所以也要管理好static变量的生命周期
  • 资源未关闭造成的内存泄漏:比如Socket、Broadcast、Cursor、Bitmap、ListView等,使用完后要关闭
  • AsyncTask造成的内存泄漏:由于非静态内部类持有匿名内部类的引用而造成内存泄漏,可以通过AsyncTask内部持有外部Activity的弱引用同时改为静态内部类或在onDestroy()中执行AsyncTask.cancel()进行修复
4. Fragment能否不依赖于Activity存在?简析一下Fragment的栈管理。

Fragment不能独立存在,它必须嵌入到activity中,而且Fragment的生命周期直接受所在的activity的影响。
// Create new fragment and transaction Fragment newFragment = new ExampleFragment;
FragmentTransaction transaction = getFragmentManager.beginTransaction;
// Replace whatever is in the fragment_container view with this fragment, // and add the transaction to the back stack transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null);
// Commit the transaction transaction.commit;
transaction只是记录了从一个状态到另一个状态的变化过程,即比如从FragmentA替换到FragmentB的过程,当通过函数transaction.addToBackStack(null)将这个事务添加到回退栈,则会记录这个事务的状态变化过程,如从FragmentA —>FragmentB,当用户点击手机回退键时,因为transaction的状态变化过程被保存,则可以将事务的状态变化过程还原,即将FragmentB —> FragmentA.
添加到回退栈的函数:transaction.addToBackStack(null);

1.2 广播的使用场景

a.同一app内有多个进程的不同组件之间的消息通信。

b.不同app之间的组件之间消息的通信。

1.什么是Android lint 检查

Android Lint 是一个静态代码分析工具,它能够对你的Android项目中潜在的bug、可优化的代码、安全性、性能、可用性、可访问性、国际化等进行检查。

视图工作机制面试题

Android进阶——Android视图工作机制之measure、layout、draw

7. Service有哪些派生类?这些派生类的使用场景是什么?

这个问题不知道问的具体是什么,如果是要 IntentService
那么可以参考官方文档的解释与使用说明:
扩展 IntentService 类
由于大多数启动服务都不必同时处理多个请求(实际上,这种多线程情况可能很危险),因此使用 IntentService 类实现服务也许是最好的选择。
IntentService 执行以下操作:
创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand 的所有 Intent。 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent 实现,这样您就永远不必担心多线程问题。 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf。 提供 onBind 的默认实现(返回 null)。 提供 onStartCommand 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent 实现。 综上所述,您只需实现 onHandleIntent 来完成客户端提供的工作即可。(不过,您还需要为服务提供小型构造函数。)

以下是 IntentService 的实现示例:
public class HelloIntentService extends IntentService { /** * A constructor is required, and must call the super IntentService(String) * constructor with a name for the worker thread. / public HelloIntentService { super("HelloIntentService"); } /* * The IntentService calls this method from the default worker thread with * the intent that started the service. When this method returns, IntentService * stops the service, as appropriate. */ @Override protected void onHandleIntent(Intent intent) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. try { Thread.sleep(5000); } catch (InterruptedException e) { // Restore interrupt status. Thread.currentThread.interrupt; } } }

您只需要一个构造函数和一个 onHandleIntent 实现即可。
如果您决定还重写其他回调方法(如 onCreate()、onStartCommand() 或 onDestroy()),请确保调用超类实现,以便 IntentService 能够妥善处理工作线程的生命周期。
例如,onStartCommand 必须返回默认实现(即,如何将 Intent 传递给 onHandleIntent()):
@Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show; return super.onStartCommand(intent,flags,startId); }

除 onHandleIntent 之外,您无需从中调用超类的唯一方法就是 onBind(仅当服务允许绑定时,才需要实现该方法)。

1.1 Activity 的四种状态

running 当前Activity正在运行,获取焦点paused 当前Activity处于暂停状态,可见,没有焦点stopped 当前Activity处于暂停状态,完全不可见,内存里的成员变量和状态信息仍在。killed 当前Activity被销毁后的状态,成员变量和状态信息被一并回收。

2.layout

Kotlin面试题

1、什么是Kotlin

  • Kotlin是一种基于JVM的编程语言
  • 对Java的一种拓展,比Java更简洁
  • Kotlin支持函数式编程
  • Kotlin类和Java类可以相互调用

2、Kotlin环境搭建

  • 直接在Plugin中下载Kotlin插件即可
  • 系统会自动配置到Kotlin环境
18. Butterknife的机制是什么?

Java Annotation Processing技术,在Java代码编译成Java字节码的时候就已经处理了@Bind、@OnClick(ButterKnife还支持很多其他的注解)这些注解了。
Annotation processing 是javac中用于编译时扫描和解析Java注解的工具
Annotation processing是在编译阶段执行的,它的原理就是读入Java源代码,解析注解,然后生成新的Java代码。新生成的Java代码最后被编译成Java字节码,注解解析器(Annotation Processor)不能改变读入的Java 类,比如不能加入或删除Java方法。
参考:ButterKnife框架原理

1.1 Fragment加载到Activity的两种方式

  • 静态加载:直接在Activity布局文件中指定Fragment。代码如下
<fragment android:name="com.example.myfragment.MyFragment" android: android:layout_width="wrap_content" android:layout_height="wrap_content"/> 
  • 动态加载:动态加载需要使用到FragmentManager,这种加载方式在开发中是非常常见的,示例代码如下:
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();//将FragmentA从容器中移除掉,减少内存的消耗fragmentTransaction.remove(fragmentA);fragmentTransaction.add(R.id.fragment_layout,new FragmentB;fragmentTransaction.commit();
1.java内存泄漏基础知识

该被释放的对象没有被释放,一直被某个或者某个实例所持有,导致不能垃圾回收

博文

二十五、内存管理

博文

二十六、冷启动优化

UI卡顿面试题

1、UI卡顿原理

  • View的绘制帧数保持60fps是最佳,这要求每帧的绘制时间不超过16ms(1000/60),如果安卓不能在16ms内完成界面的渲染,那么就会出现卡顿现象

2、UI卡顿的原因分析

  • 在UI线程中做轻微的耗时操作,导致UI线程卡顿
  • 布局Layout过于复杂,无法在16ms内完成渲染
  • 同一时间动画执行的次数过多,导致CPU和GPU负载过重
  • overDraw,导致像素在同一帧的时间内被绘制多次,使CPU和GPU负载过重
  • View频繁的触发measure、layout,导致measure、layout累计耗时过多和整个View频繁的重新渲染
  • 频繁的触发GC操作导致线程暂停,会使得安卓系统在16ms内无法完成绘制
  • 冗余资源及逻辑等导致加载和执行缓慢
  • ANR

3、UI卡顿的优化

  • 布局优化
    • 使用include、ViewStub、merge
    • 不要出现过于嵌套和冗余的布局
    • 使用自定义View取代复杂的View
  • ListView优化
    • 复用convertView
    • 滑动不加载
  • 背景和图片优化
    • 缩略图
    • 图片压缩
  • 避免ANR
    • 不要在UI线程中做耗时操作
8. Service与其它组件之间的通信实现方式有哪些?

binder
broadcast
其他参见线程和进程的通信方式

1.1 补间动画

a.渐变动画支持四种类型:平移(Translate)、旋转、缩放、不透明度

b. 只是显示的位置变动,View的实际位置未改变,表现为View移动到其他地方,点击事件仍在原处才能响应。

c. 组合使用步骤较复杂。

d. View Animation 也是指此动画。

1.简介

Android事件分发机制的发生在View与View之间或者ViewGroup与View之间具有镶嵌的视图上,而且视图上必须为点击可用。当一个点击事件产生后,它的传递过程遵循如下顺序:Activity->Window->View,即事件先传递给Activity,再到Window,再到顶级View,才开始我们的事件分发

ListView面试题

1、ListView是什么

  • ListView是能将一个数据集合以动态滚动的方式展示到用户界面上的View

2、ListView的RecycleBin机制

图片 2

这里写图片描述

3、ListView的优化

  • 重用convertView
  • 使用ViewHolder
  • 图片三级缓存
  • 监听滑动事件
  • 少用透明View
  • 开启硬件加速
5. 能否将一个Activity放到系统的最近任务列表里,独立于宿主app任务卡之外?

我印象中是可以做到了,平时没用到,知道的同学请@我,谢谢!

2.3 属性动画优缺点

缺点:(3.0 API出现)向下兼容问题。优点:易定制,效果强。

  • 对原View进行扩展方式

  • 多个View的组合方式

  • 重写View的方式

  • onMesure
  • onLayout
  • onDraw

Context的中文翻译为:语境; 上下文; 背景; 环境,在开发中我们经常说称之为“上下文”,那么这个“上下文”到底是指什么意思呢?在语文中,我们可以理解为语境,在程序中,我们可以理解为当前对象在程序中所处的一个环境,一个与系统交互的过程。比如微信聊天,此时的“环境”是指聊天的界面以及相关的数据请求与传输,Context在加载资源、启动Activity、获取系统服务、创建View等操作都要参与。

Context提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被Android系统所提供。它允许获取以应用为特征的资源和类型,是一个统领一些资源(应用程序环境变量等)的上下文。就是说,它描述一个应用程序环境的信息;是一个抽象类,Android提供了该抽象类的具体实现类;通过它我们可以获取应用程序的资源和类(包括应用级别操作,如启动Activity,发广播,接受Intent等)。

图片 3Context.png

Context的作用域:

图片 4Context的作用域.png

内容提供者(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。目前,使用内容提供者是Android实现跨程序共享数据的标准方式。不同于文件存储和SharedPreferences存储中的两种全局可读可写操作模式,内容提供者可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会泄露的风险。

内容提供者的用法一般有两种,一种是使用现有的内容提供器来读取和操作相应程序中的数据,另一种是创建自己的内容提供者来给我们的程序提供外部访问接口。如果一个程序通过内容提供者对其数据提供外部访问接口,那么任何其他的应用程序就都可以对这部分数据进行访问。Android系统中自带的电话簿,短信,媒体库等程序都提供了类似的访问接口,这就使得第三方应用程序可以充分利用这部分数据来实现更好的功能。下面我们就来看看如何通过内容提供者访问其他程序的数据:

有序广播与无序广播的区别

1.无序广播
通过Context.sendBroadcast()方法来发送,它是完全异步的。
所有的receivers(接收器)的执行顺序不确定,因此所有的receivers(接收器)接收broadcast的顺序不确定。
这种方式效率更高,但是BroadcastReceiver无法使用setResult系列、getResult系列及abortbroadcast(中止)系列API。
广播不能被终止,数据不能被修改。
2.有序广播
有序广播,即从优先级别最高的广播接收器开始接收,接收完了如果没有丢弃,就下传给下一个次高优先级别的广播接收器进行处理,依次类推,直到最后。如果多个应用程序设置的优先级别相同,则谁先注册的广播,谁就可以优先接收到广播。通过Context.sendorderBroadCast()方法来发送,sendOrderedBroadcast(intent,
receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras);,其中的参数resultReceiver,可以自己重写一个类,作为一个最终的receive 最后都能够接收到广播,最终的receiver 不需要再清单文件里面配置,initialData可以作为传输的数据
广播可以被终止,数据传输过程中可以被修改。

参考博文
进程保活

五、Webview

事件分发机制面试题

Android事件分发机制之dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent

RxJava 的适用场景和使用方式
  1. 与 Retrofit 的结合
  2. 各种异步操作
    例如数据库的读写、大图片的载入、文件压缩/解压等各种需要放在后台工作的耗时操作,都可以用 RxJava 来实现

1.1 定义

在Android中,它是一种广泛运用在应用程序之间传输信息的机制,Android中我们发送广播内容是一个Intent,这个Intent中可以携带我们要发送的数据。

3.Fragment之间的通信

在Fragment中获取Activity中的方法:getActivity
在Activity中获取Fragment中的方法:接口回调
在Fragment中获取Fragment中的方法:先使用getActivity获取到activity,然后使
用getFragmentById

三、Service

ANR面试题

1、什么是ANR

  • Application Not Responding,页面无响应的对话框

2、发生ANR的条件

  • 应用程序的响应性是由ActivityManager和WindowManager系统服务监视的,当ANR发生条件满足时,就会弹出ANR的对话框
  • Activity超过5秒无响应
  • BroadcastReceiver超过10秒无响应
  • Service超过20秒无响应

3、造成ANR的主要原因

  • 主线程被IO操作阻塞
  • Activity的所有生命周期回调都是执行在主线程的
  • Service默认执行在主线程中
  • BoardcastReceiver的回调onReceive()执行在主线程中
  • AsyncTask的回调除了doInBackground,其他都是在主线程中
  • 没有使用子线程Looper的Handler的handlerMessage,post(Runnable)都是执行在主线程中

4、如何解决ANR

  • 使用AsyncTask处理耗时IO操作
  • 使用Thread或HandlerThread提高优先级
  • 使用Handler处理工作线程的耗时操作
  • Activity的onCreate和onResume回调尽量避免耗时操作
View的事件分发

ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。
View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。
1、整个View的事件转发流程是:
View.dispatchTouchEvent->View.setOnTouchListener->View.onTouchEvent
在dispatchTouchEvent中会进行OnTouchListener的判断,如果OnTouchListener不为null且返回true,则表示事件被消费,onTouchEvent不会被执行;否则执行onTouchEvent。
1、整个ViewGroup的事件转发流程是:
可以看到大体的事件流程为:
MyLinearLayout的dispatchTouchEvent -> MyLinearLayout的onInterceptTouchEvent -> MyButton的dispatchTouchEvent ->Mybutton的onTouchEvent
1、如果ViewGroup找到了能够处理该事件的View,则直接交给子View处理,自己的onTouchEvent不会被触发;
2、可以通过复写onInterceptTouchEvent(ev)方法,拦截子View的事件(即return true),把事件交给自己处理,则会执行自己对应的onTouchEvent方法
3、子View可以通过调用getParent().requestDisallowInterceptTouchEvent(true); 阻止ViewGroup对其MOVE或者UP事件进行拦截;
好了,那么实际应用中能解决哪些问题呢?
比如你需要写一个类似slidingmenu的左侧隐藏menu,主Activity上有个Button、ListView或者任何可以响应点击的View,你在当前View上死命的滑动,菜单栏也出不来;因为MOVE事件被子View处理了~ 你需要这么做:在ViewGroup的dispatchTouchEvent中判断用户是不是想显示菜单,如果是,则在onInterceptTouchEvent(ev)拦截子View的事件;自己进行处理,这样自己的onTouchEvent就可以顺利展现出菜单栏了~~

以下摘自http://www.toutiao.com/i6398007879990772226/

1.1 Service是什么?

Service是一个一种可以在后台执行长时间运行操作而没有用户界面的组件。它运行于UI线程,因此不能进行耗时的操作。

onTouchEvent

  • return true:表示onTouchEvent处理完事件后消费了此次事件
  • return fasle:表示不响应事件,那么该事件将会不断向上层View的onTouchEvent方法传递,直到某个View的onTouchEvent方法返回true
  • return super.dispatchTouchEvent(ev):表示不响应事件,结果与return false一样

十三、ListView

参考

十四、构建

十五、Okhttp

1.Okhttp简单使用

//首先创建一个Handler
private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                    imageView.setImageBitmap((Bitmap) msg.obj);
                    break;
            }
        }
};

//然后使用OkHttp发送网络请求,并将结果通过Handler发送到主线程
public void sendRequestByOkHttp() {
        //创建Request对象,并传入请求地址
        Request request = new Request.Builder().url( "http://tnfs.tngou.net/img/ext/160321/e57d5816cb72d7486aa6dbf19a7d0c6c.jpg").build();
        //构造Call对象--其实是AsyncCall对象
        Call call = client.newCall(request);
        //调用Call.enqueue方法进行异步请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //网络请求失败
                Log.d("lenve", "onFailure: "   e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //网络请求成功,将请求的图片信息显示的ImageView控件上
                Bitmap bitmap = BitmapFactory.decodeStream(   response.body().byteStream());
                Message msg = mHandler.obtainMessage();
                msg.what = 1;
                msg.obj = bitmap;
                mHandler.sendMessage(msg);
            }
        });
    }

2.如何使用OkHttp进行异步网络请求,并根据请求结果刷新UI

3.可否介绍一下OkHttp的整个异步请求流程

4.OkHttp对于网络请求都有哪些优化,如何实现的

5.OkHttp框架中都用到了哪些设计模式

OkHttp的底层是通过Java的Socket发送HTTP请求与接受响应的(这也好理解,HTTP就是基于TCP协议的),但是OkHttp实现了连接池的概念,即对于同一主机的多个请求,其实可以公用一个Socket连接,而不是每次发送完HTTP请求就关闭底层的Socket,这样就实现了连接池的概念。而OkHttp对Socket的读写操作使用的OkIo库进行了一层封装。

博文

十六、retrofit

retrofit的原理其实就是这样,拦截到方法、参数,再根据我们在方法上的注解,去拼接为一个正常的Okhttp请求,然后执行。

十七、volley

1.首先我们初始化一个RequestQueue,因为初始化RequestQueue非常的消耗性能,所以只需要初始化一次即可
2.在newRequestQueue中我们会发现,如果Android版本大于2.3会调用HttpUrlConnection的HurlStack,如果Android版本小于2.3会调用一个HttpClient的HttpClickStack
3.接下来Volley会创建RequestQueue,并调用他的start方法
4.接着会创建1个缓存调度线程(CacheDispatcher)和4个网络调度线程(netDispatcher),并分别调用他们的start方法,所以会默认开启5个后台线程,线程优先级都为10
5.然后分别去轮询各自的消息队列
6.Volley是如何把请求的数据回调回主线程中的?

使用Handler.postRunnable(Runnable)方法回调回主线程中进行处理,ExecutorDelivery的构造方法中可以看到这段代码

图片 5

volley工作流程.png

博文
推荐博文

十八、ButterKnife

进程保活面试题

1、进程的优先级

  • 空进程
  • 后台进程
  • 服务进程
  • 可见进程
  • 前台进程

2、Android进程回收策略

  • Low memory Killer(定时执行):通过一些比较复杂的评分机制,对进程进行打分,然后将分数高的进程判定为bad进程,杀死并释放内存
  • OOM_ODJ:判别进程的优先级

3、Android保活方案

  • 利用系统广播拉活
  • 利用系统Service机制拉活
  • 利用Native进程拉活
  • 利用JobScheduler机制拉活
  • 利用账号同步机制拉活
15. 6.0系统新权限机制的解决方案。

这个。。。没什么好说的,真正了解的很好说,不了解的话就有点绕。

3.2 Activity的启动模式

  • 系统模式模式:standard 标准模式,也是系统的默认模式,启动一个activity就创建一个activity实例,不管这个实例是否存在,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity的任务栈中。
  • 栈顶复用模式:singleTop 在这种模式下,如果新的Activity已经位于栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法被回调,通过此方法的参数我们可以取出当前的请求信息。需要注意,此Activity的onCreate,onStart方法不会被系统调用。如果新Activity不在栈顶,那么新Activity任然会被重新重建。
  • 栈内复用模式:singleTask 这是一种单实例模式,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,系统也会回调onNewIntent方法。例如:当前栈内情况为ABC,此时D被以singleTask的模式被启动,当前栈变为ABCD。如果当前栈内情况为ADBC,此时D被以singleTask的模式被启动,当前栈变为AD。
  • 单实例模式:singleInstance 这是一种加强的单实例模式,它除了具有singleTask模式的所有特性外,还加强了一点,那就是具有此种模式的Activity只能单独位于一个任务栈中,比如Activity A是singleInstance模式,A被启动时系统会为它创建一个新的任务栈,A运行在这个单独的任务栈中,后续的请求均不会再创建A,除非这个单独的任务栈被系统销毁了。

Android中的四大组件为Activity,service,ContentProvider,Broadcast。Fragment因为有生命周期,使用频率不输于四大组件,可灵活加载到Activity中。

⚠️特别注意

动态广播最好在Activity 的 onResume()注册、onPause()注销。
原因:
对于动态广播,有注册就必然得有注销,否则会导致内存泄露
重复注册、重复注销也不允许

Android项目构建面试题

1、android构建流程

图片 6

这里写图片描述

2、jenkins持续集成构建

  • 这里可参考蒲公英文档

3、Git常用命令

git init:仓库的初始化
git status:查看当前仓库的状态
git diff:查看仓库与上次修改的内容
git add:将文件放进暂存区
git commit:提交代码
git clone:克隆代码
git bransh:查看当前分支
git checkout:切换当前分支

4、git工作流

fork/clone(主流) fork:将别人的仓库代码fork到自己的仓库上
clone:克隆下自己仓库的代码
update、commit:修改代码并提交到自己的仓库
push:提交到自己的仓库
pull request:请求添加到别人的仓库
clone

5、proguard是什么

  • ProGuard工具是用于压缩、优化和混淆我们的代码,其主作用是移除或混淆代码中无用类、字段、方法和属性

6、proguard技术功能

  • 压缩
  • 优化
  • 混淆
  • 预检测

7、proguard工作原理

  • 将无用的字段或方法存入到EntryPoint中,将非EntryPoint的字段和方法进行替换

8、为什么要混淆

  • 由于Java是一门跨平台的解释性语言,其源代码被编译成class字节码来适应其他平台,而class文件包含了Java源代码信息,很容易被反编译
9. View的post(Runnable r)方法里,r会带来一个新的线程吗?多线程相关。

不会,最终还是handler发送消息,执行在UI线程。
如下是源码和注释:
/** * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> * * @param action The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. * * @see #postDelayed * @see #removeCallbacks */ public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action)); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue.post(action)); return true; }

3.2 在Activity中调用Fragment的方法

在Activity中调用Fragment中的方法是最简单的,我想这里我不用多说吧!直接接口回调即可调用Fragment的任何可访问的方法。

6.LocalBroadcastManager详解

1.LocalBroadcastManager高效的原因主要是它的内部是通过Handler实现的,
sendBrocast()方法含义并非和我们平时所用一样,它的sendBrocast()其实是通过
handler发送一个Message实现的

2.既然它内部是通过Handler来实现广播的发送,那么相比与系统使用Binder实现
自然是更加高效了,同时使用Handler来实现,别的应用无法向我们的应用发送
广播,而我们应用内发送的广播也不会离开我们的广播

IntentService面试题

1、IntentService是什么

  • IntentService是继承自Service并处理异步请求的一个类,其内部采用HandlerThread和Handler实现的,在IntentService内有一个工作线程来处理耗时操作,其优先级比普通Service高。当任务完成后,IntentService会自动停止,而不需要手动调用stopSelf()。
  • 另外,可以多次启动IntentService,每个耗时操作都会以工作队列的方式在IntentService中onHandlerIntent()回调方法中执行,并且每次只会执行一个工作线程

2、IntentService使用方法

  • 创建Service继承自IntentService
  • 覆写构造方法和onHandlerIntent()方法
  • 在onHandlerIntent()中执行耗时操作
? 你们精通的开源框架,问题来了

写各种精通其实是可以的,要么真牛x,如果不是很牛x那就在最后加上一条精通----精通各种被打脸

3.1 在Fragment中调用Activity中的方法

在Fragment中调用Activity的方法很简单,Fragment有个getActivity()的方法,比如,在MainActivity中的一个Fragment中获取MainActivity的引用,并调用MainActivity的某个方法methodA()方法你可以这么写:

MainActivity mainActivity = (MainActivity) getActivity();mainActivity.methodA();
3.进程保活方案
  • 利用系统广播进行拉活
  • 利用系统的Service机制拉活
  • 利用native进程拉活(5.0以后无法实现)
  • 利用 JobScheuler机制拉活(5.0以后新加)
  • 利用账号同步机制拉活

博文
保活

三十一、UIL

三十二、Lint

后续我会在这个的基础上,对每个知识点展开来讲,并补充一些个人觉得需要掌握的知识点,希望对大家准备 Android开发岗春招秋招以及形成较完整的 Android知识体系 有帮助。

6. 对于同一个Service,在被start启动之后还能不能被bind?


服务基本上分为两种形式:
启动
当应用组件(如 Activity)通过调用 startService 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 操作完成后,服务会自行停止运行。
绑定
当应用组件通过调用 bindService 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
虽然本文档是分开概括讨论这两种服务,但是您的服务可以同时以这两种方式运行,也就是说,它既可以是启动服务(以无限期运行),也允许绑定。问题只是在于您是否实现了一组回调方法:onStartCommand(允许组件启动服务)和 onBind(允许绑定服务)。
来自官方文档

3.1 为什么需要启动模式?

每次启动一个Activity都会把对应的要启动的Activity的实例放入任务栈中,加入这个Activity被频繁启动,会产生很多的这个Activity的实例,为了杜绝这种内存浪费的行为,Activity的启动模式被创造出来。

5.广播内部实现机制

1、自定义广播接受者BroadcastReceiver,并复写onRecvice()方法;
2、接收者通过Binder机制向AMS(Activity Manager Service)进行注册;
3、广播发送着通过Binder机制向AMS发送广播;
4、AMS查找符合相应条件(IntentFilter/Permission等)的BrocastReceiver,将广播发送到BrocasrReceiver(一般是Activity)相应的循环队列中;
5、消息循环执行拿到该广播,回调BrocastReceiver的onRecerve()方法

Activity面试题

1、Activity是什么

  • Activity是四大组件之一,它提供一个界面让用户点击和各种滑动操作,这就是Activity

2、Activity四种状态

  • runing
  • paused
  • stopped
  • killed

3、Activity生命周期

  • onCreate()
  • onStart()
  • onResume()
  • onPause()
  • onStop()
  • onDestroy()
  • onRestart()

4、进程的优先级

  • 空进程
  • 后台进程
  • 服务进程
  • 可见进程
  • 前台进程

5、Activity任务栈

  • 先进后出

6、Activity启动模式

  • standard
  • singletop
  • singletask
  • singleinstance

7、scheme跳转协议
Android中的scheme是一种页面内跳转协议,通过定义自己的scheme协议,可以跳转到app中的各个页面

  • 服务器可以定制化告诉app跳转哪个页面
  • App可以通过跳转到另一个App页面
  • 可以通过H5页面跳转页面
13. 简析一下大图片的加载处理。

对Bitmap的理解,然后就是压缩图片。

2.1 补间动画优缺点

缺点:当平移动画执行完停在最后的位置,结果焦点还在原来的位置(控件的属性没有真的被改变)优点:相对于逐帧动画来说,补间动画更为连贯自然

3.内存对象序列化

内存管理面试题

1、Android内存管理机制

  • 分配机制
  • 管理机制

2、内存管理机制的特点

  • 更少的占用内存
  • 在合适的时候,合理的释放系统资源
  • 在系统内存紧张的时候,能释放掉大部分不重要的资源
  • 能合理的在特殊生命周期中,保存或还原重要数据

3、内存优化方法

  • Service完成任务后应停止它,或用IntentService(因为可以自动停止服务)代替Service
  • 在UI不可见的时候,释放其UI资源
  • 在系统内存紧张的时候,尽可能多的释放非重要资源
  • 避免滥用Bitmap导致内存浪费
  • 避免使用依赖注入框架
  • 使用针对内存优化过的数据容器
  • 使用ZIP对齐的APK
  • 使用多进程
1. Activity建立在哪些窗口组件之上?顺带涉及View的事件传递问题。

没读懂问题,=。=不知道是不是问Activity的UI结构,如果是可以参考这篇文章。

图片 7

8862185a17af456f29a005bb4a06b1d3_18f8000dd7b7d7d407bd.jpg

对于View的事件传递,则可以从 Activity --> ViewGroup --> ...... --> Activity
的** U型 **消费结构去说。

1.2 Service和Thread的区别

Service的运行是在UI线程当中的,是绝对绝对不能进行耗时操作的,而Thread开启的子线程则可以进行耗时操作,但是Thread开启的子线程是不能直接对UI进行操作的,否则极有可能发生直接让程序崩掉,这就是它们的区别。

dispatchTouchEvent

  • return true:表示该View内部消化掉了所有事件
  • return false:表示事件在本层不再继续进行分发,并交由上层控件的onTouchEvent方法进行消费
  • return super.dispatchTouchEvent(ev):默认事件将分发给本层的事件拦截onInterceptTouchEvent方法进行处理

热更新面试题

1、热更新主要流程

  • 线上检查到Crash
  • 拉出Bugfix分支修复Crash问题
  • jenkins构建和补丁生成
  • app通过推送或主动拉取补丁文件
  • 将Bugfix代码合到master上

2、热更新主流框架

  • Dexposed
  • AndFix
  • Nuwa

3、热更新的原理

  • 在ClassLoader创建一个dexElements数组
  • 将修复好的dex文件存放在dexElements数组的最前面
  • ClassLoader会遍历dexElements数组,找到最前面的dex文件优先加载
19. Okhttp是基于HTTP连接还是Socket连接?

基于Http的。

1.2 帧动画

a. 用于生成连续的Gif效果图。

b. DrawableAnimation也是指此动画

2.Fragment加载到Activity中的两种方式

静态加载:只需要把Fragment当成一个普通UI控件放到界面Layout中就完事了

动态加载:得先通过FragmentManage来获取FragmentTransaction(事务),然后使用add或者replace的方式加载fragment,最后commit提交事物。

HandlerThread面试题

1、HandlerThread产生背景

  • 当系统有多个耗时任务需要执行时,每个任务都会开启一个新线程去执行耗时任务,这样会导致系统多次创建和销毁线程,从而影响性能。为了解决这一问题,Google提供了HandlerThread,HandlerThread是在线程中创建一个Looper循环器,让Looper轮询消息队列,当有耗时任务进入队列时,则不需要开启新线程,在原有的线程中执行耗时任务即可,否则线程阻塞

2、HanlderThread的特点、

  • HandlerThread本质上是一个线程,继承自Thread
  • HandlerThread有自己的Looper对象,可以进行Looper循环,可以创建Handler
  • HandlerThread可以在Handler的handlerMessage中执行异步方法
    HandlerThread优点是异步不会堵塞,减少对性能的消耗
  • HandlerThread缺点是不能同时继续进行多任务处理,需要等待进行处理,处理效率较低
  • HandlerThread与线程池不同,HandlerThread是一个串行队列,背后只有一个线程
3. Activity A使用startForResult启动Activity B,B什么都不做并返回A,A中的onActivityResult回调是否会执行?

startActivity方法,最终都是调用startActivityForResult方法。默认的requestCode = -1 resultCode = RESULT_CANCELED = 0,当你的requestCode != -1时,onActivityResult一定会被调用。

1.3 广播的种类

标准广播:context.sendBroadcast方法发送的广播,不可被拦截

有序广播:context.sendOrderBroadcast方法发送的广播,可被拦截

本地广播:localBroadcastManager.sendBroadcast,只在app内传播

广播接收器是专门用来接收广播信息的,它可分为静态注册和动态注册:

  • 静态注册:注册完成一直在运行。

首先你要创建一个广播接收器类,实例代码如下:

public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show(); }}

另外,静态的广播接收器一定要在AndroidManifest.xml文件中注册才可以使用,AndroidManifest.xml文件中注册静态广播代码如下:

 <receiver android:name=".BootCompleteReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
  • 动态注册:跟随Activity的生命周期。新建一个类,让它继承自BroadcastReceiver,并重写父类的onReceive()方法就行了。这样有广播到来时,onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。
  • 动态注册广播接收器的优点以及缺点:动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大优势,但是它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在onCreate()方法中的。那么有没有广播能在程序未启动的情况下就能接收到广播呢?静态注册的广播接收器就可以做到。
  1. 自定义广播接收者BroadcastReceiver,并且重写onReceiver()方法。

  2. 通过Binder机制向AMS(Activity Manager Service)进行注册。

  3. 广播发送者通过Binder机制向AMS发送广播。

  4. AMS查找符合条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到相应的BroadcastReceiver(一般情况下是Activity)的消息队列中。

  5. 消息循环执行拿到此广播,回调BroadcastReceiver中的onReceiver()方法。

本地广播的发送和注册广播接收器都需要使用到LocalBroadcastManager类,如下所示为本地广播的发送和本地广播接收器注册的代码: 本地广播的发送:

public static void sendLocalBroadcast(Context context,String action){ Intent intent = new Intent; LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance; localBroadcastManager.sendBroadcast;}

本地广播的接收器的注册:

IntentFilter intentFilter = new IntentFilter(); LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance; intentFilter.addAction(new BroadcastUtil().action_next); nasbr = new NextAndStartBroadcastReceiver(); localBroadcastManager.registerReceiver(nasbr, intentFilter);//注册本地广播接收器

特点:

  1. 使用它发送的广播将只在自身app内传播,因此你不必担心泄漏隐私的数据。

2. 其他app无法对你的app发送该广播,因此你的app根本不可能收到非自身app发送的该广播,因此你不必担心有安全漏洞可以利用。

  1. 比系统广播更加高效。

内部实现机制:

1. LocalBroadcast高效的原因:因为它内部是通过Handler实现的,它的sendBroadcast()方法含义并非和系统的sendBroadcast()一样,它的sendBroadcast()方法其实就是通过Handler发送了一个Message而已。

2. LocalBroadcast安全的原因:既然它是通过Handler实现广播发送的,那么相比系统广播通过Binder机制实现那肯定更加高效,同时使用Handler来实现,别的app无法向我们应用发送该广播,而我们app内部发送的广播也不会离开我们的app。

LocalBroadcast内部协作主要是靠两个Map集合:mReceivers和mActions,当然还有一个List集合mPendingBroadcasts,这个主要存储待接收的广播对象。

  1. API 16之前版本存在远程代码执行漏洞,该漏洞源自于程序没有正确限制使用WebView.addJavascriptInterface方法,攻击者可以使用Java Reflection API利用该漏洞执行任意Java对象和方法。

  2. WebView的销毁和内存泄漏问题。WebView的完全销毁是件麻烦事,一旦销毁流程不正确,极易容易导致内存泄漏。

  3. jsbridge 通过javascript构建一个桥,桥的两端一个端是客户端,一端是服务端,它可以让本地端调用远端的web代码,也可以让远端调用本地的代码。

  4. WebViewClient.onPageFinished –> WebChromeClient.onProgressChanged

  5. 后台耗电 WebView开启网页时会自己开启线程,如果没有合理的销毁,那么残余线程就会一直运行,so这会非常耗电的,解决方案:有一种暴力方式就是Activity的onDestroy中调用System.exit()方法把虚拟机关闭,也可以结合自己应用的WebView的情况设计出一个温柔的方案。

  6. WebView硬件加速导致的页面渲染问题 WebView硬件加速偶尔导致页面白块,页面闪烁,但是加载速度比较快,解决方案:关闭硬件加速。

原因:WebView会关联一个Activity,WebView执行的操作是在新线程当中回收,时间Activity没有办法确认,Activity的生命周期和WebView线程生命周期不一致导致WebView一直执行,因为WebView内部持有Activity的引用,导致Activity一直不能被回收,原理类似于匿名内部类持有外部类的引用一样。那么如何解决呢?解决方案如下:

  1. 独立进程,简单暴力,涉及到进程间通信。

  2. 动态添加WebView,对传入WebView中使用Context弱引用,动态添加WebView意思在布局中创建一个ViewGroup用来放置WebView,Activity创建add进来,Activity停止时remove掉。

  1. 通常情况下,Binder是一种通信机制。

  2. 对于Server来说,Binder指的是Binder本地对象/对于Client来说,Binder指的是Binder的代理对象。

  3. 对于传输过程而言,Binder是可以进行跨进程传递的对象。

  4. AIDL是Binder机制的一个实例。

Handler是可以通过发送和处理Message和Runnable对象来关联相应线程的MessageQueue。通常我们认为它是一种异步机制。

  1. 可以让对应的Message和Runnable在未来的某个时间点进行相应的处理。
  2. 让自己想要的耗时操作在子线程中完成,让更新UI的操作在主线程中完成,而子线程与主线程之间的通信就是靠Handler来完成。
  • post
  • sendMessage

Handler机制也可叫异步消息机制,它主要由4个部分组成:Message,Handler,MessageQueue,Looper,在上面我们已经接触到了Message和Handler,接下来我们对4个成员进行着重的了解:

  1. Message Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。使用Message的arg1和arg2便可携带int数据,使用obj便可携带Object类型数据。

  2. Handler Handler顾名思义就是处理者的意思,它只要用于在子线程发送消息对象Message,在UI线程处理消息对象Message,在子线程调用sendMessage方法发送消息对象Message,而发送的消息经过一系列地辗转之后最终会被传递到Handler的handleMessage方法中,最终在handleMessage方法中消息对象Message被处理。

  3. MessageQueue MessageQueue就是消息队列的意思,它只要用于存放所有通过Handler发送过来的消息。这部分消息会一直存放于消息队列当中,等待被处理。每个线程中只会有一个MessageQueue对象,请牢记这句话。其实从字面上就可以看出,MessageQueue底层数据结构是队列,而且这个队列只存放Message对象。

  4. Looper Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当MesssageQueue中存在一条消息,Looper就会将这条消息取出,并将它传递到Handler的handleMessage()方法中。每个线程只有一个Looper对象。

Handler机制流程图如下:

图片 8Handler机制流程图

原因:静态内部类持有外部类的匿名引用,导致外部activity无法得到释放。解决方法:handler内部持有外部的弱引用,并把handler改为静态内部类,在activity的onDestory()中调用handler的removeCallback()方法。

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

  1. 它本质是一种特殊的Service,继承自Service并且本身就是一个抽象类。
  2. 它内部是由HandlerThread和Handler实现异步操作。

创建IntentService时,只需要实现onHandlerIntent和构造方法,onHandlerIntent为异步方法,可以执行耗时操作。

它本质上是一个封装了线程池和Handler的异步框架。

  1. 3个参数:Params, Progress 和 Result
  • Params表示用于AsyncTask执行任务的参数的类型
  • Progress表示在后台线程处理的过程中,可以阶段性地发布结果的数据类型
  • Result表示任务全部完成后所返回的数据类型
  1. 5个方法
  • onPreExecute该方法是运行在主线程中的。在AsyncTask执行了execute()方法后就会在UI线程上执行onPreExecute()方法,该方法在task真正执行前运行,我们通常可以在该方法中显示一个进度条,从而告知用户后台任务即将开始。
  • doInBackground该方法是运行在单独的工作线程中的,而不是运行在主线程中。
  • onProgressUpdate当我们在doInBackground中调用publishProgress(Progress…)方法后,就会在UI线程上回调onProgressUpdate方法。
  • onPostExecute当doInBackgroud方法执行完毕后,就表示任务完成了,doInBackgroud方法的返回值就会作为参数在主线程中传入到onPostExecute方法中,这样就可以在主线程中根据任务的执行结果更新UI。
  1. 它本质上是一个静态的线程池,AsyncTask派生出的子类可以实现不同的异步任务,这些任务都是提交到静态的线程池中执行。

  2. 线程池中的工作线程执行doInBackground方法执行异步的任务。

  3. 当任务状态改变后,工作线程向UI线程发送消息,AsyncTask内部的InternalHandler响应这些消息,并调用相关的回调函数。

  1. 内存泄漏:

静态内部类持有外部类的匿名引用,导致外部对象无法得到释放,解决方法很简单,让内部持有外部的弱引用即可解决

  1. 生命周期 在Activity的onDestory()中及时对AsyncTask进行回收,调用其cancel()方法来保证程序的稳定性。

  2. 结果丢失 当内存不足时,当前的Activity被回收,由于AsyncTask持有的是回收之前Activity的引用,导致AsyncTask更新的结果对象为一个无效的Activity的引用,这就是结果丢失。

  3. 并行或串行 在1.6之前: 在第一版的AsyncTask,任务是串行调度。一个任务执行完成另一个才能执行。由于串行执行任务,使用多个AsyncTask可能会带来有些问题。所以这并不是一个很好的处理异步(尤其是需要将结果作用于UI试图)操作的方法。1.6-2.3: 所有的任务并发执行,这会导致一种情况,就是其中一条任务执行出问题了,会引起其他任务出现错误。3.0之后AsyncTask又修改为了顺序执行,并且新添加了一个函数 executeOnExecutor,如果您需要并行执行,则只需要调用该函数,并把参数设置为并行执行即可。

开启子线程进行耗时操作,多次创建和销毁子线程是很耗费资源的,但是木有关系,谷歌考虑了这点为我们专门开发出了HandlerThread机制。

本质:Handler Thread Looper,是一个Thread内部有Looper。

  1. HandlerThread本质上是一个线程类,它继承了Thread。

  2. HandlerThread有自己内部的Looper对象,可以进行Looper循环。

  3. 通过获取HandlerThread的Looper对象传递给Handler对象,可以在handlerMessage方法中执行异步任务。

  4. 优点是不会有堵塞,减少对性能的消耗,缺点是不能进行多任务的处理,需要等待进行处理,处理效率较低。

  5. 与线程池注重并发不同,HandlerThread是一个串行队列,HandlerThread背后只有一个线程。

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

  • 它本质是一种特殊的Service,继承自Service并且本身就是一个抽象类。

  • 它内部是由HandlerThread和Handler实现异步操作。

创建IntentService时,只需要实现onHandlerIntent和构造方法,onHandlerIntent为异步方法,可以执行耗时操作。

measure→layout→draw

图片 9Measure过程

measure过程主要就是从顶层父View向子View递归调用view.measure方法(measure中又回调onMeasure方法)的过程。具体measure核心主要有如下几点:

MeasureSpec测量规格为int型,值由高2位规格模式specMode和低30位具体尺寸specSize组成。其中specMode只有三种值:

  • MeasureSpec.EXACTLY //确定模式,父View希望子View的大小是确定的,由specSize决定;
  • MeasureSpec.AT_MOST //最多模式,父View希望子View的大小最多是specSize指定的值;
  • MeasureSpec.UNSPECIFIED //未指定模式,父View完全依据子View的设计值来决定;

View的measure方法是final的,不允许重载,View子类只能重载onMeasure来完成自己的测量逻辑。

最顶层DecorView测量时的MeasureSpec是由ViewRootImpl中getRootMeasureSpec方法确定的(LayoutParams宽高参数均为MATCH_PARENT,specMode是EXACTLY,specSize为物理屏幕大小)。

ViewGroup类提供了measureChild,measureChild和measureChildWithMargins方法,简化了父子View的尺寸计算。

只要是ViewGroup的子类就必须要求LayoutParams继承子MarginLayoutParams,否则无法使用layout_margin参数。

View的布局大小由父View和子View共同决定。

使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。

整个layout过程比较容易理解,从上面分析可以看出layout也是从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。具体layout核心主要有以下几点:

  • View.layout方法可被重载,ViewGroup.layout为final的不可重载,ViewGroup.onLayout为abstract的,子类必须重载实现自己的位置逻辑。

  • measure操作完成后得到的是对每个View经测量过的measuredWidth和measuredHeight,layout操作完成之后得到的是对每个View进行位置分配后的mLeft、mTop、mRight、mBottom,这些值都是相对于父View来说的。

  • 凡是layout_XXX的布局属性基本都针对的是包含子View的ViewGroup的,当对一个没有父容器的View设置相关layout_XXX属性是没有任何意义的(前面《Android应用setContentView与LayoutInflater加载解析机制源码分析》也有提到过)。

  • 使用View的getWidth()和getHeight()方法来获取View测量的宽高,必须保证这两个方法在onLayout流程之后被调用才能返回有效值。

绘制过程就是把View对象绘制到屏幕上,整个draw过程需要注意如下细节:

  • 如果该View是一个ViewGroup,则需要递归绘制其所包含的所有子View。

  • View默认不会绘制任何内容,真正的绘制都需要自己在子类中实现。

  • View的绘制是借助onDraw方法传入的Canvas类来进行的。

  • 区分View动画和ViewGroup布局动画,前者指的是View自身的动画,可以通过setAnimation添加,后者是专门针对ViewGroup显示内部子视图时设置的动画,可以在xml布局文件中对ViewGroup设置layoutAnimation属性(譬如对LinearLayout设置子View在显示时出现逐行、随机、下等显示等不同动画效果)。

  • 在获取画布剪切区(每个View的draw中传入的Canvas)时会自动处理掉padding,子View获取Canvas不用关注这些逻辑,只用关心如何绘制即可。

  • 默认情况下子View的ViewGroup.drawChild绘制顺序和子View被添加的顺序一致,但是你也可以重载ViewGroup.getChildDrawingOrder()方法提供不同顺序。

Android上面的View是树形结构,View可能会重叠在一起,当我们点击的地方有多个View都可以响应的时候,这个点击事件应该给谁呢?为了解决这个问题,就有了事件分发机制。

  • dispatch TouchEvent用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级的dispatchTouchEvent方法影响,表示是否消耗此事件。
  • onInterceptTouchEvent在上述方法dispatchTouchEvent内部调用,用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。
  • onTouchEvent同样也会在dispatchTouchEvent内部调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。伪代码
public boolean dispatchTouchEvent(MotionEvent ev){ boolean consume = false;//记录返回值 if(onInterceptTouchEvent{//判断是否拦截此事件 consume = onTouchEvent;//如果当前确认拦截此事件,那么就处理这个事件 }else{ consume = child.dispatchToucnEvent;//如果当前确认不拦截此事件,那么就将事件分发给下一级 } return consume;}

通过上述伪代码,我们可以得知点击事件的传递规则:对于一个根ViewGroup而言,点击事件产生后,首先会传递给它,这时它的dispatchTouch就会被调用,如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前的事件,接着事件就会交给这个ViewGroup处理,即它的onTouch方法就会被调用;如果这个ViewGroup的onInterceptTouchEvent方法返回false就表示它不拦截当前事件,这时当前事件就会继续传递给它的子元素,接着子元素的dispatchTouchEvent方法就会被调用,如此直到事件被最终处理。

当一个View需要处理事件时,如果它设置了OnTouchListener,那么OnTouchListener中的onTouch方法会被回调。这时事件处理还要看onTouch的返回值,如果返回false,则当前View的onTouchEvent方法会被调用;如果返回true,那么当前View的onTouchEvent方法不会被调用。由此可见,给View设置的onTouchListener的优先级比onTouchEvent要高。在onTouchEvent方法中,如果当前设置的有onClickListener,那么它的onClick方法会被调用。可以看出,平时我们常用的OnClickListener,其优先级最低,即处于事件传递的尾端。

当一个点击事件产生后,它的传递过程遵循如下顺序:Activity–>Window–>View,即事件总数先传递给Activity,Activity再传递给Window,最后Window再传递给顶级View,顶级View接收到事件后,就会按照事件分发机制去分发事件。考虑一种情况,如果一个View的onTouchEvent返回false,那么它的父容器的onTouchEvent将会被调用,依次类推。如果所有的元素都不处理这个事件,那么这个事件将会最终传递给Activity处理, 即Activity的onTouchEvent方法会被调用。这个过程其实很好理解,我们可以换一种思路,假设点击事件是一个难题,这个难题最终被上级领导分给了一个程序员去处理,结果这个程序员搞不定(onTouchEvent返回了false),现在该怎么办呢?难题必须要解决,那就只能交给水平更高的上级解决(上级的onTouchEvent被调用),如果上级再搞不定,那就只能交给上级的上级去解决,就这样难题一层层地向上抛,这是公司内部一种常见的处理问题的过程。

关于事件传递机制还需要注意以下:

  1. 同一见事件序列是从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一系列事件,这个事件的序列以down开始,中间含有数量不定的move事件,最终以up事件结束。

  2. 正常情况下,一个事件序列只能被一个View拦截且消耗。这一条的原因可以参考,因为一旦一个元素拦截了某个事件,那么同一个事件序列的所有事件都会直接交给它处理,因此同一个事件序列中的事件不能分别由两个View同时处理,但是通过特殊手段可以做到,比如一个View将本该自己处理的事件通过onTouchEvent强行传递给其他View处理。

  3. 某个View一旦决定拦截,那么这个事件序列都只能由它来处理(如果事件序列能够传递给它的话),并且它的onInterceptTouchEvent不会被调用。这条也很好理解,就是说当一个View决定拦截一个事件后,那么系统会把同一个事件序列内的其他方法都直接交给它来处理,因此就不用再调用这个View的onInterceptTouchEvent去询问它是否拦截了。

  4. 某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false),那么同一件序列中的其他事件都不会再交给它处理,并且事件 将重新交由它的父元素去处理,即父元素的onTouchEvent会被调用。意思就是事件一旦交给一个View处理,那么它就必须消耗掉,否则同一事件序列中剩下的事件就不再交给它处理了,这就好比上级交给程序员一件事,如果这件事没有处理好,短时间内上级就不敢再把事件交给这个程序员做了,二者是类似的道理。

  5. 如果View不消耗ACTION_DOWN以外的事件,那么这个点击事件会消失,此时父元素的onTouchEvent并不会调用,并且当前View可以持续收到后续的事件,最终这些消失的点击事件会传递给Activity处理。

  6. ViewGroup默认不拦截任何事件。Android源码中ViewGroup的onInterceptTouchEvent方法默认返回false。

  7. View没有onInterceptTouchEvent方法,一旦点击事件传递给它,那么它的onTouchEvent方法就会被调用。

  8. View的onTouchEvent默认都会消耗事件,除非它是不可点击的(clickable和longClickable同时为false)。View的longClickable属性默认为false,clickable属性要分情况,比如Button的clickable属性默认为true,而TextView的clickable属性默认为false。

  9. View的enable属性不影响onTouchEvent的默认返回值。哪怕一个View是disable状态的,只要它的clickable或者longClickable有一个为true,那么它的onTouchEvent就返回true。

  10. onClick会发生的前提是当前View是可点击的,并且它接收到了down和up事件。

  11. 事件传递过程是由外向内的,即事件总是先传递给父元素,然后再由父元素分发给子View,通过requestDisallowInterTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外。

Activity–>PhoneWindow–>DecorView–>ViewGroup–>…–>View(View树最底部的View)

图片 10事件分发

ListView就是能用一个数据集合以动态滚动的方式展示到用户界面上的View。

  • ArrayAdapter:支持泛型操作,最简单的一个Adapter,只能展现一行文字
  • SimpleAdapter:同样具有良好扩展性的一个Adapter,可以自定义多种效果
  • BaseAdapter:抽象类,实际开发中我们会继承这个类并且重写相关方法,用得最多的一个Adapter

BaseAdapter是开发中最常用的适配器ArrayAdapter, SimpleAdapter 都继承于BaseAdapter。BaseAdapter可以完成自己定义的Adapter,可以将任何复杂组合的数据和资源,以任何你想要的显示效果展示给用户。

继承BaseAdapter之后,需要重写以下四个方法:getCount,getItem,getItemId,getView。

系统在绘制ListView之前,将会先调用getCount方法来获取Item的个数。每绘制一个Item就会调用一次getView方法,在getView中引用事先定义好的layout布局确定显示的效果并返回一个View对象作为一个Item显示出来。

这两个方法是自定ListView显示效果中最为重要的,同时只要重写好了这两个方法,ListView就能完全按开发者的要求显示。而getItem和getItemId方法将会在调用ListView的响应方法的时候被调用到。

在某一时刻,我们看到ListView中有许多View呈现在UI上,这些View对我们来说是可见的,这些可见的View可以称作OnScreen的View,即在屏幕中能看到的View,也可以叫做ActiveView,因为它们是在UI上可操作的。

当触摸ListView并向上滑动时,ListView上部的一些OnScreen的View位置上移,并移除了ListView的屏幕范围,此时这些OnScreen的View就变得不可见了,不可见的View叫做OffScreen的View,即这些View已经不在屏幕可见范围内了,也可以叫做ScrapView,Scrap表示废弃的意思,ScrapView的意思是这些OffScreen的View不再处于可以交互的Active状态了。ListView会把那些ScrapView(即OffScreen的View)删除,这样就不用绘制这些本来就不可见的View了,同时,ListView会把这些删除的ScrapView放入到RecycleBin中存起来,就像把暂时无用的资源放到回收站一样。

当ListView的底部需要显示新的View的时候,会从RecycleBin中取出一个ScrapView,将其作为convertView参数传递给Adapter的getView方法,从而达到View复用的目的,这样就不必在Adapter的getView方法中执行LayoutInflater.inflate()方法了。

RecycleBin中有两个重要的View数组,分别是mActiveViews和mScrapViews。这两个数组中所存储的View都是用来复用的,只不过mActiveViews中存储的是OnScreen的View,这些View很有可能被直接复用;而mScrapViews中存储的是OffScreen的View,这些View主要是用来间接复用的。

  • convertView重用机制

  • ViewHolder机制

  • 三级缓冲/滑动监听事件

优化一:在Adapter中的getView方法中使用ConvertView,即ConvertView的复用,不需要每次都inflate一个View出来,这样既浪费时间,又浪费内存。

优化二:使用ViewHolder,不要在getView方法中写findViewById方法,因为getView方法会执行很多遍,这样也可以节省时间,节约内存。

优化三:使用分页加载,讲真实际开发中,ListView的数据肯定不止几百条,成千上万条数据你不可能一次性加载出来,所以这里需要用到分页加载,一次加载几条或者十几条,但是如果数据量很大的话,像qq,微信这种,如果顺利加载到最后面的话,那么你的list中也会有几万甚至几十万的数据,这样可能也会导致OOM,所以你的数据集List中也不能有那么多数据,所以每加载一页的时候你可以覆盖前一页的数据。

优化四:如果数据当中有图片的话,使用第三方库来加载,如果你的能力强大到能自己维护的话,那也不是不可以。

优化五:当你手指在滑动列表的时候,尽可能的不加载图片,这样的话滑动就会更加流畅。

4.广播的实现

1、静态注册:在AndroidManifest中注册就可以,注册完成就一直运行,如果它
所依赖的activity销毁了,仍然能够接受广播,甚至app 进程杀死仍然能够接受广

2、动态注册:跟随activity生命周期,在代码中调用Context.registerReceiver方法

// 选择在Activity生命周期方法中的onResume()中注册
@Override
  protected void onResume(){
      super.onResume();

    // 1. 实例化BroadcastReceiver子类 &  IntentFilter
     mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
     IntentFilter intentFilter = new IntentFilter();

    // 2. 设置接收广播的类型
    intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);

    // 3. 动态注册:调用Context的registerReceiver()方法
     registerReceiver(mBroadcastReceiver, intentFilter);
 }


// 注册广播后,要在相应位置记得销毁广播
// 即在onPause() 中unregisterReceiver(mBroadcastReceiver)
// 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中
// 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
 @Override
 protected void onPause() {
     super.onPause();
      //销毁在onResume()方法中的广播
     unregisterReceiver(mBroadcastReceiver);
     }
}

WebView面试题

1、WebView安全漏洞

  • API16之前存在远程代码执行安全漏洞,该漏洞源于程序没有正确限制使用WebView.addJavascriptInterface方法,远程攻击者可通过使用Java反射机制利用该漏洞执行任意Java对象的方法

2、WebView销毁步骤

  • WebView在其他容器上时(如:LinearLayout),当销毁Activity时,需要在onDestroy()中先移除容器上的WebView,然后再将WebView.destroy(),这样就不会导致内存泄漏

3、WebView的jsbridge

  • 客户端和服务端之间可以通过Javascript来互相调用各自的方法

4、WebViewClient的onPageFinished

  • WebViewClient的onPageFinished在每次完成页面的时候调用,但是遇到未加载完成的页面跳转其他页面时,就会一直调用,使用WebChromeClient.onProgressChanged可以替代

5、WebView后台耗电

  • 在WebView加载页面的时候,会自动开启线程去加载,如果不很好的关闭这些线程,就会导致电量消耗加大,可以采用暴力的方法,直接在onDestroy方法中System.exit(0)结束当前正在运行中的java虚拟机

6、WebView硬件加速

  • Android3.0引入硬件加速,默认会开启,WebView在硬件加速的情况下滑动更加平滑,性能更加好,但是会出现白块或者页面闪烁的副作用,建议WebView暂时关闭硬件加速

7、WebView内存泄漏

  • 由于WebView是依附于Activity的,Activity的生命周期和WebView启动的线程的生命周期是不一致的,这会导致WebView一直持有对这个Activity的引用而无法释放,解决方案如下
  • 独立进程,简单暴力,不过可能涉及到进程间通信(推荐)
  • 动态添加WebView,对传入WebView中使用的Context使用弱引用
17. RxJava的机制是什么?

RxJava是使用Java语言,以响应式编程思维来进行编程的Java类库。参考ReactiveX

2.1 startService()方法开启Service

步骤: a.定义一个类继承Service。 b.在AndroidManifest.xml文件中配置该Service。 c.使用Context的startService方法启动该Service。 d.不再使用该Service时,调用Context的stopService方法停止该Service。

2.activity四种状态

running / paused /stopped / killed

running:处于活动状态,用户可以点击屏幕,屏幕会做出相应的响应,处于
activity栈顶的一个状态。

paused:失去焦点的状态,或者是被一个非全屏的activity所占据,又或是被一个
透明的activity放置再栈顶,只是失去了和用户交互的能力,所有的状态信息和成
员变量都还在,除非在内存非常紧张的情况下会被回收。

stopped:一个activity被另一个activity完全覆盖的时候,被覆盖的activtiy的就会
处于stopped状态,所有的状态信息和成员变量都还在,除非在内存非常紧张的
情况下会被回收。

killed:activity已经被系统回收,所有保存的状态信息和成员变量都将不存在了。

冷启动和热启动面试题

1、什么是冷启动和热启动

  • 冷启动:在启动应用前,系统中没有该应用的任何进程信息
  • 热启动:在启动应用时,在已有的进程上启动应用(用户使用返回键退出应用,然后马上又重新启动应用)

2、冷启动和热启动的区别
冷启动:创建Application后再创建和初始化MainActivity
热启动:创建和初始化MainActivity即可

3、冷启动时间的计算

  • 这个时间值从应用启动(创建进程)开始计算,到完成视图的第一次绘制为止

4、冷启动流程

  • Zygote进程中fork创建出一个新的进程
  • 创建和初始化Application类、创建MainActivity
  • inflate布局、当onCreate/onStart/onResume方法都走完
  • contentView的measure/layout/draw显示在界面上

总结:Application构造方法 -> attachBaseContext() -> onCreate() -> Activity构造方法 -> onCreate() -> 配置主题中背景等属性 -> onStart() -> onResume() -> 测量布局绘制显示在界面上

5、冷启动优化

  • 减少第一个界面onCreate()方法的工作量
  • 不要让Application参与业务的操作
  • 不要在Application进行耗时操作
  • 不要以静态变量的方式在Application中保存数据
  • 减少布局的复杂性和深度
  • 不要在mainThread中加载资源
  • 通过懒加载方式初始化第三方SDK
10. 在非UI线程中使用Handler需要注意哪些问题?

参考:
new Thread{
public void run
//给当前线程初始化Looper
{ Looper.prepare;
//Toast初始化的时候会new Handler;
Toast.makeText(getApplicationContext,"更新UI",0).show;
无参构造默认获取当前线程的Looper,如果没有prepare过,则抛出题主描述的异常。上一句代码初始化过了,就不会出错。 Looper.loop;//这句执行,Toast排队show所依赖的Handler发出的消息就有人处理了,Toast就可以吐出来了。但是,这个Thread也阻塞这里了,因为loop是个for (;;) ... } }.start;

1.3 属性动画

a.支持对所有View能更新的属性的动画(需要属性的setXxx()和getXxx。

b. 更改的是View实际的属性,所以不会影响其在动画执行后所在位置的正常使用。

c. Android3.0及以后出现的功能,3.0之前的版本可使用github第三方开源库nineoldandroids.jar进行支持。

2.关于sp的安全问题

Activity面试题
Fragment面试题
Service面试题
Broadcast Receiver面试题
WebView面试题
Binder面试题
Handler面试题
AsyncTask面试题
HandlerThread面试题
IntentService面试题
视图工作机制面试题
事件分发机制面试题
ListView面试题
Android项目构建面试题
ANR面试题
OOM面试题
Bitmap面试题
UI卡顿面试题
内存泄漏面试题
内存管理面试题
冷启动和热启动面试题
其他优化面试题
架构模式面试题
插件化面试题
热更新面试题
进程保活面试题
Lint面试题
Kotlin面试题

11. 自定义View时有哪些重要的方法,它们的作用及执行顺序是怎样的?

按照顺序:onMeasure
-->onLayout
-->onDraw
.其他的自己扩展吧。

2.1 ContentResolver的基本用法

想要访问内容提供者中共享的数据,就一定要借助CotentResolver类,可以通过Context中的getContentResolver()方法获取该类的实例。ContentResolver中提供了一系列的方法用于对数据进行CRUD操作,其中insert()方法用于添加数据,update()方法用于数据的更新,delete()方法用于数据的删除,query()方法用于数据的查询。这好像SQLite数据库操作有木有? 不同于SQLiteDatabase,ContentResolver中的增删改查方法都是不接收表名参数的,而是使用一个Uri的参数代替,这个参数被称作内容URI。内容URI给内容提供者中的数据建立了唯一的标识符,它主要由两部分组成:authority和path。authority是用于对不同的应用程序做区分的,一般为了避免冲突,都会采用程序包名的方式来进行命名。比如某个程序的包名为com.example.app,那么该程序对应的authority就可以命名为com.example.app.provider。path则是用于对同一应用程序中不同的表做区分的,通常都会添加到authority的后面。比如某个程序的数据库里存在两张表:table1和table2,这时就可以将path分别命名为/table1和/table2,然后把authority和path进行组合,内容的URI就变成了com.example.app.provider/table1和com.example.app.provider/table2。不过目前还是很难辨认出这两个字符串就是两个内容URI,我们还需要在字符串的头部加上协议声明。因此,内容URI最标准的格式写法如下:

content://com.example.app.provider/table1content://com.example.app.provider/table2

在得到内容URI字符串之后,我们还需要将它解析成Uri对象才可以作为参数传入。解析的方法也相当简单,代码如下所示:

Uri uri = new Uri.parse("content://com.example.app.provider/table1");

只需要调用Uri的静态方法parse()就可以把内容URI字符串解析成URI对象。 现在,我们可以通过这个Uri对象来查询table1表中的数据了。代码如下所示:

Cursor cursor = getContentResolver() .query( uri,projection,selection,selectionArgs,sortOrder );

query()方法接收的参数跟SQLiteDatabase中的query()方法接收的参数很像,但总体来说这个稍微简单一些,毕竟这是在访问其他程序中的数据,没必要构建复杂的查询语句。下标对内容提供者中的query的接收的参数进行了详细的解释:

查询完成仍然会返回一个Cursor对象,这时我们就可以将数据从Cursor对象中逐个读取出来了。读取的思路仍然是对这个Cursor对象进行遍历,然后一条一条的取出数据即可,代码如下:

if(cursor != null){//注意这里一定要进行一次判空,因为有可能你要查询的表根本不存在 while(cursor.moveToNext{ String column1 = cursor.getString(cursor.getColumnIndex("column1")); int column2 = cursor.getInt(cursor.getColumnIndex("column2")); }}

增加,删除,修改

//增加数据ContentValues values = new ContentValues();values.put("Column1","text");values.put("Column2","1");getContextResolver.insert(uri,values);//删除数据getContextResolver.delete(uri,"column2 = ?",new String[]{ "1" });//更新数据ContentValues values = new ContentValues();values.put("Column1","改数据");getContextResolver.update(uri,values,"column1 = ? and column2 = ?",new String[]{"text","1"});

前面已经提到过,如果要想实现跨程序共享数据的功能,官方推荐的方式就是使用内容提供器,可以新建一个类去继承ContentProvider类的方式来创建一个自己的内容提供器。ContentProvider类有6个抽象方法,我们在使用子类继承它的时候,需要将这6个方法全部重写。新建MyProvider继承字ContentProvider类,代码如下所示:

public class MyProvider extends ContentProvider { @Override public boolean onCreate() { return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; }//查询 @Override public Uri insert(Uri uri, ContentValues values) { return null; }//添加 @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; }//更新 @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; }//删除 @Override public String getType { return null; }}
  1. onCreate()方法: 初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作。返回true表示内容提供器初始化成功,返回false则表示失败。注意,只有当存在ContentResolver尝试访问我们的程序中的数据时,内容提供器才会被初始化。

  2. query()方法: 从内容提供器中查询数据。使用uri参数来确定查询的哪张表,projection参数用于确定查询的哪一列,selection和selectionArgs参数用于约束查询哪些行,sortOrder参数用于对结果进行排序,查询的结果存放在Cursor对象中返回。

  3. insert()方法: 向内容提供器中添加一条数据。使用uri参数来确定要添加的表,待添加的数据保存在values参数中。添加完成后,返回一个用于表示这条新纪录的URI。

  4. update()方法: 更新内容提供器中已有的数据。使用uri参数来确定更新哪一张表中的数据,新数据保存着values参数当中,selection和selectionArgs参数用于约束更新哪些行,受影响的行数将作为返回值返回。

  5. delete()方法: 从内容提供器中删除数据。使用uri参数来确定删除哪一张表中的数据,selection和selectionArgs参数用于约束删除哪些行,被删除的行数将作为返回值返回。

  6. getType()方法: 根据传入的内容URI来返回相应的MIME类型。

1.什么是冷启动

插件化面试题

1、插件化解决的问题

  • 动态加载APK(反射、类加载器)
  • 资源加载(反射、AssetManager、独立资源、分段资源)
  • 代码加载(反射获取生命周期)

2、类加载器(Java中字节码添加到虚拟机中)

  • DexClassLoader:能够加载未安装的jar/apk/dex,主要用于动态加载和代码热更新
  • PathClassLoader:只能加载系统中已经安装过的apk
14. 设计师只给了一套1280*800的UI图标注,如何进行其它分辨率尺寸屏幕的适配?

名称
像素密度范围
图片大小

mdpi
120dp~160dp
48×48px

hdpi
160dp~240dp
72×72px

xhdpi
240dp~320dp
96×96px

xxhdpi
320dp~480dp
144×144px

xxxhdpi
480dp~640dp
192×192px

以 720*1080 5英寸为例:
(720^2 1080^2)开方=260
放在xhdpi中。
本题中同理可以算得 293,还是xhdpi中。

1.2 Activity的生命周期

Activity启动 →onCreate()→onStart()→onResume();点击home键返回桌面→onPause()→onStop();再次回到原Activity→ onRestart()→onStart()→onResume();按返回键退出当前Activity→onPause()→onStop()→onDestroy();

优先级:前台>可见>服务>后台>空前台:正在与用户进行交互的Activity所在的进程可见:Activity可见但没有在前台所在的进程服务:Activity在后台开启了服务所在的进程后台:Activity完全处于后台所在的进程空:没有任何Activity存在的进程

4.commit和commitAllowingStateLoss区别

commit:该方法必须在存储状态之前调用,也就是onSaveInstanceState(),如果在存储状态之后调用就会报出:
Can not perform this action after onSaveInstanceState错误信息

commitAllowingStateLoss:允许在存储状态之后再调用,但是这是危险的,因为如果activity需要从其状态恢复,那么提交就会丢失,因此,只有在用户可以意外的更改UI状态的情况下,才可以使用该提交。

以下是部分的核心源码:

图片 11

commit与commitAllowingStateLoss源码对比.png

Bitmap面试题

1、recycle

  • 在安卓3.0以前Bitmap是存放在堆中的,我们只要回收堆内存即可
    在安卓3.0以后Bitmap是存放在内存中的,我们需要回收native层和Java层的内存
  • 官方建议我们3.0以后使用recycle方法进行回收,该方法也可以不主动调用,因为垃圾回收器会自动收集不可用的Bitmap对象进行回收
  • recycle方法会判断Bitmap在不可用的情况下,将发送指令到垃圾回收器,让其回收native层和Java层的内存,则Bitmap进入dead状态
    recycle方法是不可逆的,如果再次调用getPixels()等方法,则获取不到想要的结果

2、LruCache原理

  • LruCache是个泛型类,内部采用LinkedHashMap来实现缓存机制,它提供get方法和put方法来获取缓存和添加缓存,其最重要的方法trimToSize是用来移除最少使用的缓存和使用最久的缓存,并添加最新的缓存到队列中

3、计算inSampleSize

public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {
    if (width > height) {
        inSampleSize = Math.round((float)height / (float)reqHeight);
    } else {
        inSampleSize = Math.round((float)width / (float)reqWidth);
    }
}
return inSampleSize;
}

4、缩略图

public static Bitmap thumbnail(String path,int maxWidth, int maxHeight, boolean autoRotate) {
  BitmapFactory.Options options = new BitmapFactory.Options();
  options.inJustDecodeBounds = true;
  Bitmap bitmap = BitmapFactory.decodeFile(path, options);
  options.inJustDecodeBounds = false;
  int sampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
  options.inSampleSize = sampleSize;
  options.inPreferredConfig = Bitmap.Config.RGB_565;
  options.inPurgeable = true;
  options.inInputShareable = true;
  if (bitmap != null && !bitmap.isRecycled()) {
    bitmap.recycle();
}
  bitmap = BitmapFactory.decodeFile(path, options);
  return bitmap;

}

5、保存Bitmap

public static String save(Bitmap bitmap,Bitmap.CompressFormat format, int quality, File destFile) {
try {
    FileOutputStream out = new FileOutputStream(destFile);
    if (bitmap.compress(format, quality, out)) {
        out.flush();
        out.close();
    }
    if (bitmap != null && !bitmap.isRecycled()) {
        bitmap.recycle();
    }
    return destFile.getAbsolutePath();
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}
return null;

}

6、保存到SD卡

public static String save(Bitmap bitmap,Bitmap.CompressFormat format, int quality, Context context) {
if (!Environment.getExternalStorageState()
        .equals(Environment.MEDIA_MOUNTED)) {
    return null;
}
File dir = new File(Environment.getExternalStorageDirectory()
          "/"   context.getPackageName()   "/save/");
if (!dir.exists()) {
    dir.mkdirs();
}
File destFile = new File(dir, UUID.randomUUID().toString());
return save(bitmap, format, quality, destFile);

}

7、三级缓存

  • 网络缓存
  • 本地缓存
  • 内存缓存
12. 如何单独对ListView上的一个item进行更新?

更新对应view的内容
通过ViewHolder去设置值
调用一次getView方法(Google IO 推荐)

1.2 Fragment 与ViewPager搭配使用

通常情况下我们开发应用最常见的使用情况是TabLayout ViewPager Fragment的使用方式,这就涉及到两个常用的适配器的使用,一个是FragmentPagerAdapter,另外一个是FragmentStatePagerAdapter,那么它们之间有什么区别呢?其实很简单,FragmentPagerAdapter适用于页面较少的情况,而FragmentStatePagerAdapter适用于页面较多的情况。

Fragment

界面打开onCreate() 方法执行!onCreateView() 方法执行!onActivityCreated() 方法执行!onStart() 方法执行!

onResume() 方法执行!

按下主屏幕键/锁屏

onPause() 方法执行!onStop() 方法执行!

重新打开onStart() 方法执行!onResume() 方法执行!

按下后退键onPause() 方法执行!onStop() 方法执行!onDestroyView() 方法执行!onDestroy() 方法执行!onDetach() 方法执行!

Activity

打开应用onCreate() 方法执行!onStart() 方法执行!onResume() 方法执行!

按下主屏幕键/锁屏onPause() 方法执行!onStop() 方法执行!

重新打开应用onRestart() 方法执行!onStart() 方法执行!onResume() 方法执行!

按下后退键onPause() 方法执行!onStop() 方法执行!onDestroy() 方法执行!

在Activity中加入Fragment,对应的生命周期

打开Fragment onAttach()方法执行Fragment onCreate() 方法执行!Fragment onCreateView() 方法执行!Fragment onViewCreated()方法执行Activity onCreate() 方法执行!Fragment onActivityCreated() 方法执行!Activity onStart() 方法执行!Fragment onStart() 方法执行!Activity onResume() 方法执行!Fragment onResume() 方法执行!

按下主屏幕键/锁屏Fragment onPause() 方法执行!Activity onPause() 方法执行!Fragment onStop() 方法执行!Activity onStop() 方法执行!

再次打开Activity onRestart() 方法执行!Activity onStart() 方法执行!Fragment onStart() 方法执行!Activity onResume() 方法执行!Fragment onResume() 方法执行!

按下后退键Fragment onPause() 方法执行!Activity onPause() 方法执行!Fragment onStop() 方法执行!Activity onStop() 方法执行!Fragment onDestroyView() 方法执行!Fragment onDestroy() 方法执行!Fragment onDetach() 方法执行!Activity onDestroy() 方法执行!

梳理ButterKnife原理流程

  • 开始它会扫描java代码中所有的ButterKnife注解
  • ButterKnifeProcssor ---><className>$$ViewBinder
  • 调用bind方法加载生成的ViewBinder类

Lint面试题

1、什么是Android Lint

  • Android Lint是一个静态代码分析工具,它能够对你的Android项目中潜在的Bug、可优化的代码、安全性、性能、可用性、可访问性、国际化等进行检查

2、Lint工作流程

图片 12

这里写图片描述

3、配置Lint

  • 创建Lint.xml到根目录下,自定义Lint安全等级等
  • 在Java文件中可以使用@suppressLint(“NewApi”)来忽视Lint的报错
  • 在xml文件中可以使用tool:ignore(“UnusedResources”)来忽视Lint的报错
  • 自定义Lint检查,可以创建类,继承Detector和实现JavaPsiScanner
Activity/Window/View三者的差别,fragment的特点

Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图) LayoutInflater像剪刀,Xml配置像窗花图纸。
在Activity中调用attach,创建了一个Window
1.创建的window是其子类PhoneWindow,在attach中创建PhoneWindow
2.在Activity中调用setContentView(R.layout.xxx)
3.其中实际上是调用的getWindow().setContentView()
4.调用PhoneWindow中的setContentView方法
5.创建ParentView:作为ViewGroup的子类,实际是创建的DecorView(作为FramLayout的子类)
6.将指定的R.layout.xxx进行填充通过布局填充器进行填充【其中的parent指的就是DecorView】
7.调用到ViewGroup
8.调用ViewGroup的removeAllView(),先将所有的view移除掉
9.添加新的view:addView()

fragment 特点
Fragment可以作为Activity界面的一部分组成出现;
可以在一个Activity中同时出现多个Fragment,并且一个Fragment也可以在多个Activity中使用;
在Activity运行过程中,可以添加、移除或者替换Fragment;
Fragment可以响应自己的输入事件,并且有自己的生命周期,它们的生命周期会受宿主Activity的生命周期影响。

2.2 bindService方法开启Service(Activity与Service绑定)

步骤: a.创建BinderService服务端,继承自Service并在类中创建一个实现IBinder接口的实现实例对象并提供公共方法给客户端调用。 b.从onBind()回调方法返回此Binder实例。 c.在客户端中,从onServiceConnected回调方法接收Binder,并使用提供的方法调用绑定服务。

服务的生命周期有两种,因为服务可以跟Activity绑定起来,也可以不绑定,Activity和服务进行通信的话,是需要把服务和Activity进行绑定的。因此服务的生命周期分为未绑定Activity的和绑定Activity的。

没有绑定Activity的服务生命周期:

启动服务>onCreate()>onStartCommand()>服务运行>onDestory()>服务销毁

绑定Activity的服务生命周期

绑定服务>onCreate()>onBind()>服务运行>onUnBind()>onDestory()>服务被销毁

  1. 通过Intent和startService()方法启动了一个服务,接下来执行onCreate()方法,首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind。如果服务已在运行,则不会调用此方法。

  2. 当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果您实现此方法,则在服务工作完成后,需要由您通过调用 stopSelf() 或 stopService() 来停止服务。(如果您只想提供绑定,则无需实现此方法。)

  3. 服务开始处于运行状态。

  4. 某个操作导致服务停止,比如执行了方法stopService(),那么服务接下来会执行onDestory()销毁。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。

  5. 服务被完全销毁,下一步就是等待被垃圾回收器回收了。

  6. 通过Intent和bindService()方法启动了一个服务,接下来会执行onCreate()方法,首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind。如果服务已在运行,则不会调用此方法。

  7. 当另一个组件想通过调用 bindService() 与服务绑定时,系统将调用此方法。在此方法的实现中,您必须通过返回 IBinder 提供一个接口,供客户端用来与服务进行通信。请务必实现此方法,但如果您并不希望允许绑定,则应返回 null。

  8. 服务开始处于运行状态。成功与Activity绑定。

  9. 某个操作导致服务解除绑定,比如执行了方法unbindService(),那么服务接下来会解除与当前Activity的绑定。接下来服务将面临销毁。

  10. 服务执行onDestory()方法被销毁。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。

  11. 服务被完全销毁,下一步就是等待被垃圾回收器回收了。

Service总结:

  1. 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。

  2. 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。

  3. 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。

  4. 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

2.singleTop(栈顶复用模式)

如果需要创建的activity实例刚好处于任务栈的栈顶,那么就不会再去创建一个新
的activity实例了,而是复用这个栈顶的activity。

Broadcast Receiver面试题

1、Broadcast Receiver是什么

  • Broadcast是四大组件之一,是一种广泛运用在应用程序之间传输信息的机制,通过发送Intent来传送我们的数据

2、Broadcast Receiver的使用场景

  • 同一App具有多个进程的不同组件之间的消息通信
  • 不同App之间的组件之间的消息通信

3、Broadcast Receiver的种类

  • 普通广播
  • 有序广播
  • 本地广播
  • Sticky广播

4、Broadcast Receiver的实现

  • 静态注册:注册后一直运行,尽管Activity、进程、App被杀死还是可以接收到广播
  • 动态注册:跟随Activity的生命周期

5、Broadcast Receiver实现机制

  • 自定义广播类继承BroadcastReceiver,复写onReceiver()
  • 通过Binder机制向AMS进行注册广播
  • 广播发送者通过Binder机制向AMS发送广播
  • AMS查找符合相应条件的广播发送到BroadcastReceiver相应的循环队列中
  • 消息队列执行拿到广播,回调BroadcastReceiver的onReceiver()

6、LocalBroadcastManager特点

  • 本地广播只能在自身App内传播,不必担心泄漏隐私数据
  • 本地广播不允许其他App对你的App发送该广播,不必担心安全漏洞被利用
  • 本地广播比全局广播更高效
  • 以上三点都是源于其内部是用Handler实现的
自我介绍

我叫王少健,平时喜欢旅游,一方面作为兴趣爱好的培养,同时也是自我释压的方式;和平友爱是我的交流方式,做人不失小幽默。我比较喜欢专研新技术,有较扎实的Java和Android方面的基础知识。在上一年工作,因为是我个人独立开发的程序,在公司中我学到了很多,主要是在学习中找到了快乐,不断在沉淀自己地小天地。感到在工作中的我很充实;希望通过自身不懈的努力与执着的追求,在IT领域中有所成就.

1.standard

每次启动activity都会重新创建一个activity的实例,然后将它加到任务栈中,不会
去考虑任务栈是否有该activity的实例,默认的情况就是此种启动模式

分享一篇Android知识点总结文章 面试复习——Android工程师之Android面试大纲

Activity,View,Window和WindowManager之间的关系

View:最基本的UI组件,表示屏幕上的一个矩形区域。
Window: 表示一个窗口,包含一个View tree和窗口的layout 参数。View tree的root View可以通过getDecorView得到。还可以设置Window的Content View。
Activity包含一个Window,该Window在Activity的attach方法中通过调用PolicyManager.makeNewWindow创建。
WindowManager:一个interface,继承自ViewManager。 有一个implementation class:android.view.WindowManagerImpl。其实WindowManager并不是整个系统的窗口管理器,而是所在应用进程的窗口管理器。系统全局的窗口管理器运行在SystemServer进程中,是一个Service。ViewRoot通过IWindowSession接口与全局窗口管理器进行交互。 将一个View add到WindowManager时,WindowManagerImpl创建一个ViewRoot来管理该窗口的根View。,并通过ViewRoot.setView方法把该View传给ViewRoot。
ViewRoot用于管理窗口的根View,并和global window manger进行交互。ViewRoot中有一个nested class: W,W是一个Binder子类,用于接收global window manager的各种消息, 如按键消息, 触摸消息等。 ViewRoot有一个W类型的成员mWindow,ViewRoot在Constructor中创建一个W的instance并赋值给mWindow。 ViewRoot是Handler的子类, W会通过Looper把消息传递给ViewRoot。 ViewRoot在setView方法中把mWindow传给sWindowSession。
总之,每个窗口对应着一个Window对象,一个根View和一个ViewRoot对象。要想创建一个窗口,可以调用WindowManager的addView方法,作为参数的view将作为在该窗口上显示的根view。

3.activity生命周期

图片 13

Activity生命周期.gif

  • Activity启动➡️ onCreate()➡️ onStart()➡️ onResume()

    onCreate():activity被创建的时候调用,生命周期的第一个调用方法,创建
    activity的时候一定要重写该方法,可以做一些初始化的操作,比如布局资源的加
    载或者一些数据的加载。

    onStart():表明此时activity正在启动,已经处于用户可见的状态,当然此时还没
    有处于前台的显示,就是说此时用户还不能与用户进行交互操作;总结就是一个
    可以看见但是无法触摸的状态。

    onResume():已经在前台进行展示,可以与用户进行交互了。

  • 点击Home键回到手机桌面(Activity不可见):onPause()➡️ onstop()

    onPause():activity处于一个暂停的状态,可见但是不可交互

    onstop():整个activity表明已经完全停止,或者完全被覆盖的状态,完全不可
    见,处于后台的状态,如果处于内存紧张的状态下就有可能会被系统回收掉。

  • 当我们再次回到原Activity的时候:onRestart()➡️ onStart()➡️ onResume()

    onStart():表示activity正在重新启动,是由不可见状态变为可见状态的时候调用

  • 退出Activity时:onPause()➡️ onStop()➡️ onDestory()
    onDestory():当前activity正在销毁,是生命周期中的最后一个方法,可以做一
    些回收工作以及资源的释放

OOM面试题

1、什么是OOM

  • OOM指Out of memory(内存溢出),当前占用内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出Out of memory异常

2、OOM相关概念

  • 内存溢出:指程序在申请内存时,没有足够的空间供其使用
  • 内存泄漏:指程序分配出去的内存不再使用,无法进行回收
  • 内存抖动:指程序短时间内大量创建对象,然后回收的现象

3、解决OOM

  • Bitmap相关
  • 图片压缩
  • 加载缩略图
  • 在滚动时不加载图片
  • 回收Bitmap
  • 使用inBitmap属性
  • 捕获异常

其他相关

  • listview重用convertView、使用lru
  • 避免onDraw方法执行对象的创建
  • 谨慎使用多进程
IntentService与Service的区别

Service主要用于后台服务 当应用程序被挂到后台的时候,为了保证应用某些组件仍然可以工作而引入了Service这个概念,那么这里面要强调的是Service不是独立的进程,也不是独立的线程,它是依赖于应用程序的主线程的,在更多时候不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。这时需要引入IntentService,IntentService是继承Service的,那么它包含了Service的全部特性,当然也包含service的生命周期,那么与service不同的是,IntentService在执行onCreate操作的时候,内部开了一个线程,去执行你的耗时操作。
IntentService:异步处理服务,新开一个线程:handlerThread在线程中发消息,然后接受处理完成后,会清理线程,并且关掉服务。
IntentService有以下特点:
(1) 它创建了一个独立的工作线程来处理所有的通过onStartCommand()传递给服务的intents。
(2) 创建了一个工作队列,来逐个发送intent给onHandleIntent()。
(3) 不需要主动调用stopSelft()来结束服务。因为,在所有的intent被处理完后,系统会自动关闭服务。
(4) 默认实现的onBind()返回null
(5) 默认实现的onStartCommand()的目的是将intent插入到工作队列中

1.service是什么

service(服务)是一个可以在用户后台执行长时间操作,而没有用户界面的应用组件
注意:service是运行在主线程中的,绝对是不能做耗时操作的。
请勿将服务与子线程混为一谈

其他优化面试题

1、Android不用静态变量存储数据

  • 静态变量等数据由于进程已经被杀死而被初始化
  • 使用其他数据传输方式:文件/sp/contentProvider

2、SharePreference安全问题

  • 不能跨进程同步
  • 文件不宜过大

3、内存对象序列化

  • Serializeble:是java的序列化方式,Serializeble在序列化的时候会产生大量的临时对象,从而引起频繁的GC
  • Parcelable:是Android的序列化方式,且性能比Serializeble高,Parcelable不能使用在要将数据存储在硬盘上的情况

4、避免在UI线程中做繁重的操作

说一下上一个项目中的主要技术、

使用http和ftp实现与服务器交互,并且下载文件。
使用的University_Image_Loader实现的图片的三级缓存(看一下源码)
通过广播实现了fragment之间的沟通。

4.Android视图工作机制中的重绘

一、invalidate()和requestLayout()

invalidate()和requestLayout(),常用于View重绘和更新,其主要区别如下

invalidate方法只会执行onDraw方法
requestLayout方法只会执行onMeasure方法和onLayout方法,并不会执行onDraw方法。
所以当我们进行View更新时,若仅View的显示内容发生改变且新显示内容不影响View的大小、位置,则只需调用invalidate方法;若View宽高、位置发生改变且显示内容不变,只需调用requestLayout方法;若两者均发生改变,则需调用两者,按照View的绘制流程,推荐先调用requestLayout方法再调用invalidate方法

二、invalidate()和postInvalidate()

invalidate方法用于UI线程中重新绘制视图
postInvalidate方法用于非UI线程中重新绘制视图,省去使用handler

博文

十二、事件分发

1、简要的谈谈Android的事件分发机制?

当点击事件发生时,首先Activity将TouchEvent传递给Window,再从Window传递给顶层View。TouchEvent会最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则整个事件将会被销毁,如果dispatchTouchEvent返回 false ,则交给上层view的 onTouchEvent 方法来开始处理这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给自身的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么事件将继续传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了,且这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最顶view的 onTouchEvent 也返回 false 的话,这个事件就会消失,直到onTouchEvent返回true为止。

2、为什么View有dispatchTouchEvent方法?

因为View可以注册很多事件的监听器,如长按、滑动、点击等,它也需要一个管理者来分发

3、ViewGroup中可能有很多个子View,如何判断应该分配给哪一个?

根据源码可知,它会分配给在点击范围内的子View

4、当点击时,子View重叠应该如何分配?

一般分配给最上层的子View,这是由于安卓的渲染机制导致的

博文

AsyncTask面试题

1、AsyncTask是什么

  • 它本质上就是一个封装了线程池和Handler的异步框架

2、AsyncTask使用方法

  • 三个参数

    • Params:表示后台任务执行时的参数类型,该参数会传给AysncTask的doInBackground()方法
    • Progress:表示后台任务的执行进度的参数类型,该参数会作为onProgressUpdate()方法的参数
    • Result:表示后台任务的返回结果的参数类型,该参数会作为onPostExecute()方法的参数
  • 五个方法

    • onPreExecute():异步任务开启之前回调,在主线程中执行
    • doInBackground():执行异步任务,在线程池中执行
    • onProgressUpdate():当doInBackground中调用publishProgress时回调,在主线程中执行
    • onPostExecute():在异步任务执行之后回调,在主线程中执行
    • onCancelled():在异步任务被取消时回调

3、AsyncTask工作原理

  • Android进阶——多线程系列之异步任务AsyncTask的使用与源码分析

4、AsyncTask引起的内存泄漏

  • 原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放
  • 解决: AsyncTask内部持有外部Activity的弱引用
    AsyncTask改为静态内部类
    AsyncTask.cancel()

5、AsyncTask生命周期

  • 在Activity销毁之前,取消AsyncTask的运行,以此来保证程序的稳定

6、AsyncTask结果丢失

  • 由于屏幕旋转、Activity在内存紧张时被回收等情况下,Activity会被重新创建,此时,旧的AsyncTask持有旧的Activity引用,这个时候会导致AsyncTask的onPostExecute()对UI更新无效

7、AsyncTask并行or串行

  • AsyncTask在Android 2.3之前默认采用并行执行任务,AsyncTask在Android 2.3之后默认采用串行执行任务
  • 如果需要在Android 2.3之后采用并行执行任务,可以调用AsyncTask的executeOnExecutor()
LaunchMode应用场景

standard,创建一个新的Activity。
singleTop,栈顶不是该类型的Activity,创建一个新的Activity。否则,onNewIntent。
singleTask,回退栈中没有该类型的Activity,创建Activity,否则,onNewIntent ClearTop。
设置了"singleTask"启动模式的Activity,它在启动的时候,会先在系统中查找属性值affinity等于它的属性值taskAffinity的Task存在; 如果存在这样的Task,它就会在这个Task中启动,否则就会在新的任务栈中启动。因此, 如果我们想要设置了"singleTask"启动模式的Activity在新的任务中启动,就要为它设置一个独立的taskAffinity属性值。
如果设置了"singleTask"启动模式的Activity不是在新的任务中启动时,它会在已有的任务中查看是否已经存在相应的Activity实例, 如果存在,就会把位于这个Activity实例上面的Activity全部结束掉,即最终这个Activity 实例会位于任务的Stack顶端中。
在一个任务栈中只有一个”singleTask”启动模式的Activity存在。他的上面可以有其他的Activity。这点与singleInstance是有区别的。
singleInstance,回退栈中,只有这一个Activity,没有其他Activity。

singleTop适合接收通知启动的内容显示页面。
例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。
singleTask适合作为程序入口点。
例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。
singleInstance应用场景:
闹铃的响铃界面。 你以前设置了一个闹铃:上午6点。在上午5点58分,你启动了闹铃设置界面,并按 Home 键回桌面;在上午5点59分时,你在微信和朋友聊天;在6点时,闹铃响了,并且弹出了一个对话框形式的 Activity(名为 AlarmAlertActivity) 提示你到6点了(这个 Activity 就是以 SingleInstance 加载模式打开的),你按返回键,回到的是微信的聊天界面,这是因为 AlarmAlertActivity 所在的 Task 的栈只有他一个元素, 因此退出之后这个 Task 的栈空了。如果是以 SingleTask 打开 AlarmAlertActivity,那么当闹铃响了的时候,按返回键应该进入闹铃设置界面。

2.原因:应用程序的响应性是由Activity Manager和Window Manager系统服务监视的,当它监测到组件在相应的响应的时间没有响应就会弹出ARN的提示。
  • 哪些操作是在主线程中呢?
    Activity中所有生命周期的回调都是在主线程中。

    Service默认是执行在主线程中的。

    BrocastReceiver中的onReceve()回调执行在主线程中。

    没有使用子线程的Lopper的Handler的handlerMessage,post(Runner)是执行在
    主线程中的。

    AsyncTask中除了doInBackground,其他方法都是执行在主线程中的。

Handler面试题

1、Handler是什么

  • Handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue
    2、Handler使用方法
  • post(runnable)
  • sendMessage(message)

3、Handler工作原理
Android进阶——Android消息机制之Looper、Handler、MessageQueen

4、Handler引起的内存泄漏

  • 原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放
  • 解决: Handler内部持有外部Activity的弱引用
    Handler改为静态内部类
    Handler.removeCallback()
16. EventBus的机制是什么?和Handler的区别怎样?

EventBus
是采用观察者模式实现的事件订阅总线,可以用在应用程序中,组件之间,线程之间的通信,并且由于事件可以是任意类型的对象,所以使用起来更加的方便快捷。
Handler
是 Android 的消息机制,集中解决线程间通信问题。

1.kotlin到底是什么
  • 是一种基于JVM的编程语言
  • 是对java的一种扩展
  • kotlin支持函数式编程
  • kotlin类与java类能相互调用

Fragment面试题

1、Fragment为什么被称为第五大组件
Fragment比Activity更节省内存,其切换模式也更加舒适,使用频率不低于四大组件,且有自己的生命周期,而且必须依附于Activity

2、Activity创建Fragment的方式

  • 静态创建
  • 动态创建

3、FragmentPageAdapter和FragmentPageStateAdapter的区别

  • FragmentPageAdapter在每次切换页面的的时候,是将Fragment进行分离,适合页面较少的Fragment使用以保存一些内存,对系统内存不会多大影响
  • FragmentPageStateAdapter在每次切换页面的时候,是将Fragment进行回收,适合页面较多的Fragment使用,这样就不会消耗更多的内存

4、Fragment生命周期

  • onAttach()
  • onCreate()
  • onCreateView()
  • onActivityCreated()
  • onStart()
  • onResume()
  • onPause()
  • onStop()
  • onDestroyView()
  • onDestroy()
  • onDetach()

5、Fragment的通信

  • Fragment调用Activity中的方法:getActivity
  • Activity调用Fragment中的方法:接口回调
  • Fragment调用Fragment中的方法:FragmentManager.findFragmentById

6、Fragment的replace、add、remove方法

  • replace:替代Fragment的栈顶页面
  • add:添加Fragment到栈顶页面
  • remove:移除Fragment栈顶页面
介绍Activity、Service、Broadcast、BroadcastReceiver、Intent、IntentFilter

Activity
活动(Activity) - 用于表现功能
服务(Service) - 相当于后台运行的 Activity
广播(Broadcast) - 用于发送广播
广播接收器(BroadcastReceiver) - 用于接收广播
Intent - 用于连接以上各个组件,并在其间传递消息
(1)Action也就是要执行的动作
(2)Data,也就是执行动作要操作的数据
(3)type(数据类型)
(4)category(类别)
(5)component(组件)
(6)extras(附加信息)
Intent filters
要告诉android系统哪个intent它们可以处理,activities,services,和 broadcast receivers 必须设置一个或者多个intent过滤器。每个过滤器描述了组件的一种能力,它过滤掉不想要的intent,留下想要的。显示意图则不用考虑这些。
一个过滤器中包含 一个Intent object 中的三个属性 action,data,catrgory 。一个隐式意图必须要通过这三项测试才能传递到 包含该过滤器的组件中

onInterceptTouchEvent

  • return true:表示将事件进行拦截,并将拦截到的事件交由本层控件的onTouchEvent进行处理
  • return false:表示不对事件进行拦截,事件得以成功分发到子View
  • return super.onInterceptTouchEvent(ev):默认表示不拦截该事件,并将事件传递给下一层View的dispatchTouchEvent

架构模式面试题

  • Android基础——框架模式MVC在安卓中的实践
  • Android基础——框架模式MVP在安卓中的实践
  • Android基础——框架模式MVVM之DataBinding的实践
2. 什么情况下,Activity的onNewInstent方法会执行?Activity的启动模式相关。

当此Activity的实例已经存在,并且此时的启动模式为SingleTask和SingleInstance,另外当这个实例位于栈顶且启动模式为SingleTop时也会触发onNewInstent。

1.什么是activity?

Activity一个应用程序的组件,它提供一个屏幕来与用户交互,以便做一些诸如打电话、发邮件和看地图之类的事情,原话如下:
An [Activity](http://developer.android.com/reference/android/app/Activity.html) is an application component that provides a screen with which users can interact in order to do something, such as dial the phone, take a photo, send an email, or view a map.

Service面试题

1、Service是什么

  • Service是四大组件之一,它可以在后台执行长时间运行操作而没有用户界面的应用组件

2、Service和Thread的区别

  • Service是安卓中系统的组件,它运行在独立进程的主线程中,不可以执行耗时操作。Thread是程序执行的最小单元,分配CPU的基本单位,可以开启子线程执行耗时操作
  • Service在不同Activity中可以获取自身实例,可以方便的对Service进行操作。Thread在不同的Activity中难以获取自身实例,如果Activity被销毁,Thread实例就很难再获取得到

3、Service启动方式

  • startService
  • bindService

4、Service生命周期

  • startService

    • onCreate()
    • onStartCommand()
    • onDestroy()
  • bindService

    • onCreate()
    • onBind()
    • onUnbind()
    • onDestroy()
2.两种启动service的方式
  • startService
    1、定义一个类继承Service
    2、在Manifest.xml文件中配置该service
    3、使用Context中的startService(Intent)方法启动该Service
    4、不使用时,使用stopService(Intent)方法停止该服务
    5、onCreate()方法只会调用一次, onStartCommand()方法可以重复调用
    6、服务一旦启动,服务即可在后台无限期的运行,即便启动服务的组件被销毁
    了也不会受影响,除非手动关闭,已启动的服务通常是执行单一操作,而且不会
    将结果返回给调用方

  • bindService
    1、创建BindService服务端,继承自service并在类中,创建一个实现IBinder接
    口的实例对象并提供公共方法给客户端使用
    2、从onBind()回调方法返回此Binder实例
    3、在客户端中,从onServiceConncted()回调方法接收Binder,并使用提供的方法调用绑定服务
    4、当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。
    绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、
    获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应
    用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取
    消绑定后,该服务即会被销毁

最后附上一张官方生命周期图:

图片 14

service生命周期图.png

参考博文

四、Broadcast receiver

博文
博文

4. singleInstance

singleTask的升级版,整个系统中只有一个实例,启动一singleInstanceActivity
时,系统会创建一个新的任务栈,并且这个任务栈只有他一个Activity,一般用于
多个应用之间的跳转。

  • singleTask与singleTask的却别:
    一个系统中可能存在多个singleTask Activity实例,
    一个系统中只能存在一个singleInstance Activity实例。

四、scheme跳转协议

android中的scheme是一种页面内跳转协议,是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便的跳转app中的各个页面;通过scheme协议,服务器可以定制化的告诉app跳转哪个页面,可以通过消息栏定义化跳转页面,可以通过H5页面跳转页面等。

二、Fragment

1.广播定义

在Android中,Broadcast是一种广泛运用在应用程序之间传输信息的机制,我们
要发送的广播内容是一个Intent,这个Itent中可以携带我们需要传输的数据。

1.Fragment为什么被称为第五大组件

首先在使用上是不属于其他四大组件的,它有自己的生命周期,同时它可以灵活
动态的加载到activity中去,同时它并不是像activity一样完全独立的,它必须依附
于activity,而且还需要加载到activity中去。

2.kotlin的环境搭建
  • Android studio安装kotlin插件
  • 编写kotlinActivity测试
  • kotlin会自动配置kotlin

三十四、LruCache

LruCache源码分析
DiskLruCache

LruCache是一个针对内存层面的缓存,基于LRU算法(最近最少使用)实现的。
内部主要是使用LinkHashMap来存储数据,LinkHashMap是继承HashMap的,主要是重写了get方法,当获取数据的时候会把自己添加到节点的头部,保证处于链表的最前面;不常用的数据就会排在最后面,当缓存的数据满了之后,一般是申请进程空间的1/8,就会清楚掉排在最后的这些数据,trimToSize()方法中执行

public V get(Object key) {
        //使用HashMap的getEntry方法,验证了我们所说的查询效率为O(1)
        LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
        if (e == null)
            return null;
        e.recordAccess(this);//在找到节点后调用节点recordAccess方法 往下看
        return e.value;
    }

        //LinkedHashMapEntry
        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            if (lm.accessOrder) {//accessOrder该值默认为false 但是  在LruCache中设置为true
                lm.modCount  ;
                remove();//该方法将本身节点移除链表
                addBefore(lm.header);//将自己添加到节点头部 保证最近使用的节点位于链表前边
            }
        }

三十五、算法

八大算法

1.measure

测量中首先得知道测量规则,MeasureSpec这个类

MeasureSpec这个类包括测量模式(specModel)
和测量模式下的规格大小(specSize)

MeasureSpec表示形式是32位的int值
前两位是specModel 后30位是specSize

我们都知道SpecMode的尺子类型有很多,不同的尺子有不同的功能,而SpecSize刻度是固定的一种,所以SpecMode又分为三种模式

UNSPECIFIED:父容器不对View有任何大小的限制,这种情况一般用于系统内部,表示一种测量状态
EXACTLY:父容器检测出View所需要的精确大小,这时候View的值就是SpecSize
AT_MOST:父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值

measure是final修饰的,需要重写的方式是onMeasure()

图片 15

measure.png

1.recycle
4.缩略图
2.Android进程的回收策略
  • Low momery killer:通过一些比较复杂的评分机制,对进程进行打分,然后将分数高的进程判定为bad进程,杀死并释放内存。

  • OOM_ODJ:判读进程优先级

3.如何对冷启动的时间进行优化

博文

二十七、其他优化

1.android中不用静态变量存储数据
4.避免ui线程中做繁重的操作

博文

二十七、mvc/mvp/mvvm

博文1
博文2
博文3

二十八、插件化

二十九、热修复
原理
三十、进程保活

3.FragmentPagerAdapter与FragmentStatePagerAdapter的区别

FragmentPagerAdapter适用于页面较少的情况下,在源码中destoryItem()函数中的最后一行可以看到,调用的是detach方法,该方法的作用是将fragment与activity的关联取消掉,并没有做内存回收处理,所以会更加的消耗内存

图片 16

FragmentPagerAdapter_destoryItem.png

FragmentStatePagerAdapter适用于页面较多的情况下,查看源码可发现调用的是remove的方法,会进行内存回收处理。

图片 17

FragmentStatePagerAdapter_destoryItem.png

1.UI卡顿原理

60fps --> 16ms
60fps:1秒60桢,1000/16
16ms:android中每隔16ms会进行一个渲染,16ms以内完成不了就会导致界面
的卡顿

每次dalvik虚拟机进行GC的时候,所有的线程都会暂停,所以说在16ms内正在
渲染的时候正好遇到了大量的GC操作,就会导致渲染时间不够,从而导致UI卡顿.

overdraw:过度绘制,在屏幕上,它的某个像素在同一桢的时间内绘制了很多
次,经常出现在多层次的UI结构中。可以在手机中的GPU选项中进行观察,有蓝
色、淡绿、淡红、红色、深红色,减少红色尽量出现蓝色。

5.Fragment的生命周期

下面列出两张图,第一张可用于面试记忆,第二张可以详细的摸索清楚activity与
fragment生命周期的走向。

图片 18

Fragment生命周期.png

图片 19

fragment与activity生命周期关联图.png

onAttach:将fragment与activity关联
onCreate:初次创建fragment时调用,只是用来创建fragment,此时的activity并没有创建完成
onCreateView:系统在fragment首次绘制界面的时候调用
onViewCreated:fragment的界面已经绘制完毕。

Activity - onCreate:初始化activity中的布局

onActivityCreate:activity渲染绘制完毕

Activity - onStart:activity可见但不可交互操作
onStart:fragment可见但不可交互操作

Activity - onResume:activity已经在前台进行展示,可以与用户进行交互了
onResume:fragment已经在前台进行展示,可以与用户进行交互了

onPause:fragment处于一个暂停的状态,可见但是不可交互
Activity - onPause:activity处于一个暂停的状态,可见但是不可交互

onStop:fragment不可见
Activity - onStop:activity不可见

onDestoryView:与onCreateView关联,fragment即将结束会被保存
onDestory:销毁Fragment,通常按Back键退出或者Fragment被回收时调用此方法
onDetach:会将fragment从activity中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护

Activity - onDesroty:activity销毁

1.什么是ANR?

Application Not Responding

在主线程中做耗时的操作,导致长时间无法对用户的操作做出响应,Activity最长
为5秒,BrocastReceiver最长为10秒,Service最长为20秒。

1.使用
  • 绑定一个view
  • 给一个view添加点击事件
  • 给多个view添加点击事件
  • 给ListView 设置 setItemClickListener
3.总结

1.布局优化;尽量使用include、merge、ViewStub标签,尽量不存在冗余嵌套及过于复杂布局(譬如10层就会直接异常),尽量使用GONE替换INVISIBLE,使用weight后尽量将width和heigh设置为0dp减少运算,Item存在非常复杂的嵌套时考虑使用自定义Item View来取代,减少measure与layout次数等。

2.列表及Adapter优化;尽量复用getView方法中的相关View,不重复获取实例导致卡顿,列表尽量在滑动过程中不进行UI元素刷新等。

3.背景和图片等内存分配优化;尽量减少不必要的背景设置,图片尽量压缩处理显示,尽量避免频繁内存抖动等问题出现。

4.自定义View等绘图与布局优化;尽量避免在draw、measure、layout中做过于耗时及耗内存操作,尤其是draw方法中,尽量减少draw、measure、layout等执行次数。

5.避免ANR,不要在UI线程中做耗时操作,遵守ANR规避守则,譬如多次数据库操作等

来自博文

二十四、内存泄漏

2.WebView的性能优化

如果WebView是使用xml进行加载的往往在关闭Activity的时候WebView的资源并不会随着Activity的销毁而销毁。

网上推荐的方法解决大体分为2种:
一种是使用代码动态的创建WebView,对传入WebView中的Context使用弱引用,
动态添加WebView的意思是在布局创建一个ViewGroup用来放置
WebView,Activity创建时add进来,在Activity停止时remove掉

第二种是为WebView新开一个进程,不过这样的话使用起来会比较麻烦,效果还
是挺简单粗暴的,据说QQ微信也在使用这种方式:参考

六、Binder

一、Linux内核的基础知识

1.进程隔离/虚拟地址控件

在操作系统中,为了保护某些进程之间的互不干扰,设计了一个叫进程隔离的技术,就是为了避免进程A可以去操作进程B而实现的;进程的隔离实现用到了虚拟地址空间,就是说进程A的虚拟地址空间和进程B的虚拟空间是不一样的,这样,就防止了进程A的数据可以写到进程B中;所以操作系统中不同的进程之间数据是不共享的,如果需要进程间的数据通信,就需要某种进程间通信的机制来完成,在android中就是使用Binder这个机制来完成的。

不同进程间的引用叫做句柄,相同进程间的应用叫做指针,也就是对进程空间中虚拟地址的引用

2.系统调用

对内核有保护机制,对于应用程序只可以访问某些许可的资源,不许可的资源是不可以访问的。

3.Binder驱动

在android系统中,是运行在内核之中的;负责各个用户进程通过Binder通信的内核来交互的模块叫Binder驱动

二、Binder通信机制介绍

1.为什么使用Binder

1)Android使用的Linux内核拥有着非常多的跨进程通信机制
2)性能:在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进
程通信对通信机制的性能有严格的要求,Binder相对出传统的Socket方式,更加
高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共
享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。
3)安全:传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比
如Socket通信ip地址是客户端手动填入,很容易进行伪造,而Binder机制从协议
本身就支持对通信双方做身份校检,因而大大提升了安全性

2.Binder通信模型

图片 20

Binder通信图.png

客户端进程只不过是持有了我们一个服务端的一个代理

3.到底什么是Binder??
1)通常意义下,binder指的是一种通信机制
2)对于Server进程来说,Binder指的是Binder本地对象;
对于Client对象来说,Binder指的是Binder代理对象
3)对于传输过程而言,Binder是可以进行跨进程传递的对象

博文参考
博文参考
博文参考

七、Handler

1、处理过程:
从handler中获取一个消息对象,把数据封装到消息对象中,通过handler的
send…方法把消息push到MessageQueue队列中。
Looper对象会轮询MessageQueue队列,把消息对象取出。
通过dispatchMessage分发给Handler,再回调用Handler实现的handleMessage
方法处理消息

图片 21

[图片上传中...(Handler流程图.png-523e3e-1515822798479-0)]

图片 22

Handler流程图.png

2、Handler中为什么要使用ThreadLocal来获取Lopper呢?
因为在不同线程访问同一ThreadLocal时,不管是set方法还是get方法对
ThreadLocal所做的读写操作仅限与各自线程内部,这样就可以使每一个线程有
单独唯一的Lopper。
ThreadLocal博文

3、Handler引起的内存泄漏以及解决办法
原因:在java中,非静态的内部类和匿名内部类都会隐式的持有一个外部类的引
用,所以原因就是匿名内部类持有外部类的引用,导致外部Activity无法释放

解决办法:
1、最直接的思路就是避免使用非静态内部类。使用Handler的时候,放在一个新建的文件中来继承Handler或者使用静态的内部类来替代。静态内部类不会隐含的持有外部类的引用,因此这个activity也就不会出现内存泄漏问题。
2、如果你需要在Handler内部调用外部Activity的方法,你可以让这个Handler持有这个Activity的弱引用,这样便不会出现内存泄漏的问题了。
3、另外,对于匿名类Runnable,我们同样可以设置成静态的,因为静态内部类不会持有外部类的引用。
4、注意:如果使用Handler发送循环消息,最好是在Activity的OnDestroy方法中调用mLeakHandler.removeCallbacksAndMessages(null);移除消息。(这不是解决内存泄漏的方法)
博文

八、AnsycTask

1.Asynctask是什么?
它是Android提供的一个抽象类,他本质上就是一个封装了线程池和handler的异
步框架,主要是来执行异步任务的,由于它内部继承了handler,所以他可以在工
作线程和UI线程中随意切换。

注意:Asynctask能够让你避免使用线程类thread和handler直接处理后台操作,
他可以把运算好的结果交给UI 线程来显示,不过Asynctask只能做一些耗时较短
的操作,如果我们要做耗时较长的操作,我们还是尽量使用线程池。

2.Asynctask使用
它主要有三个重要参数,五个重要方法

  • 三个参数,三种泛型分别代表:
public abstract class AsyncTask<Params, Progress, Result> { }

第一个:“启动任务执行的输入参数”。
第二个:“后台任务执行的进度”。
第三个:“后台计算结果的类型”。
  • 五个方法:
第一种:execute(params…params):执行一个异步任务,需要我们在代码中调用
此方法,触发异步任务的执行(必须在UI线程中调用)

第二种:onPreExecute(),在execute(Params… params)被调用后立即执行,一
般用来在执行后台任务前对UI做一些标记(也可以理解为初始化数据)。

第三种:doInBackground(Params… params),在onPreExecute()完成后立即执
行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行
过程中可以调用publishProgress(Progress… values)来更新进度信息。

第四种:onProgressUpdate(Progress… values),在调用
publishProgress(Progress… values)时,此方法被执行,直接将进度信息更新到
UI组件上。

第五种:onPostExecute(Result result),当后台操作结束时,此方法将会被调
用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

3.Asynctask的内部原理

1、Asynctask的本质是一个静态的线程池,Asynctask派生出的子类可以显现不
同的异步任务,这些任务都是提交到静态的线程池中执行。

2、线程池中的工作线程执行doinbackground(mparams)方法来执行异步操作。

3、当任务状态改变后,工作线程会向UI线程发送消息,Asynctask内部的
internalHandler相应这些消息,并调用相关的回调。

4.Asynctask的注意事项

一、内存泄露。和handler造成的问题一样。非静态内部类持有外部类的匿名引
用,导致外部activity像被内存回收,由于非静态内部类还持有外部类的引用,导
致外部类不能被系统回收,造成内存泄露。

解决方法: 
1、谁为静态内部类。 
2、非静态内部类使用弱引用。 
3、在destroy方法中finish。

5.为什么AsyncTask只会执行一次
AsyncTask定义了一个mStatus变量,表示异步任务的运行状态,分别是PENDING、RUNNING、FINISHED,当只有PENDING状态时,AsyncTask才会执行,这样也就保证了AsyncTask只会被执行一次

二、生命周期

如果在外部类被销毁的时候,没有执行onCancelled方法,这有可能让我们的
activity在销毁之前导致崩溃。因为Asynctask正在处理某些view。但是activity不
存在了,所以就会崩溃。

三、结果丢失 
假如说,activity被系统回收或者横竖屏切换,导致activity被重新创建,而之前运
行的Asynctask会持有之前activity的引用,但是这个引用已经无效,所以这个时
候,你调用onpostExecute()方法去更新界面就会失败,因为结果已经丢失。

5.为什么AsyncTask只能执行一次?
应该说同一时间段内,只执行一次,防止对同一件事进行操作,造成的混乱。我
们知道异步的线程执行的顺序很大程度上是不确定的,有可能先执行的最后执行
完成,后执行的反而先完成。如果在同一时间段内,执行多个线程这里就会出现
混乱。

6.什么是并行,什么是串行,两者的区别是什么
在android1.6版本之前,Asynctask都是串行,也就是他会把所有任务,一串一串
的放在线程池中。但是在1.6到2.3的版本中,它改成了并行。为了维护系统的稳
定,2.3版本后又改回了串行,但是还是可以执行并行的,建议使用串行

来自博文
推荐

九、HandlerThread

1.由来
当我们需要做一个耗时操作时,自然而然会启动一个子线程去执行,当子线程执
行完成之后,就会自动关闭掉;如果过了一会我们又想执行一个耗时操作呢,那
就又得重新开启一个子线程,这样重复的来回开启是非常消耗性能的;而在
android中就为我们提供了HandlerThrea来完成这样的操作。

2.特点

  • HandlerThread本质上是一个线程类,它继承了Thread;
  • HandlerThread有自己内部的Lopper对象,可以进行looper循环;
  • HandlerThread中是不可以处理界面更新的,还是得交给Handler处理;
  • 优点是不会堵塞,减少了对性能的消耗;缺点是不能同时进行多任务
    的处理,需要等待进行处理,处理效率比较低;
  • 与线程池重并发不同,HandlerThread是一个串行队列,HandlerThread背后只
    有一个线程

推荐博文

十、intentService

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

2.IntentService使用方法
创建IntentService时,只需要实现onHandlerIntent方法和构造方法,onHandlerIntent为异步方法,可以执行耗时操作。

十一、View绘制

首先是measure(测量) 其次 layout(布局) 最后 draw(绘制)

3.广播种类

1、普通广播(Normal Broadcast):Context.sendBroadcast
2、系统广播(System Broadcast):Android中内置了多个系统广播,只要涉及到手
机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播
2、有序广播(Ordered Broadcast):Context.sendOrderedBroadcast
3、本地广播(Local Broadcast):只在app内传播

1.WebView安全漏洞

Android API level 16以及之前的版本存在远程代码执行安全漏洞,该漏洞源于程
序没有正确限制使用addJavascriptInterface方法,远程攻击者可通过使用
Java Reflection API 利用该漏洞执行任意java对象的方法

2.冷启动流程
2.广播的使用场景

1、同一app具有多个进程的不同组件之间的消息通信
2、不同app之间的组件之间消息通信

2.UI卡顿原因分析

1.人为的在UI线程中做轻微的耗时操作,导致UI线程卡顿;
2.布局Layout过于复杂,无法在16ms内完成渲染;
3.同一时间动画执行的次数过多,导致CPU或GPU负载过重;
4.View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU
负载过重;
5.View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染;
6.内存频繁触发GC过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作;
7.冗余资源及逻辑等导致加载和执行缓慢;
8.臭名昭著的ANR;

2.工作流程

他有个lint tool 工具,它会把我们的Android源代码和lint xml配置文件打包成一个文件之后,输出一个lint output ,并展现出具体哪行代码有问题,我们可以定位到具体问题所在,改写代码,保证上线时的代码质量

3.绑定view的时候,为什么不能private和static??

因为性能问题,如图我们我们把这个view设置成private,那么这个框架他就不能
通过注解的方式来获取了,只能通过反射来获取,此时不管你的性能有多快,都
会影响性能。这是必须注意并且避免的。这也就是和其他注解方式不同的一点。

来自博文

十九、Glide加载图片

Glide原理的核心是为bitmap维护一个对象池。对象池的主要目的是通过减少大对象内存的分配以重用来提高性能
glide与picasso对比
glide原理

二十、ANR

1.Android进程的优先级
  • 前台进程
  • 可见进程
  • 服务进程
  • 后台进程
    *空进程
3.处理:
  • 使用AnsycTask处理耗时IO操作
  • 使用Thread或者HandlerThread提高优先级
  • 使用Handler来处理工作线程的耗时任务
  • Activity的onCreate()和onResume()回调中尽量避免耗时代码

来自博文

二十一、OOM

3.singleTask(栈内复用模式)

是一个单例模式,当需要创建一个activity的时候,会去检测整个任务栈中是否存
在该activity实例,如果存在,会将其置于任务栈的栈顶,并将处于该activity以上
的activity都移除销毁,这时会回调一个onNewIntent()方法。

  • 补充
    <activity android:name=".MainActivity" android:launchMode="singleTask" android:taskAffinity="com.demo.singletask"/>
    默认情况下taskAffinity的值就是包名,taskAffinity相同就说明是在同一个栈中,TaskId即一样
1.什么是oom?

当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就
会抛出的out of memory 异常。

博文

二十二、bitmap

2.原理

⚠️ 首先我们应该避免一个误区,butterknife中并不是用的反射的原理来实现的,
因为用反射的话性能会很差

自己用反射实现原理:通过反射获得类中所有注解的属性,并且获得注解当中资
源中Layout的值,最后通过反射findViewById获取到这个View,并赋值给Activity
当中的属性。
很大的缺点是在运行时会大量的使用到反射,而反射其实会影响app的性能,特
别是运行时的性能,容易造成卡顿;又会产生很多的临时变量,临时变量又会引
起垃圾回收,在UI优化中,大量频繁的变量的垃圾回收会造成UI卡顿,UI卡顿也
是一种性能的标志之一。

而我们的butterknife没有使用这样的方法。butterknife使用的是java的注解处理技
术,也就是在代码编译的时候,编译成字节码的时候,它就处理好了注解操作

注意:java的注解处理技术是在编译阶段执行的,它的原理是通过读入我们的源
代码,解析注解然后生产新的java代码,而新生产的java代码当中,最后被编译
成java字节码,由于注解解释器它是不能改变读入的java类的。这就是butterknife的注解原理。

3.计算inSampleSize
4.进程优先级

可见进程:处于可见但是用户不可点击的进程

前台进程:处于前台正在与用户交互的,或者前台activity绑定的service

后台进程:前台进程点击了home键回到桌面后,这个进程就会转变为后台进
程,不会立马被kill掉,会根据内存的情况来做回收。

服务进程:在后台开启的service服务

空进程:五个进程中优先级最低,是一个没有活跃的组件,只是出于缓存的目的
而保留

二、Activity任务栈

一个用来管理Activity的容器,后进先出的栈管理模式,一个应用中可以有多个任
务栈,也可以只有一个任务栈,这得根据启动模式来决定。

三、Activity启动模式

2.LRU

LRUCache原理
LruCache中维护了一个集合LinkedHashMap,该LinkedHashMap是以访问顺序(accessOrder为true,其余非构造函数此值全为false)排序的。当调用put()方法时,就会在集合中添加元素,并调用trimToSize()判断缓存是否已满,如果满了就用LinkedHashMap的迭代器删除队尾元素,即近期最少访问的元素。当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队头。
LinkedHashMap内部是使用双向循环链表来存储数据的。也就是每一个元素都持有他上一个元素的地址和下一个元素的地址。

一、Activity

3.draw
2.概念

Android事件分发机制主要由三个重要的方法共同完成的

dispatchTouchEvent:用于进行点击事件的分发
onInterceptTouchEvent:用于进行点击事件的拦截
onTouchEvent:用于处理点击事件
这里需要注意的是View中是没有onInterceptTouchEvent()方法的

本文由新葡京8455发布,转载请注明来源

关键词: