Android学习笔记:Android基础知识点(不断更新中)

2020年9月21日 13点热度 0条评论 来源: 浩比浩比

1、Android学习笔记:OkHttp
2、Android学习笔记:更新UI的方法(UI线程和非UI线程)
3、Android学习笔记:Volley
4、Android学习笔记:Handler
5、Android学习笔记:Android-Async-Http
6、Android学习笔记:HttpClient和HttpURLConnection
7、Android学习笔记:SharedPreferences
8、Android学习笔记:AsyncTask
9、Android学习笔记:EventBus和otto
10、Android学习笔记:活动(Activity)
11、Android学习笔记:布局
12、Android学习笔记:ListView
13、Android学习笔记:Universal-Image-Loader
14、Android学习笔记:WebView
15、Android学习笔记:RecyclerView
16、Android学习笔记:ButterKnife插件
17、Java学习手册:XML
18、Android学习手册:JSON解析工具比较
19、Android学习笔记:服务(Service)
20、Android学习笔记:广播(Broadcast)
21、Android学习笔记:内容提供器(Content Provider)
22、Android学习笔记:碎片(Fragment)
23、Android学习笔记:框架模式
24、Android学习笔记:Android优化
25、Android学习笔记:线程池(ThreadPool)
26、Android学习笔记:IntentService
27、Android学习笔记:通知(Notification)
28、Android学习笔记:ANR的定位和修正
29、Android学习笔记:Material Design

0、Android的系统架构
Android系统采用分层架构,由高到低分为4层,依次是应用程序层、应用程序框架层、核心类库层、Linux内核。

  • 1、应用程序层:
    应用程序层是一个核心应用程序的集合,所有安装在手机上的APP属于这一层。
  • 2、应用程序框架层
    应用程序框架层主要提供了构建应用程序时用到的各种API。
  • 3、核心类库
    核心类库中包含了系统库及Android运行环境。
  • 4、Linux内核
    Linux内核层为Android设备的各种硬件提供了底层的驱动。

1、四大组件
(1)Android系统的四大组件分别是:活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)、内容提供器(Content Provider)。
(2)Android的四大组件中除了BroadcastReceiver以外,其他三种组件(Activity、Service、ContentProvider)都必须在AndroidManifest中注册,对于BroadcastReceiver来说,它既可以在AndroidManifest.xml中注册,也可以通过代码来注册。
(3)在调用方式上,Activity、Service、BroadcastReceiver需要借助Intent,而ContentProvider则无须借助Intent。

  • Activity
    用户可操作的可视化界面,为用户提供一个完成操作指令的窗口。一个Activity通常是一个单独的屏幕,Activity通过Intent来进行通信。Android中会维持一个Activity Stack,当一个新Activity创建时,它就会放到栈顶,这个Activity就处于运行状态。
  • Service
    服务,运行在手机后台,适合执行不需和用户交互且还需长期运行的任务。
  • Broadcast Receiver
    广播接收器,运用在应用程序间传输信息,可以使用广播接收器来让应用对一个外部事件做出响应。
  • Content Provider
    内容提供器,使一个应用程序的指定数据集提供给其他应用程序,其他应用可通过ContentResolver类从该内容提供者中获取或存入数据。它提供了一种跨进程数据共享的方式,当数据被修改后,ContentResolver接口的notifyChange函数通知那些注册监控特定URI的ContentObserver对象。
    ①如果ContentProvider和调用者在同一进程中,ContentProvider的方法(query/insert/update/delete等)和调用者在同一线程中;
    ②如果ContentProvider和调用者不在同一进程,ContentProvider方法会运行在它自身进程的一个Binder线程中。

2、定义一个应用程序名的字符串,有以下两种方式来引用它:

<resources>
	<string name="app_name">HelloWorld</string>
</resources>
  • 在代码中通过R.string.app_name可以获得该字符串的引用
  • 在XML中通过@string/app_name可以获得该字符串的引用

3、targetSdkVersion(build.gradle中)
如果设置了此属性,那么程序在执行时,如果目标设备的API版本正好等于此数值,它会告诉Android平台:此程序在此版本已经经过充分测试,没有问题。不必为此程序开启兼容性检查判断的工作了。也就是说,如果targetSdkVersion与目标设备的API版本相同时,运行效率可能会高一些。

4、日志工具Log

方法 对应级别 级别由低到高
Log.v() verbose
Log.d() debug
Log.i() info
Log.w() warn
Log.e() error

5、两个Activity之间跳转时必然会执行的是哪几个方法?
前一个Activity的onPause()方法,后一个Activity的onResume()方法。

6、配置主活动

<activity>标签内部加入<intent-filer>标签,并在标签里添加如下两句声明即可。
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>

7、Toast
(1)Toast是Android系统提供的一种非常好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一段时间后自动消失,并且不会占用任何屏幕空间。
(2)跨程序访问时不能直接使用Toast。

8、Intent
Intent是Android程序中各种组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。
使用隐式Intent需要注意:
(1)只有< action >和< category >中的内容同时能够匹配上Intent指定的action和category时,这个活动才能响应该Intent。
(2)每个Intent中只能指定一个action,但却能指定多个category。
(3)只有< data >标签中指定的内容和Intent中携带的Data完全一致时,当前活动才能够响应该Intent。
特殊情况说明:(如下)

Intent intent = new Intent(A.this, B.class);
startActivity(intent);
//跳转后加上一句finish(),当从A跳转到B的时候,再点击返回键,B会跳转到A之前的那个activity,而不会返回A
//如果A是主界面,即A之前没有activity,那么会直接返回到手机主界面
finish();

9、SingleInstance
(1)以singleInstance模式启动的Activity具有全局唯一性,即整个系统中只存在一个这样的实例。
(2)以singleInstance模式启动的Activity在整个系统中是单例,如果再启动这样的Activity时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。
(3)以singleInstance模式启动的Activity具有独占性,即它会独自占用一个任务栈,被它开启的任何Activity都会运行在其他任务栈中。
(4)被singleInstance模式的Activity开启的其他Activity,能够在新的任务栈中启动,但不一定开启新的任务栈,也可能在已有的一个任务栈中开启。

10、android:layout_gravity和android:gravity的区别
(1)android:gravity是对view控件本身来说的,是用来设置view本身的内容应该显示在view的什么位置,默认值是左侧,也可以用来设置布局中的控件位置。
(2)android:layout_gravity是相对于包含该元素的父元素来说的,设置该元素在父元素的什么位置。
例如TextView中,android:gravity表示TextView文本在TextView的什么位置,默认值是左侧;android:layout_gravity表示TextView在界面上的位置。

11、dp与sp的区别
(1)dp:一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp=1px。
(2)sp:主要用于字体显示,与刻度无关的一种像素,与dp类似。
→长度宽度的数值要使用dp作为单位放入dimens.xml文件中
→字体大小的数值要使用sp作为单位放入dimens.xml文件中
注意:使用sp作为字体大小的单位,会随着系统的字体大小而改变,而dp作为单位则不会。

12、Android工程中图片资源命名禁忌
(1)大写字母
(2)”default.png“
(3)以下划线("_")开头
(4)以数字加下划线("[0-9] _")开头

13、layout_weight属性
只有线性布局(LinearLayout)支持使用layout_weight属性,来实现按比例指定控件大小的功能。
系统会先把LinearLayout下所有控件指定的layout_weight值相加,得到一个总值。然后每个控件所占大小的比例就是用该控件的layout_weight值除以刚才算出的总值。

14、ViewGroup
所有的布局都是直接或间接继承自ViewGroup的。
View是Android中最基本的一种UI组件,它可以在屏幕上绘制一块矩形区域,并能响应这块区域的各种事件。
ViewGroup则是一种特殊的View,它可以包含很多子View和子ViewGroup,是一个用于放置控件和布局的容器。
ViewGroup的作用就是对添加进它的View组件进行布局。

15、LayoutInflater与findViewById的区别
在实际开发中,LayoutInflater这个类还是非常有用的,它的作用类似于findViewById()。不同点是:

  • LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化;
  • findViewById()是找xml布局文件下的具体widget控件(如 Button、TextView等);

16、JSON
一个最基本的JSON对象是有两个花括号:"{“和”}"。

17、JSONObject
在JSONObject这个类中获取value存在两类方法 optXXX和getXXX ,二者的区别如下:
(1)getString在值为null时会抛出异常,我们需要对该异常进行捕获处理;
(2)optString在值为null时不会抛出异常,而是返回fallback的值,因此我们无需进行异常处理;
因此,如果不想对异常进行捕获处理则使用optXXX,否则就需要对相应的异常进行捕获操作。

18、序列化(使用Intent传递对象)
序列化的作用:

  • ①永久性保存对象,保存对象的字节序列到本地文件中;
  • ②通过序列化对象在网络中传递对象;
  • ③通过序列化在进程间传递对象;

两种序列化(使用Intent传递对象)的方法:

  • 实现Serializable接口
    (1)是Java SE本身就支持的。
    (2)序列化的方法:只需让一个类实现Serializable这个接口即可。(implement Serializable)
    (3)获取传递的序列化对象:调用getSerializableExtra() 方法来获取通过参数传递过来的序列化对象。
    (4)Serializable是序列化的意思,表示将一个对象转换成可存储或可传输状态,序列化后的对象可以在网络上进行传输,也可以存储到本地。
  • 实现Parcelable接口
    (1)是Android特有的功能,效率比实现Serializable接口更高效,可用于Intent数据传递,也可用于进程间通信(IPC)。
    (2)序列化的方法:首先需要实现Parcelable接口,然后重写describeContents()和writeToParcel()两个方法。其中describeContents()方法直接返回0即可,而writeToParcel()方法中我们需要调用Parcel的writeXXX()方法,将类中的字段一一写出。注意,字符串数据就调用writeString()方法,整型数据就调用writeInt()方法,以此类推。
    (3)获取传递的序列化对象:调用getParcelableExtra() 方法来获取传递过来的对象。
    (4)除了Serializable之外,使用Parcelable也可以实现相同的效果,不过不同于将对象进行序列化,Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样就可以实现传递对象的功能了。

二者的不同:

  • Parcelable效率更高且消耗内存小,推荐使用Parcelable提高性能。
  • 尽管Serializable效率低,但Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable在外界有变化的情况下不能很好地保证数据的持续性。尽管Serializable效率低点,但此时还是建议使用Serializable。
  • Serializable使用了反射,在序列化的时候会产生大量的临时变量,从而引起频繁的GC。

总结:Android上应该尽量采用Parcelable,效率至上,效率远高于Serializable。(建议在网络传输时采用Serializable,在Android程序内使用Parcelable)

19、线程间通讯
我们知道线程是CPU调度的最小单位。在Android中主线程是不能够做耗时操作的,子线程是不能够更新UI的。而线程间通信的方式有很多,比如广播,Eventbus,接口回调,在Android中主要是使用Handler。Handler通过调用sendmessage()方法,将发送的消息Message保存到Messagequeue中,而looper对象不断的调用loop()方法,从messageueue中取出message,交给Handler处理,从而完成线程间通信。

20、Nine-Patch图片
创建过程:在Android Studio中对着任意一张png图片右击→Create 9-Patch file,即可创建Nine-Patch图片。
(1)左边线条:当图片进行纵向拉伸时,由此线条从图片左边水平位移到图片右边,所形成的区域都是可以纵向拉伸的,此区域外侧不进行拉伸,保留原来效果。
(2)上边线条:当图片进行水平拉伸时,由此线条从图片上边垂直位移到图片下边,所形成的区域都是可以横向拉伸的,此区域外侧不进行拉伸,保留原来效果。
(3)右边线条:控制图片填充内容的垂直pandding留白。
(4)下边线条:控制图片填充内容的水平pandding留白。
(5)总:上边框和左边框的区域就是图片要拉伸的区域;右边框和下边框的区域就是文字内容写在图片上的区域

21、字体
Android系统对字体的载入有一个优先级顺序,首先是西方字符,然后是一些符号字体(包括emoji字体),最后是中日韩字符。Android系统自带的只有"sana(默认)"、“serif(西方国家字母体系)”、"monospace(等宽字体)"三种字体,用户可以根据需要自行添加字体。

22、数据加密

  • 1、Base64:通常将数据转换为二进制数据。
  • 2、DES加密:由于DES数据加密算法中密钥长度较小(56位),已经不适应当今分布式开放网络对数据加密安全性的要求。
  • 3、AES加密:AES是一个迭代的、对称密钥分组的密码,AES加密数据块分组长度必须为128bit,密钥长度可以是128bit、195bit、256bit中的任意一个。
  • 4、RSA加密:RSA算法可以实现非对称加密,公钥发布供任何人使用,私钥则为自己所有,供解密之用。
  • 5、MD5(信息-摘要算法5)算法:用于确保信息传输完整一致,是计算机广泛使用的杂凑算法之一。MD5算法将数据(如汉字)运算为另一固定长度值。

注:采用DES与RSA相结合的应用,使它们的优缺点正好互补,即DES加密速度快,适合加密较长的报文,可用其加密明文;RSA加密速度慢,安全性好,应用于DES 密钥的加密,可解决DES 密钥分配的问题。目前这种RSA和DES结合的方法已成为EMAIL保密通信标准。

23、限定符(Qualifiers)

屏幕特征 限定符 描述
大小 small 提供给小屏幕设备的资源
大小 normal 提供给中等屏幕设备的资源
大小 large 提供给大屏幕设备的资源
大小 xlarge 提供给超大屏幕设备的资源
分辨率 ldpi 提供给低分辨率设备的资源(120dpi以下)
分辨率 mdpi 提供给中分辨率设备的资源(120dpi~160dpi)
分辨率 hdpi 提供给高分辨率设备的资源(160dpi~240dpi)
分辨率 xhdpi 提供给超高分辨率设备的资源(240dpi~320dpi)
分辨率 xxhdpi 提供给超超高分辨率设备的资源(320dpi~480dpi)
方向 land 提供给横屏设备资源
方向 port 提供给竖屏设备资源

24、Application类
Application类和Activity、Service一样是Android框架的一个系统组件,当Android程序启动时系统会创建一个Application对象,用来存储系统的一些信息。
Android系统自动会为每个程序运行时创建一个Application类的对象且只创建一个,所以Application可以说是单例(Singleton)模式的一个类。
通常我们是不需要指定一个Application的,系统会自动帮我们创建,如果需要创建自己的Application,则可以创建一个类使其继承Application,并实现onCreate()方法,此外在AndroidManifest.xml文件中的application标签中进行注册。

注意:不要在Android的Application对象中缓存数据。基础组件之间的数据共享可以使用Intent等机制,也可以使用SharedPreferences等数据持久化机制。

25、数据持久化
(1)瞬时数据:指那些存储在内存中,有可能因为程序关闭或其他原因导致内存被回收而丢失的数据。
(2)数据持久化:指将那些内存中的瞬时数据保存到设备中,保证即使在手机关机的情况下,这些数据仍然不会丢失。Android系统的数据持久化主要有三种方式:即文件存储、SharedPreference存储、数据库存储,此外还可以将数据存入SD卡中。

  • ①文件存储
    文件存储不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中,因此适合用于存储一些简单的文本数据或二进制数据。

    //1、将数据存储到文件
    String data = "XXX";
    //Context类提供了一个openFileOutput()方法,用于将数据存储到指定的文件中
    //该方法有两个参数,第一个参数是文件名(不可以包含路径,因为存储路径默认)
    //第二个参数是文件的操作模式,有两种模式:MODE_PRIVATE和MODE_APPEND
    //MODE_PRIVATE:默认操作模式,表示当指定同样文件名的时候,所写入的内容会覆盖原文件中的内容。
    //MODE_APPEND:表示如果文件已存在,就往文件里追加内容,不存在就创建新文件。
    FileOutputStream out = openFileOutput("data", Context.MODE_PRIVATE);
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
    writer.writer(data);
    
    //2、从文件中读取数据
    //Context类还提供了一个openFileInput()方法,用于从文件中读取数据
    //系统回到默认目录下加载文件,并返回一个FileInputStream对象,在通过Java流的方式将数据解析出来
    
  • ②SharedPreferences存储
    详见:Android学习笔记:SharedPreferences
    SharedPreferencds文件是使用XML格式来对数据进行管理的。

  • ③数据库存储-SQLite(下见72)
    SQLite是一款轻量级的关系数据库,它的运行速度非常快,占用资源很少,通常只需要几百KB的内存。此外,SQLite不仅支持标准的SQL语法,还遵循了数据库的ACID事务。
    Android的SQLite主要用于较大的数据持久化保存,以达到节省客户流量的作用。

  • ④数据库存储-LitePal
    LitePal项目主页地址:https://github.com/LitePalFramework/LitePal
    LitePal是一款开源的Android数据库框架,采用了对象关系映射(ORM)模式。1)修改LitePal数据库时,只需修改内容,然后将版本号加1即可。2)LitePal进行表管理操作时不需要模型类有任何的继承结构,但是进行CRUD(指计算机处理时的增加(Create)、读取查询(Retrieve)、更新(Update)、删除(Delete)几个单词的首字母)操作时,必须要继承自DataSupport类。3)对于LitePal,对象是否已存储就是根据调用model.isSaved()方法的结果来判断的,返回true就表示已存储,返回false就表示未存储。4)LitePal仍支持使用原生的SQL来进行查询。

26、动画(Animation)
Android中共有3种动画,分别是:Tween Animation(补间动画)、Frame Animation(帧动画)、Property Animation(属性动画)。

  • 1、Tween Animation(补间动画)
    补间动画是通过特定的对象让视图组件实现旋转、平移、缩放、改变透明等一系列动画效果,该动画不会改变原有控件的位置。根据动画效果的不同,补间动画又可以分为旋转动画、平移动画、缩放动画、透明度渐变动画4种。
  • 2、Frame Animation(帧动画)
    帧动画是通过加载一系列的图片资源,按照顺序播放排列好的图片。
  • 3、Property Animation(属性动画)
    属性动画和补间动画的使用基本没有区别,但是属性动画可以改变控件的属性

27、运行时权限
Android将所有权限归成了两类,一类是普通权限,一类是危险权限。

  • 普通权限(Normal Permission):指那些不会直接威胁到用户的安全和隐私的权限,对于这部分权限的申请,无需用户进行授权,只需在AndroidManifest.xml中简单声明即可,在安装程序时Android会自动进行授权,无需每次使用时都检查权限,而且用户不能取消以上授权。
  • 危险权限(Dangerous Permission):表示那些可能会触及用户隐私或者对设备安全性造成影响的权限,如获取设备联系人信息等,对于这部分权限申请,必须要由用户手动点击授权才可以,否则程序就无法使用相应的功能。
    Android中的所有危险权限,一共是9组24个权限。我们在进行运行时权限处理时使用的是权限名,但是用户一旦同意授权了,那么该权限所对应的权限组中所有的其他权限也会同时被授权。

Android在6.0及以上系统在使用危险权限的时候都必须进行运行时权限处理。

Android 6.0系统默认认为targetSdkVersion小于23的应用授予了所申请的所有权限,所以如果App的targetSdkVersion小于23,在运行时也不会崩溃。

28、不管是ScrollView还是NestedScrollView,它们的内部都只允许存在一个直接子布局

29、XML与JSON的区别
Java学习手册:XML
(1)XML是重量级的,JSON是轻量级的;
(2)XML在传输的过程中比较占带宽,JSON占带宽少,易于压缩;
(3)XML与JSON都用在项目交互下,XML多用作配置文件,JSON用于数据交互;
(4)XML可以通过SAX、DOM、Pull等方式解析,JSON可通过json-lib、Jackson、JsonObject、Gson、FastJson等方式解析;
(5)JSON语义较差,看起来不如XML直观;

30、视频播放方式

  • 1、使用自带的播放器
    使用Intent设置ACTION_VIEW来调用系统的播放器。这种方式播放视频,主要是指定Action为ACTION_VIEW、Data为Uri、Type为MIME类型(MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当扩展名文件被访问时,浏览器会自动使用指定应用程序来打开)。
  • 2、使用VideoView控件播放视频
    VideoView控件需要与MediaController类相结合来播放视频。
  • 3、使用MediaPlayer与SurfaceView播放视频
    可以直接从内存或者DMA等硬件接口中取得图像数据,是个非常重要的绘图容器。它可以在主线程之外的线程向屏幕绘图,这样可以避免画图任务繁重时造成主线程阻塞,从而提高了程序的反应速度。
    使用MediaPlayer与SurfaceView播放视频的步骤如下:
    (1)创建MediaPlyer对象,并让其加载指定的视频文件。
    (2)在布局文件中定义SurfaceView组件,或在程序中创建SurfaceView组件,并为SurfaceView的SurfaceHolder添加Callback监听器。
    (3)调用MediaPlayer对象的setDisplay()方法将所播放的视频图像输出到指定的SurfaceView组件。
    (4)调用MediaPlayer对象的start()、stop()、pause()方法控制视频的播放状态。

31、进程、线程与消息通信
(1)不要通过Intent在Android基础组件之间传递大数据(binder transaction缓存为1MB),可能导致OOM。
(2)在Application的业务初始化代码加入进程判断,确保只在自己需要的进程初始化。特别是后台进程减少不必要的业务初始化。
(3)新建线程时,必须通过线程池提供(AsyncTask或者ThreadPoolExecutor或者其他形式自定义的线程池),不允许在应用中自行显式创建线程。
说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。另外,创建匿名线程不便于后续的资源使用分析,对性能分析等会造成困扰。
(4)线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式可以避免资源耗尽的风险。
Executors返回的线程池对象的弊端如下:

  • FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
  • CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX__VALUE,可能会创建大量的线程,从而导致OOM。

(5)子线程中不能更新界面,更新界面必须在主线程中进行,网络操作不能在主线程中调用。
(6)不要在非UI线程中初始化ViewStub,否则会返回null。

32、定时任务
Android中的定时任务一般有两种实现方式,一种是使用Java API里提供的Timer类,一种是使用Android的Alarm机制。这两种方法在大多数情况下都能实现类似的效果,但Timer类有一个明显的短板,它并不太适用于那些需要长期在后台运行的定时任务。此外,Android手机会在长时间不操作的情况下自动让CPU进入到睡眠状态,这就有可能导致Timer中的定时任务无法正常运行。而Alarm则具有唤醒CPU的功能,它可以保证大多数情况下需要执行定时任务的时候CPU都能正常工作。
(1)Alarm机制
从Android4.4系统开始,Alarm任务的触发时间将会变得不准确,有可能会延迟一段时间后任务才能得到执行。如果想要Alarm任务的执行时间必须准确无误,可以使用AlarmManager的setExact()方法替代set()方法,就基本上可以保证任务能够准时执行了。
(2)Doze模式下的Alarm机制
在Android6.0系统中,加入了一个全新的Doze模式,如果该设备未插接电源,且屏幕关闭了一段时间之后,就会进入到Doze模式。在Doze模式下,系统对CPU、网络、Alarm等活动进行限制,从而延长了电池的使用寿命。系统不会一直处于Doze模式,而是会间歇性地退出Doze模式一小段时间,以执行同步操作、Alarm任务等。
在Doze模式下,Alarm任务将会变得不准时。但如果要求Alarm任务在Doze模式下也必须正常执行,可以调用AlarmManager的setAndAllowWhileIdle()方法或setExactAndAllowWhileIdle()方法就能让定时任务即使在Doze模式下也能正常执行。

33、AsyncTask和Handler的区别
Android学习笔记:AsyncTask
Android学习笔记:Handler
AsyncTask和Handler都能适用于简单的异步处理,相比之下AsyncTask更轻量级(代码上)。

类名 优点 缺点
AsyncTask 简单、快捷、过程可控 在使用多个异步操作并进行UI变更时,就会比较复杂
Handler 结构清晰、功能定义明确 在单个后台异步处理时,显得代码过多,结构过于复杂(相对)

34、消息推送
消息推送指从服务器端向客户端发送连接,传输一定的信息。推送是自动传送消息给用户,来减少用户在网络上的搜索时间。推送的实现方式有两种,具体描述如下:

  • 客户端使用Pull方式:客户端隔一段时间去服务器上获取一下消息,看是否有最新消息。
  • 服务器使用Push方式:当服务器有新消息时,服务器把最新信息推送到客户端上。

消息推送的实现原理:

  • 1、轮询方式(Pull)
    客户端向服务器发送询问消息,若服务器有变化,则立即同步消息。这种方式需要考虑轮询的频率,如果频率太低,则有可能导致某些消息延迟;如果频率太高,则会消耗大量网络带宽和流量。
  • 2、SMS方式(Push)
    通过拦截SMS消息、解析消息内容来了解服务器的意图并采取相应操作。
    优点:可以实现完全的实时操作。
    缺点:成本较高,比较依赖于运营商。
  • 3、持久连接方式(Push)
    客户端和服务器之间建立长久连接,这样可以实现消息的及时性和实时性。

常见的第三方消息推送SDK:友盟SDK,极光推送等。

35、View的绘制流程
每一个视图的绘制过程必须经过3个主要阶段:OnMeasure()——>OnLayout()——>OnDraw()

  • 1、测量View尺寸(onMeasure())
    测量View尺寸需要重写onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法(这两个参数分别用于确定视图的宽度和高度的规格和大小),并调用setMeasuredDimension()或者super.onMeasure()方法来设置自身的mMeasuredWidth和mMeasuredHeight值,否则,就会抛出异常。
    步骤:从顶层父View到子View递归调用measure()方法,measure()方法又回调OnMeasure()方法。
  • 2、确定View位置(onLayout())
    确定View的位置需要重写onLayout()方法,由于View的位置是由父类指定的,因此onLayout()方法是空实现,没有必要重写。在ViewGroup中,onLayout()方法用于确定所有子View的位置,各种布局的差异都在该方法中得到了体现,因此必须重写。
    步骤:从顶层父View向子View的递归调用view.layout()方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。
  • 3、绘制View(onDraw())
    绘制View需要重写onDraw()方法,onDraw()方法是每个View用于绘制自身的实现方法,通过该方法可以绘制View的背景、边框、视图、子视图等。
    步骤:ViewRoot创建一个Canvas对象,然后调用OnDraw()。六个步骤:①、绘制视图的背景;②、保存画布的图层(Layer);③、绘制View的内容;④、绘制View子视图,如果没有就不用;⑤、还原图层(Layer);⑥、绘制滚动条。
    //OnDraw()
    1、Draw the background
    2、If necessary, save the canvas' layers to prepare for fading
    3、Draw view's content
    4、Draw children
    5、If necessary, draw the fading edges and restore layers
    6、Draw decorations (scrollbars for instance)
    

36、Android中的引用

  • 1、强引用
    若内存中的对象具有强引用时,即使内存不足,宁可抛出OOM异常使程序终止,垃圾回收器也不回收它。(若内存中的对象不再有任何强引用时,则垃圾回收器开始考虑可能要对此内存进行垃圾回收。)
  • 2、软引用
    当一个对象只具有软引用时,如果内存足够,则垃圾回收器就不回收它;如果内存空间不足,则垃圾回收器就会回收该对象的内存。(如果软引用所引用的对象被垃圾回收器回收,则Java虚拟机就会把这个软引用加入到与之关联的引用队列中。)
  • 3、弱引用
    在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只有弱引用的对象,不管当前内存空间是否足够,都会回收它的内存。(由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。)
  • 4、虚引用
    主要用于跟踪对象被垃圾回收器回收的活动。(虚引用并不会决定对象的生命周期,并且必须与引用队列联(ReferenceQueue)合使用。)

37、JNI
Java JNI的本意是 Java Native Interface(Java本地接口),它是为了方便Java调用C、C++等本地代码所封装的一层接口。Java提供了JNI专门用于和本地代码交互,这样就增强了Java语言的本地交互能力。通过JNI,用户可以调用用C、C++所编写的本地代码。
(1)开发流程
①创建一个Android工程,在Java中声明native方法;
②编译Java源文件得到class文件,然后通过javah命令导出JNI的头文件;
③实现JNI方法;(JNI方法是指Java中声明的native方法,这里可以选择C++或者C来实现)
④编译so库并在Java中调用;
(2)数据类型
①JNI的数据类型包含两种:基本类型和引用类型。
②JNI中的引用类型主要有类、对象和数组。
③JNI的类型签名标识了一个特定的Java类型,这个类型既可以是类和方法,也可以是数据类型。
(3)JNI调用Java方法的流程
JNI调用Java方法的流程是先通过类名找到类,然后再根据方法名找到方法的id,最后就可以调用这个方法了。
如果是调用Java中的非静态方法,那么需要构造出类的对象后才能调用它。
JNI调用Java的过程和Java中方法的定义有很大关联,针对不同类型的Java方法,JNIEnv提供了不同的接口去调用。

38、NDK
NDK是Android所提供的一个工具集,通过NDK可以在Android中更加方便地通过JNI来访问本地代码,比如C或者C++。
(1)使用NDK有如下好处:
①提高代码的安全性。
②可以很方便地使用目前已有的C/C++开源库。
③便于平台间的移植。
④提高程序在某些特定情形下的执行效率。
(2)NDK的开发流程
①下载并配置NDK;
②创建一个Android项目,并声明所需的native方法;
③实现Android项目中所声明的native方法;
④切换到jni目录的父目录,然后通过ndk-build命令编译产生so库。

39、MotionEvent
在编写Android的自定义控件,或者判断用户手势操作时,往往需要使用MotionEvent中的getRawX()、getRawY()、getX()、getY()四个方法取得触摸点在X轴与Y轴上的距离。

  • getRawX():相对于屏幕左上角的x坐标值。
  • getRawY():相对于屏幕左上角的y坐标值。
  • getX():表示Widget相对于自身左上角的x坐标值。
  • getY():表示Widget相对于自身左上角的y坐标值。

40、Android中的线程形态

  • 1、AsyncTask
    (1)概念:AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。
    (2)原理:AysncTask封装了Thread和Handler。 内部是Handler和两个线程池实现的,Handler用于将线程切换到主线程,两个线程池一个用于任务的排队,一个用于执行任务,当AsyncTask执行execute()方法时会封装出一个FutureTask对象,将这个对象加入队列中,如果此时没有正在执行的任务,就执行它,执行完成之后继续执行队列中下一个任务,执行完成通过Handler将事件发送到主线程。AsyncTask必须在主线程初始化,因为内部的Handler是一个静态对象,在AsyncTask类加载的时候它就已经被初始化了。在Android3.0开始,execute方法串行执行任务的,一个一个来,3.0之前是并行执行的。如果要在3.0上执行并行任务,可以调用executeOnExecutor方法。
    (3)缺点:AsyncTask不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。

  • 2、HandlerThread
    (1)概念:HandlerThread继承了Thread,是一种可以使用Handler的Thread。
    (2)原理:在HandlerThread中可以创建Handler。 继承自Thread,start()方法开启线程后,会在其run()方法中会通过Looper创建消息队列并开启消息循环,这个消息队列运行在子线程中,所以可以将HandlerThread中的Looper实例传递给一个Handler,从而保证这个Handler的handleMessage()方法运行在子线程中,Android中使用HandlerThread的一个场景就是IntentService。

  • 3、IntentService
    (1)概念:IntentService是一种特殊的Service,它继承了Service并且是一个抽象类,可用于执行后台耗时任务。
    (2)原理:继承自Service,它的内部封装了HandlerThread和Handler,可以执行耗时任务,同时因为它是一个服务,优先级比普通线程高很多,所以更适合执行一些高优先级的后台任务。HandlerThread底层通过Looper消息队列实现的,所以它是顺序的执行每一个任务。可以通过Intent的方式开启IntentService,IntentService通过Handler将每一个Intent加入HandlerThread子线程中的消息队列,通过looper按顺序一个个的取出并执行,执行完成后自动结束自己,不需要开发者手动关闭。
    (3)特点:由于IntentService是服务,导致它的优先级比单纯的线程要高很多,所以IntentService比较适合执行一些高优先级的后台任务,因为它优先级高不容易被系统杀死。

41、AIDL
AIDL的全称:Android Interface Definition Language,安卓接口定义语言。

在Android中,出于安全性考虑,Android系统中的进程之间不能共享内存,每个应用都执行在自己的进程中,无法直接调用其他应用的资源。由于Android系统中的进程之间不能共享内存,所以需要提供一些机制在不同的进程之间进行数据通信。为了使应用程序之间能够彼此通信,Android提供了IPC(Inter Process Communication,进程间通信)的一种独特的实现:AIDL(Android Interface Definition Language,Android接口定义语言)。

AIDL是一种Android内部进程间通信接口的描述语言,通过它可以定义进程间的通信接口。AIDL的语法与Java的接口语法类似,在编写AIDL时需要注意以下几项:
(1)接口名与AIDL文件必须相同。
(2)客户端与服务端的AIDL接口文件所在的包名必须相同。
(3)接口和方法前不能加访问权限修饰符,如public、private、protected等,也不能用final、static。
(4)AIDL默认支持的类型包括Java基本类型(int、long、boolean、String等),以及List、Map、CharSequence,使用这些类型时不需要用import声明。
(5)对于List和Map而言,其泛型元素的类型必须是AIDL支持的类型。
(6)如果使用自定义类型作为参数或返回值,则自定义类型必须实现Parcelable接口。
(7)在AIDL文件中所有非Java基本类型参数必须加上in、out、inout标记,用于指明参数是输入参数、输出参数还是输入输出参数,Java原始类型默认的标记为in,不能为其他标记。
(8)自定义类型和AIDL即使在同一个包下,生成的其他接口类型在AIDL描述文件中也需要显示import。
(9)需要一个Service类进行配合。

42、CrashHandler
通过CarshHandler来监视应用的crash信息,给程序设置一个CrashHandler。当crash发生时,系统就会回调UncaughtExceptionHandler的uncaughtException()方法。在这个方法中我们可以获取crash信息并上传到服务器(可以先暂存在SD卡中,在合适时机通过网络将crash信息上传至服务器),通过这种方式服务端就能监控程序的运行状况了,且有助于开发人员分析crash的场景从而在后面的版本中修复此类crash。

注意:代码中被catch捕获的异常不会交给CrashHandler处理,CrashHandler只能收到那些未被捕获的异常。

43、方法数越界
(1)multidex
在Android中,整个应用的方法数不能超过65536(包括Android FrameWork、依赖的jar包以及应用本身的代码中的所有方法),否则就会出现编译错误,并且程序也无法成功地安装到手机上。Google提供了multidex方案专门用于解决这个问题,通过将一个dex文件拆分为多个dex文件来避免单个dex文件方法数越界的问题。
multidex的局限性:①应用的启动速度会降低;②使用multidex的应用无法在Android4.0以下版本的手机上运行。

(2)动态加载技术
动态加载技术(也叫插件化技术),当项目越来越庞大的时候,需要通过插件化来减轻应用的内存和CPU占用,还可以实现热插拔,即在不发布新版本的情况下更新某些模块。
动态加载可以直接加载一个dex形式的文件,将部分代码打包到一个单独的dex文件中(也可以是dex格式的jar或者apk),并在程序运行时根据需要去动态加载dex中的类,这种方式既可以解决缓解方法数越界的问题,也可以为程序提供按需加载的特性,同时还为应用按模块更新提供了可能性。

44、反编译
在Android中反编译主要通过dex2jar以及apktool来完成。
(1)使用dex2jar和jd-gui反编译apk
首先将apk解压后提取出classes.dex文件,接着通过dex2jar反编译classes.dex,然后通过jd-gui来打开反编译后的jar包。
(2)使用apktool对apk进行二次打包
使用dex2jar和jd-gui反编译apk,可以将一个dex文件反编译为Java代码,但是它们无法反编译出apk中的二进制数据资源,但是apktool可以做到这一点。此外apktool还可以用于二次打包。

45、Bitmap
(1)Bitmap在Android中指的是一张图片,而BitmapFactory类提供了四类方法:decodeFile、decodeResource、decodeStream、decodeByteArray,分别用于支持从文件系统、资源、输入流以及字节数组中加载出一个Bitmap对象。
(2)如何高效的加载Bitmap?通过BitmapFactory.Options就可以按照一定的采样率来加载缩小后的图片,将缩小后的图片在ImageView中显示,这样就会降低内存占用从而在一定程度上避免OOM,提高了Bitmap的加载效率。
(3)通过BitmapFactory.Options来缩放图片,主要用到了它的inSampleSize参数,即采样率。

46、缓存策略
缓存策略主要包含缓存的添加、获取和删除。
目前常用的一种缓存算法是LRU(最近最少使用算法),它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。采用LRU算法的缓存有两种:LruCache和DiskLruCache,LruCache用于实现内存缓存,DiskLruCache常用于存储设备缓存。
(1)LruCache
LruCache是一个泛型类,它内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,其提供了get和put方法来完成缓存的获取和添加操作,当缓存满时,LruCache会移除较早使用的换粗对象。LruCache是线程安全的。

(2)DiskLruCache
DiskLruCache用于实现存储设备缓存,即磁盘缓存,它通过将缓存写入文件系统从而实现缓存的效果。

47、ImageLoader

  • 图片的同步加载:指能够以同步的方式向调用者提供所加载的图片。
  • 图片的异步加载:ImageLoader需要自己在线程中加载图片并将图片设置给所需的ImageView。
  • 图片压缩:降低OOM概率。
  • 内存缓存
  • 磁盘缓存
  • 网络拉取

注:内存缓存和磁盘缓存是ImageLoader的核心,通过这两级缓存极大提高了程序的效率并且有效的降低了对用户所造成的流量消耗,只有当这两级缓存都不可用时才需要从网络中拉取图片。而内存缓存、磁盘缓存、网络拉取三者合起来就是三级缓存策略。

48、Binder的内存拷贝过程
相比其他的IPC通信,比如消息机制、共享内存、管道、信号量等,Binder仅需一次内存拷贝,即可让目标进程读取到更新数据,同共享内存一样相当高效,其他的IPC通信机制大多需要2次内存拷贝(见49解释)。Binder内存拷贝的原理为:进程A为Binder客户端,在IPC调用前,需将其用户空间的数据拷贝到Binder驱动的内核空间,由于进程B在打开Binder设备(/dev/binder)时,已将Binder驱动的内核空间映射到自己的进程空间,所以进程B可以直接看到Binder驱动内核空间的内容改动。

49、传统IPC机制的通信原理(2次内存拷贝)
(1)传统的通信方式
进程间通信(IPC)是指在不同进程之间传播或交换信息。

  • 1、管道(pipe):速度慢,容量有限,只有父子进程能通讯。
  • 2、命名管道(FIFO):任何进程间都能通讯,但速度慢。
  • 3、消息队列:容量受到系统限制,且需要注意第一次读的时候,需要考虑上一次没有读完数据的问题。
  • 4、信号量:不能传递复杂消息,只能用来同步。
  • 5、共享内存:能够很容易控制容量,速度快,但要保持同步。

(2)通信原理

  • 1、发送方进程通过系统调用(copy_from_user)将要发送的数据存拷贝到内核缓存区中。
  • 2、接收方开辟一段内存空间,内核通过系统调用(copy_to_user)将内核缓存区中的数据拷贝到接收方的内存缓存区。

(3)存在问题
传统IPC机制存在2个问题:

  • 1、需要进行2次数据拷贝,第1次是从发送方用户空间拷贝到内核缓存区,第2次是从内核缓存区拷贝到接收方用户空间。
  • 2、接收方进程不知道事先要分配多大的空间来接收数据,可能存在空间上的浪费。

参考资料:进程间通信

50、App启动流程(待整理!!!)
App启动时,AMS(Activity Manager Service)会检查这个应用程序所需要的进程是否存在,不存在就会请求Zygote进程启动需要的应用程序进程,Zygote进程接收到AMS请求并通过fock自身创建应用程序进程,这样应用程序进程就会获取虚拟机的实例,还会创建Binder线程池(ProcessState.startThreadPool())和消息循环(ActivityThread looper.loop),然后App进程,通过Binder IPC向sytem_server进程发起attachApplication请求;system_server进程在收到请求后,进行一系列准备工作后,再通过Binder IPC向App进程发送scheduleLaunchActivity请求;App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;主线程在收到Message后,通过反射机制创建目标Activity,并回调Activity.onCreate()等方法。到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面。

51、RecyclerView在很多方面能取代ListView,Google为什么没把ListView划上一条过时的横线?
ListView采用的是RecyclerBin的回收机制在一些轻量级的List显示时效率更高。

52、Kotlin的特点

  • 1、代码量少且代码末尾没有分号;
  • 2、空类型安全(编译期处理了各种null情况,避免执行时异常);
  • 3、函数式的,可使用lambda表达式;
  • 4、可扩展方法(可扩展任意类的的属性);
  • 5、互操作性强,可以在一个项目中使用kotlin和java两种语言混合开发;

53、文件与数据库
(1)任何时候不要硬编码文件路径,请使用Android文件系统API访问。
(注:Android应用提供内部和外部存储,分别用于存放应用自身数据以及应用产生的用户数据。可以通过相关API接口获取对应的目录,进行文件操作。)

android.os.Environment#getExtemalStorageDirectory()
android.os.Environment#getExtemalStoragePublicDirectory()
android.content.Context#getFilesDir()
android.content.Context#getCacheDir

(2)当使用外部存储时,必须检查外部存储的可用性。
(3)应用间共享文件时,不要通过放宽文件系统权限的方式去实现,而应使用FileProvider。
(4)数据库Cursor必须确保使用完后关闭,以免内存泄漏。(注:Cursor是对数据库查询结果集管理的一个类,当查询的结果集较小时,消耗内存不易察觉。但是当结果集较大,长时间重复操作会导致内存消耗过大,需要开发者在操作完成后手动关闭Cursor。数据库Cursor在创建及使用时,可能发生各种异常,无论程序是否正常结束,必须在最后确保Cursor正确关闭,以避免内存泄漏。同时,如果Cursor的使用还牵涉多线程场景,那么需要自动保证操作同步。)
(5)多线程操作写入数据库时,需要使用事务,以免出现同步问题。(注:Android通过SQLiteOpenHelper获取数据库SQLiteDatabase实例,Helper中会自动缓存已经打开的SQLiteDatabase实例,单个APP中应使用SQLiteOpenHelper的单例模式确保数据库连接唯一。由于SQLite自身是数据库级锁,单个数据库操作是保证线程安全的(不能同时写入),transaction是一次原子操作,因此处于事务中的操作是线程安全的。若同时打开多个数据库连接,并通过多线程写入数据库,会导致数据库异常,提示数据库已被锁住。)
(6)执行SQL语句时,应使用SQLiteDatabase#insert()、update()、delete()、不要使用SQLiteDatabase#execSQL(),以免SQL注入风险。
(7)如果ContentProvider管理的数据库存储在SQL数据库中,应该避免将不受信任的外部数据直接拼接在原始SQL语句中,可使用一个用于将?作为可替换参数的选择子句以及一个单独的选择参数数组,会避免SQL注入。
正例:

//使用一个可替换参数
String mSelectionClause = "var = ?";
String selectionArgs = { ""}
selectionArgs[0] = mUserInput;

反例:

//拼接用户输入内容和列名
String mSelectionClause = "var = " + mUserInput;

54、Activity、Window、View三者的区别
(1)Activity:是Android四大组件之一,负责界面展示、用户交互与业务逻辑处理。Activity构造的时候会初始化一个Window(PhoneWindow)。
(2)Window:就是负责界面展示以及交互的职能部门,就相当于Activity的下属,Activity的生命周期方法负责业务的处理。PhoneWindow有一个“ViewRoot”,这个“ViewRoot”是一个View或者说ViewGroup,是最初始的根视图。“ViewRoot”通过addView()方法来一个个的添加View,比如TextView,Button等。
(3)View:就是放在Window容器的元素,Window是View的载体,View是Window的具体展示。

三者的关系: Activity通过Window来实现视图元素的展示,Window可以理解为一个容器,盛放着一个个的View,用来执行具体的展示工作。

(上图来源于网络,侵删)

55、自定义View的一些问题(待整理!!!)
(1)getMeasuredHeight()和getHeight()方法有什么区别?
getMeasuredHeight(测量高度)形成于view的measure过程,getHeight(最终高度)形成于layout过程,在有些情况下,view需要measure多次才能确定测量宽高,在前几次的测量过程中,得出的测量宽高有可能和最终宽高不一致,但是最终来说,还是会相同。

(2)子view宽高可以超过父view?能
①android:clipChildren = “false” 这个属性要设置在父 view 上。代表其中的子View 可以超出屏幕。
②子view 要有具体的大小,一定要比父view 大才能超出。(高度可以在代码中动态赋值,但不能用wrap_content / match_partent)。
③对父布局还有要求,要求使用LinearLayout。如果必须用其他布局,可以在需要超出的view上面套一个linearLayout,外面再套其他的布局。
④最外面的布局如果设置的padding,则不能超出。

(3)自定义view需要注意的几点
①让view支持wrap_content属性,在onMeasure方法中针对AT_MOST模式做专门处理,否则wrap_content会和match_parent效果一样(继承ViewGroup也同样要在onMeasure中做这个判断处理)。
②让view支持padding(onDraw的时候,宽高减去padding值,margin由父布局控制,不需要view考虑),自定义ViewGroup需要考虑自身的padding和子view的margin造成的影响。
③在view中尽量不要使用handler,使用view本身的post方法。
④在onDetachedFromWindow中及时停止线程或动画。
⑤view带有滑动嵌套情形时,处理好滑动冲突。

(4)invalidate()和postInvalidate()的区别及使用
①View.invalidate():层层上传到父级,直到传递到ViewRootImpl后触发了scheduleTraversals(),然后整个View树开始重新按照View绘制流程进行重绘任务。在UI线程刷新view。
②View.postInvalidate():最终会调用ViewRootImpl.dispatchInvalidateDelayed()方法,它的原理就是invalidate+handler,在工作线程刷新view(底层还是handler)。

(5)如何优化自定义View
①在要在onDraw或是onLayout()中去创建对象,因为onDraw()方法可能会被频繁调用,可以在view的构造函数中进行创建对象。
②降低view的刷新频率,尽可能减少不必要的调用invalidate()方法。或是调用带四种参数不同类型的invalidate(),而不是调用无参的方法。无参变量需要刷新整个view,而带参数的方法只需刷新指定部分的view。在onDraw()方法中减少冗余代码。
③使用硬件加速,GPU硬件加速可以带来性能增加。
④状态保存与恢复,如果因内存不足,Activity置于后台被杀重启时,View应尽可能保存自己属性,可以重写onSaveInstanceState和onRestoreInstanceState方法,状态保存。

56、Service 与 Activity 之间通信的几种方式

  • 1、通过Binder
    通过BindService启动服务,可以在ServiceConnection的onServiceConnected中获取到Service的实例,这样就可以调用Service的方法,如果Service想调用Activity的方法,可以在Service中定义接口类及相应的set()方法,在Activity中实现相应的接口,这样Service就可以回调接口方法。
  • 2、通过广播

57、Android 为每个应用程序分配的内存大小是多少?
Google原生OS的默认值是16M,但是各个厂家的系统会对这个值进行修改,不同厂商的值不同。

  • 1、未设定属性android:largeheap = "true"时,可以申请到的最大内存空间。
  • 2、设定属性android:largeheap = "true"时, 可以申请的最大内存空间为原来的两倍多一些。

58、LinearLayout与RelativeLayout
性能对比:LinearLayout的性能要比RelativeLayout好。
因为RelativeLayout会测量两次。而默认情况下(没有设置weight)LinearLayout只会测量一次。
为什么RelativeLayout会测量两次?首先RelativeLayout中的子view排列方式是基于彼此依赖的关系,而这个依赖可能和布局中view的顺序无关,在确定每一个子view的位置的时候,就需要先给每一个子view排一下序。又因为RelativeLayout允许横向和纵向相互依赖,所以需要横向纵向分别进行一次排序测量。

59、View、ViewGroup事件分发(Android中Touch事件的传递机制)

  • 1、dispatchTouchEvent():该方法是用于处理事件分发的,若事件能够传递到当前View,则一定会调用该方法。View中该方法的源码如下:
	public boolean dispatchTouchEvent(Motion e){ 
		boolean result = false;
		if(onInterceptTouchEvent(e)){ 
			//如果当前View截获事件,那么事件就由当前View处理,即调用onTouchEvent()
			result = onTouchEvent(e);
		}else{ 
			//如果不截获,则交给其子View来分发
			result = child.dispatchTouchEvent(e);
		}
	}
  • 2、onInterceptTouchEvent():该方法是在dispatchTouchEvent()方法中调用,用于判断是否需要截获事件,若该方法返回为true,则调用onTouchEvent()方法并消费该事件;若返回false,则调用子View的dispatchTouchEvent()方法将事件交由子View来处理。

  • 3、onTouchEvent():该方法也是在dispatchTouchEvent()方法中调用,用于处理点击事件,包括ACTION_DOWN、ACTION_UP、ACTION_MOVE。

(1)Touch事件分发中只有两个主角:ViewGroup和View。其中ViewGroup又继承于View。
ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三个相关事件。
View包含dispatchTouchEvent、onTouchEvent两个相关事件。
(2)ViewGroup和View组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup。
(3)触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。
(4)当Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。 分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。
(5)当某个子View返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下来的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象。
(6)当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用super.dispatchTouchEvent()函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。
(7)onInterceptTouchEvent有两个作用:1、拦截Down事件的分发。2、中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。

60、ListView图片加载错乱的原理和解决方案
(1)原理

  • 行item图片显示重复,当前行item显示了之前某行item的图片
    比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中listView已经滑动到了第14行,且滑动过程中该图片加载结束,第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的view可能被第14行复用,这样我们看到的就是第14行显示了本该属于第2行的图片,造成显示重复。
  • 行item图片显示闪烁
    如果第14行图片又很快加载结束,所以我们看到第14行先显示了第2行的图片,立马又显示了自己的图片进行覆盖造成闪烁错乱。

(2)解决方法
通过上面的分析我们知道了出现错乱的原因是异步加载及对象被复用造成的,如果每次getView能给对象一个标识,在异步加载完成时比较标识与当前行item的标识是否一致,一致则显示,否则不做处理即可。

61、Activity之间的通信方式

  • 1、通过Intent方式传递参数跳转
  • 2、通过广播方式
  • 3、通过接口回调方式
  • 4、借助类的静态变量或全局变量
  • 5、借助SharedPreference或是外部存储,如数据库或本地文件

62、Activity切换的生命周期
(1)前台切换到后台,然后再回到前台,Activity生命周期回调方法。
前台切换到后台,会执行onPause()->onStop(),再回到前台,会执行onRestart()->onStart()->onResume()。

(2)弹出Dialog,生命值周期回调方法。
弹出Dialog,并不会影响Activity生命周期。

63、如何实现Fragment的滑动?
ViewPager+FragmentPagerAdapter+List< Fragment>

64、Fragment之间传递数据的方式?
(1)在相应的Fragment中编写方法,在需要回调的Fragment里获取对应的Fragment实例,调用相应的方法。
(2)采用接口回调的方式进行数据传递。
①在Fragment1中创建一个接口及接口对应的set方法;②在Fragment1中调用接口的方法;③在Fragment2中实现该接口;
(3)利用第三方开源框架EventBus。

65、Intent与PendingIntent的区别
PendingIntent是延时意图,可以看作是对Intent的包装,主要用于处理非即时Intent,供当前App与外部App调用。PendingIntent主要持有的信息是它所包装的Intent和当前的App Context,即使当前App已经不存在了,也可以通过存在于PendingIntent中的Context来执行Intent。例如,当用户点击通知栏中的消息时,跳转到App的某个页面。

Intent intent = new Intent(this, NotificationACtivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);

PendingIntent主要提供了几个静态方法用于获取PendingIntent实例,可以根据需求来选择使用getActivity()方法、getBroadcast()方法或getService()方法。这几个方法所接受的参数都是相同的,第一个参数是Context。第二个参数一般用不到,通常传入0即可。第三参数是一个Intent对象。第四个参数用于确定PendingIntent的行为,有FLAG_ONE_SHOT、FLAG_NO_CREATE、FLAG_CANCEL_CURRENT和FLAG_UPDATE_CURRENT这4种值可选,通常情况下传入0即可。

Intent与PendingIntent的区别主要有以下几点:
(1)Intent是即时启动,随所在的Activity的消失而消失,而PendingIntent用于处理非即时Intent。
(2)Intent在程序结束后终止,而PendingIntent在程序结束后依然有效。
(3)Intent需要在某个Context内运行,而PendingIntent自带Context。
(4)Intent在原Task中运行,而PendingIntent在新的Task中运行。
(5)Intent一般用于Activity、Service、BroadcastReceiver之间传递数据,而PendingIntent一般用于消息通知上,可以理解为延迟执行Intent。

66、Android的安全问题
①错误导出组件
② 参数校验不严
③WebView引入各种安全问题,webview中的js注入
④不混淆、不防二次打包
⑤明文存储关键信息
⑦ 错误使用HTTPS
⑧山寨加密方法
⑨滥用权限、内存泄露、使用debug签名

67、Context
Context是包含上下文信息(外部值) 的一个参数。Android 中的 Context 分三种:Application Context、Activity Context、Service Context。它描述的是一个应用程序环境的信息,通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作,例如:启动一个Activity,发送广播,接受Intent信息等。
一个应用Context的数量=Activity数量+Service数量+1(Application数量)
注:Application 和 Activity 的 Context 对象的区别
①Application Context是伴随应用生命周期;
②Activity Context指生命周期只与当前Activity有关,即凡是跟UI相关的,都得用Activity做为Context来处理。

68、如何缩减APK包大小?
(1)代码
①保持良好的编程习惯,不要重复或者不用的代码,谨慎添加libs,移除使用不到的libs。②使用proguard混淆代码,它会对不用的代码做优化,并且混淆后也能够减少安装包的大小。③native code的部分,大多数情况下只需要支持armabi与x86的架构即可。如果非必须,可以考虑拿掉x86的部分。
(2)资源
①使用工具查找没有使用到的资源,去除不使用的图片,String,XML等。②生成APK的时候,aapt工具本身会对png做优化,但是在此之前还可以使用其他工具如tinypng对图片进行进一步的压缩预处理。③jpeg还是png,根据需要做选择,在某些时候jpeg可以减少图片的体积。对于.9.png的图片,可拉伸区域尽量切小,另外可以通过使用.9.png拉伸达到大图效果的时候尽量不要使用整张大图。
(3)策略
①有选择性的提供hdpi,xhdpi,xxhdpi的图片资源。建议优先提供xhdpi的图片,对于mdpi,ldpi与xxxhdpi根据需要提供有差异的部分即可。②尽可能的重用已有的图片资源。例如对称的图片,只需要提供一张,另外一张图片可以通过代码旋转的方式实现。③能用代码绘制实现的功能,尽量不要使用大量的图片。例如减少使用多张图片组成animate-list的AnimationDrawable,这种方式提供了多张图片很占空间。

69、Android中进程间通信有哪些实现方式?
Intent、Binder(AIDL)、Messenger、BroadcastReceiver

70、Android多线程的实现方式有哪些?
Thread & AsyncTask
(1)Thread 可以与Loop 和 Handler 共用建立消息处理队列。
(2)AsyncTask 可以作为线程池并行处理多任务。

71、多进程
一般情况下,一个应用程序就是一个进程,这个进程名称就是应用程序包名。我们知道进程是系统分配资源和调度的基本单位,所以每个进程都有自己独立的资源和内存空间,别的进程是不能任意访问其他进程的内存和资源的。
那如何让自己的应用拥有多个进程?四大组件在AndroidManifest文件中注册的时候,有个属性是android:process,这里可以指定组件的所处的进程。默认就是应用的主进程。指定为别的进程之后,系统在启动这个组件的时候,就先创建(如果还没创建的话)这个进程,然后再创建该组件。
(1)优点
①分担主进程的内存压力。将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。
②典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。
③防止主进程被杀,守护进程和主进程之间相互监视,有一方被杀就重新启动它。
(2)缺点
①占用过多的系统空间,容易占满系统内存而导致卡顿。
②消耗用户的电量。应用程序架构会变复杂,因为要处理多进程之间的通信。

72、SQLite数据库
Android中的SQLite数据库主要用于较大的数据持久化保存,以达到节省客户流量的目的。SQLite中的增删改查的方法可以通过SQL语句来执行。

SQL数据库中的表与表之间的关系有多种,如一对一、一对多、多对多。如下:

  • 1、一对一
    在一对一关系中,A表中的一行最多只能匹配于B表中的一行,反之亦然。如果相关列都是主键或都具有唯一约束,则可以创建一对一关系。
  • 2、一对多
    在这种关系中,A表中的一行可以匹配B表中的多行,但是B表中的一行只能匹配A表中的一行,只有当一个相关列是一个主键或具有唯一约束时,才能创建一对多关系。
  • 3、多对多
    在多对多关系中,A表中的一行可以匹配B表中的多行,反之亦然。要创建这种关系,需要定义第三个表,称为结合表,它的主键由A表和B表的外部键组成。

(未完待续。。。)

73、如何实现进程保活
(1)Service设置成START_STICKY,kill后会被重启(等待5秒左右),重传Intent,保持与重启前一样。
(2)通过 startForeground将进程设置为前台进程, 做前台服务,优先级和前台应用一个级别,除非在系统内存非常缺,否则此进程不会被 kill。
(3)双进程Service。让2个进程互相保护对方,其中一个Service被清理后,另外没被清理的进程可以立即重启进程。
(4)用C编写守护进程(即子进程)。Android系统中当前进程(Process)fork出来的子进程,被系统认为是两个不同的进程。当父进程被杀死的时候,子进程仍然可以存活,并不受影响(Android5.0以上的版本不可行)联系厂商,加入白名单。
(5)锁屏状态下,开启一个一像素Activity。

74、Bitmap、Drawable与动画
(1)加载大图片或者一次性加载多张图片,应该在异步线程中进行。图片的加载,涉及到IO操作,以及CPU密集操作,很可能引起卡顿。
(2)在ListView、ViewPager、RecyclerView、GridView等组件中使用图片时,应做好图谱的缓存,避免始终持有图片导致内存泄漏,也避免重复创建图片,引起性能问题。建议使用Fresco、Glide等图片库。
(3)png图片使用tinypng或者类似工具压缩处理,减少包体积。
(4)使用完毕的图片,应该及时回收。
(5)在Activity.onPause()或Activity.onStop()回调中,关闭当前activity正在执行的动画。

75、三级缓存原理
当Android端需要获得数据时比如获取网络中的图片,首先从内存中查找(按键查找),内存中没有的再从磁盘文件或sqlite中去查找,若磁盘中也没有才通过网络获取。

76、LruCache底层实现原理(待整理!!!)
LruCache中Lru算法的实现就是通过LinkedHashMap来实现的。LinkedHashMap继承于HashMap,它使用了一个双向链表来存储Map中的Entry顺序关系,对于get、put、remove等操作,LinkedHashMap除了要做HashMap做的事情,还做些调整Entry顺序链表的工作。
LruCache中将LinkedHashMap的顺序设置为LRU顺序来实现LRU缓存,每次调用get(也就是从内存缓存中取图片),则将该对象移到链表的尾端。调用put插入新的对象也是存储在链表尾端,这样当内存缓存达到设定的最大值时,将链表头部的对象(近期最少用到的)移除。
注:LruCache默认缓存大小——4MB

77、Handler、Thread和HandlerThread的区别(待整理!!!)
(1)Handler线程的消息通讯的桥梁,主要用来发送消息及处理消息。
(2)Thread普通线程,如果需要有自己的消息队列,需要调用Looper.prepare()创建Looper实例,调用loop()去循环消息。
(3)HandlerThread是一个带有Looper的线程,在HandleThread的run()方法中调用了Looper.prepare()创建了Looper实例,并调用Looper.loop()开启了Loop循环,循环从消息队列中获取消息并交由Handler处理。利用该线程的Looper创建Handler实例,此Handler的handleMessage()方法是运行在子线程中的。即Handler利用哪个线程的Looper创建的实例,它就和相应的线程绑定到一起,处理该线程上的消息,它的handleMessage()方法就是在那个线程中运行的,无参构造默认是主线程。HandlerThread提供了quit()/quitSafely()方法退出HandlerThread的消息循环,它们分别调用Looper的quit和quitSafely方法,quit会将消息队列中的所有消息移除,而quitSafely会将消息队列所有延迟消息移除,非延迟消息派发出去让Handler去处理。
HandlerThread适合处理本地IO读写操作(读写数据库或文件),因为本地IO操作耗时不长,对于单线程+异步队列不会产生较大阻塞,而网络操作相对比较耗时,容易阻塞后面的请求,因此HandlerThread不适合加入网络操作。

78、内存泄漏和内存溢出区别
(1)内存泄漏
指程序中已动态分配的堆内存由于某种原因未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统奔溃等严重后果。①一次内存泄漏似乎不会有大的影响,但内存泄漏后堆积的结果就是内存溢出。②内存泄漏具有隐蔽性,积累性的特征,比其他内存非法访问错误更难检测。这是因为内存泄漏产生的原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。此外,内存泄漏不会直接产生可观察的错误,而是逐渐积累,降低系统的整体性性能。
(2)内存溢出
指程序在申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,就会导致内存不够用,报错OOM,即出现内存溢出的错误。

79、Android各个版本API的区别
(1)android3.0,代号Honeycomb,引入Fragments、 ActionBar、属性动画、硬件加速;
(2)android4.0,代号I,API14,截图功能、人脸识别、虚拟按键、3D优化驱动;
(3)android5.0,代号L,API21,调整桌面图标及部件透明度等;
(4)android6.0,代号M,API23,软件权限管理、安卓支付、指纹支持、App关联;
(5)android7.0,代号N,API24,多窗口支持(不影响Activity生命周期),增加了JIT编译器,引入了新的应用签名方案APK Signature Scheme v2(缩短应用安装时间和更多未授权APK文件更改保护),严格了权限访问;
(6)android8.0,代号O ,API26,取消静态广播注册,限制后台进程调用手机资源,桌面图标自适应;
(7)android9.0,代号P,API27,加强电池管理,系统界面添加了Home虚拟键,提供人工智能API,支持免打扰模式;

80、HttpUrlConnection 和 OkHttp的关系
两者都可以用来实现网络请求,Android4.4之后的HttpUrlConnection的实现是基于OkHttp的。

81、描述一次网络请求的流程
(1)域名解析
(2)TCP三次握手
(3)建立TCP连接后发起HTTP请求
客户端按照指定的格式开始向服务端发送HTTP请求,HTTP请求格式由四部分组成,分别是请求行、请求头、空行、消息体,服务端接收到请求后,解析HTTP请求,处理完成逻辑,最后返回一个具有标准格式的HTTP响应给客户端。
(4)服务器响应HTTP请求
服务器接收处理完请求后返回一个HTTP响应消息给客户端,HTTP响应信息格式包括:状态行、响应头、空行、消息体。
(5)浏览器解析HTML代码,请求HTML代码中的资源
浏览器拿到html文件后,就开始解析其中的HTML代码,遇到js/css/image等静态资源时,向服务器发起一个HTTP请求,如果返回304状态码,浏览器会直接读取本地的缓存文件。否则开启线程向服务器请求下载。
(6)浏览器对页面进行渲染并呈现给用户
(7)TCP的四次挥手

82、Interpolator动画篡改器(插值器)
Interpolator接口被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果accelerate(加速)、decelerated(减速)、repeated(重复)、bounced(弹跳)等。

附:与Interpolator相关的类

类名 说明
AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间时加速
AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
AnticipateInterpolator 开始时向后,然后向前甩
AnticipateOvershootInterpolator 开始时向后,然后向前甩一定值后返回最后的值
BounceInterpolator 动画结束时弹起
CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
DecelerateInterpolator 在动画开始的地方快,然后慢
LinearInterpolator 以常量速率改变
OvershootInterpolator 向前甩一定值后再回到原来位置

83、事件驱动程序设计模型
事件驱动模型典型的应用是在GUI系统中,GUI系统中的应用程序要在不确定的时间里响应用户的UI事件。如果是传统的编程模型,应用程序必须有个线程去轮询GUI系统以便及时发现并处理消息,但这是非常浪费CPU资源的。

而事件驱动程序设计模型解决了这个问题,事件驱动程序模型有以下特点:

  • (1)有一个消息队列,存放消息。
  • (2)有一个轮询器,不停轮询消息队列,如果没有消息,那么轮询器就会休眠。
  • (3)有消息实体,而且每个消息都与一个处理消息的Handler绑定。Handler生命周期都很短。

这样的话,在没有消息时CPU就不空转,而有了消息之后再唤醒该线程。这样还有一个好处,就是这个线程不仅可以处理UI的事件,其他的事件同样可以发到该消息队列中。

84、如何捕获全局异常?
在Android项目开发时,如果程序出错了,会弹出一个强制退出的对话框,用户体验非常差。虽然我们在发布程序时总会经过仔细的测试,但是难免会碰到预料不到的错误。全局异常捕获可以帮助开发者及时获取在目标设备上导致崩溃的相关信息,这对于下一个版本的Bug修复帮助极大,而且开发者还可以在全局异常捕获时做某些处理,如在发生异常时重启App,提升用户体验。

进行全局异常捕获需要以下几个步骤:
(1)创建一个CrashHandler类,实现UncaughtExceptionHandler接口中的uncaughtException()方法。
(2)在Application中将CrashHandler类注册为默认的全局异常捕获器。
(3)捕获到全局异常后,可以在uncaughtException()方法中进行一些处理来帮助开发者定位问题,提升用户体验。例如,在捕获到异常后重启App、收集用户的相关信息、上传到服务器等。

85、OAuth2认证机制
OAuth是Open Authorization的简写,是一种得到第三方服务提供商的授权,并取得相应用户在第三方服务器上的数据的授权方式。

OAuth协议为用户资源的授权提供了一个安全、开放而又简易的标准。允许第三方网站在用户授权的前提下访问用户在服务商那里存储的各种信息。这种授权无须将用户名和密码提供给第三方网站,而是让用户提供一个令牌给第三方网站,一个令牌对应一个特定的第三方网站,同时该令牌只能在特定的时间内访问特定的资源。

OAuth2.0是OAuth协议的升级版本,不兼容OAuth1.0。OAuth认证和授权流程中涉及的三方包括服务商(用户使用服务的提供方)、用户(服务商的用户)和第三方(通常是网站或者App)。

OAuth认证和授权的过程:

  • 1、用户访问第三方网站网站,想对用户存放在服务商的某些资源进行操作。
  • 2、第三方网站向服务商请求一个临时令牌。
  • 3、服务商验证第三方网站的身份后,授予一个临时令牌。
  • 4、第三方网站获得临时令牌后,将用户导向至服务商的授权页面请求用户授权,然后这个过程中将临时令牌和第三方网站的返回地址发送给服务商。
  • 5、用户在服务商的授权页面上输入自己的用户名和密码,授权第三方网站访问所相应的资源。
  • 6、授权成功后,服务商将用户导向第三方网站的返回地址。
  • 7、第三方网站根据临时令牌从服务商那里获取访问令牌。
  • 8、服务商根据令牌和用户的授权情况,授予第三方网站访问令牌。
  • 9、第三方网站使用获取到的访问令牌访问存放在服务商的对应的用户资源。

在Android开发过程中,接入微博、微信的第三方登录,获取对应微博、微信用户的某些信息等都用到OAuth认证机制。

86、RxJava框架
RxJava是一个响应式的编程框架,也是ReactiveX在Java上的开源实现。ReactiveX是一个专注于异步编程与控制可观察数据(或者事件)流的API。RxJava可以轻松处理不同运行环境下的后台线程或UI线程任务的框架。RxJava的异步是通过一种扩展的观察者模式来实现。

RxJava的主要类有Observable(观察者)和Subscriber(订阅者)。其中Observable是一个发出数据流或事件的类,一个Observable的标准流会发出一个或多个Item。Subscriber是一个对Observable发出的Items(数据流或者事件)进行处理(采取行动)的类。

Observable与Observer之间是通过subscribe()方法实现订阅关系的。一个Observable可以有多个Subscribers,并且通过Observable发出的每一个Item将会被发送到Subscriber.onNext()方法中进行处理。一旦Observable不再发出Items,它将会调用Subscriber.onCompleted()方法。或如果有一个出错的话Observable会调用Subscribe.onError()方法。Observable与Observer之间的执行过程中有onNext()、onCompleted()、onError()3个方法,这三个方法具体如下:

  • 1、onNext()
    RxJava的事件回调方法,针对普通事件。
  • 2、onCompleted()
    事件队列完结。RxJava不仅把每个事件单独处理,还会把它们看作一个队列。RxJava规定,当不会再有新的onNext()方法发出时,则需要出发onCompleted()方法作为标志。
  • 3、onError()
    事件队列异常。在事件处理过程中出现异常时,onError()会被触发,同时队列自动终止,不允许再有事件发出。

在一个正确运行的事件序列中,onCompleted()和onError()方法有且只有一个,并且是事件序列中的最后一个。需要注意的是onCompleted()和onError()方法二者也是互斥的,即只能在队列中调用其中一个。

87、Android测试
Android中常见的测试分为单元测试和Monkey测试。

  • 1、JUnit单元测试
    (1)JUnit实际上是Android自带的测试工具,它是Android SDK 1.5加入的自动化测试功能,需要通过配置清单文件和创建测试类的方式来实现。针对项目中的接口、数据库、工具类进行测试,提高代码质量。JUnit单元测试既可以嵌入到项目中,也可以作为一个单独的项目。
    (2)程序出现错误时,JUnit窗口中的条目是红色,并且还会显示测试方法运行的时间。此时点击出错的方法,会将错误定位到源代码中的某行代码,这样可以清楚地看到是哪一行代码出错,对修复Bug有很大帮助。
    (3)Junit单元测试不需要关注控制层,当业务逻辑写好之后就可以进行单独测试,确保没有Bug之后由控制层直接调用即可,应用简单、方便,并且可以加快程序的开发速度。

  • 2、Monkey测试
    Monkey是Android中的自动化测试工具,当Monkey程序在模拟器或真实设备运行时,程序会产生一定数量或一定时间内的随机模拟用户操作的事件,如点击、按键、手势等,以及一些系统级别的事件,通常也称随机测试或者稳定性测试。通过多次并且不同设定下的Monkey测试才算是一个稳定性足够的程序。
    (1)Monkey的特征:测试的对象仅为应用程序包,有一定的局限性,该测试使用的事件流、数据流是随机的,不能进行自定义,可对MonkeyTest的对象、事件数量、类型、频率等进行设置。
    (2)Monkey的基本语法如下:

    $ adb shell monkey[options]
    

    如果不指定options,Monkey将以无反馈模式启动,并把事件任意发送到安装在目标环境中的全部包。
    (3)Monkey Test 执行过程中在下列3种情况下会自动停止:
    ①如果限定了Monkey运行在一个或几个特定的包上,那么它会监测试图转到其他包的操作,并对其进行阻止。
    ②如果应用程序崩溃或收到任何失控异常,Monkey将停止并报错。
    ③如果应用程序产生了ANR异常,Monkey将会停止并报错。

88、REST
REST(REpresentational State Transfer,表述性状态转移)是Roy Thomas Fielding博士在他的博士论文中提出的一种软件架构风格。REST从资源的角度来观察整个网络,分布在各处的资源由URI确定,对资源的操作包括获取、创建、修改和删除资源,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法,REST通过操作资源的表现形式来操作资源。

资源的表现形式有XML、HTML、JSON文件等。

REST的优点如下:

  • (1)可以利用缓存Cache来提高响应速度。
  • (2)通信本身的无状态性可以让不同的服务器处理一系列请求中的不同请求,提高服务器的扩展性。
  • (3)浏览器即可作为客户端,简化软件需求。
  • (4)相对于其他叠加在HTTP协议之上的机制,REST的软件依赖性更小。
  • (5)不需要额外的资源发现机制。
  • (6)在软件技术演进中的长期兼容性更好。

REST的缺点如下:

  • (1)在复杂的应用中,构造的URL会很长,影响对URL的理解。
  • (2)REST不能支持事务。
  • (3)在安全应用中,REST方式先天不足,需要后期策略补救。
  • (4)由于REST是一种架构风格,不是一个标准,加上每个人理解的差异,造成REST不能很好地统一,规范较困难。

89、RESTful Web Service
RESTful Web Service(又称RESTful Web API)是一个使用HTTP并符合REST原则的Web服务。通过URL在表单中指定method="GET|POST"来发出请求,如下:

资源 资源说明 GET PUT POST DELETE
http://www.haobi.com/Products Products是一组资源组合 列出该资源集合中每个资源的详细信息 更新当前整组资源 新增或附加一个新资源 删除整组资源
http://www.haobi.com/Products/1 Products/1是单个资源 取得指定资源的详细信息 更新或新增指定的资源 新增或附加一个新元素 删除单个元素

以上表格与数据库中的操作特别类似,在RESTful中每个资源都有自己独立的URI,客户端从资源集合或单个资源开始进入,不管是资源集合或单个资源,我们都能与HTTP方法配合使用。

90、URI与资源的关系
(1)URI既是资源的名称也是资源的地址。
(2)一个资源至少有一个URI,而一个URI只能指示一个资源。
(3)同一资源具有多个URI,虽然能让引用变得更加容易,但是将产生“稀释效应”,客户端无法自动验证它们是指向同一个资源的。

91、屏幕适配
(1)套图适配
目前来说,套图适配是针对图片适配的最佳方式,可以防止图片的失真以及变形,但针对不同手机进行不同的套图适配,会对UI人员施加过大的工作压力,以及对App本身也会造成冗余的影响,因为图片资源会使App变得臃肿。
优点:完美适配、不会失真。
缺点:不易达到,App臃肿。

(2)9patch适配
9patch图片作为特殊的png图片,可以在特定的情况下对不同机型进行适配,而达到图片不失真的情况。
优点:省精力、省时间、省内存、减少代码量。
缺点:不能完全适合所有图片的适配。

(3)布局适配

  • 1、使用权重适配,在布局比较有规律的页面中,我们可以多用权重,少用具体的dp值。在布局嵌套较多与较复杂的界面中,权重适配的效果不太大。
  • 2、尽量使用线性布局、相对布局和帧布局,由于绝对布局适配性极差,因此很少使用。
  • 3、对于纯色背景,尽量使用Android的shape自定义。
  • 4、开发中多使用match_parent,少使用fill_parent,避免日后淘汰。

92、屏幕的尺寸、屏幕分辨率和屏幕像素密度
(1)屏幕的尺寸
屏幕尺寸是指屏幕对角线的长度,单位是英寸。以手机为例,目前常见的屏幕尺寸有5.0、5.5等。

(2)屏幕分辨率
屏幕分辨率是指某一单位上显示的像素点数,单位是px,1px=1个像素点。一般以纵向像素x横向像素,如1960x1080。

(3)屏幕像素密度
屏幕像素密度是指每英寸上的像素点个数,单位是dpi,即“dot per inch”的缩写。

(4)总结
屏幕像素密度、屏幕尺寸、屏幕分辨率之间有一定的关联,在单一变化条件下,屏幕尺寸越小,分辨率越高,像素密度越大反之越小。

注:与上述属性相关的一些单位有px、dp、dip、dpi等。

  • px:即像素,一个像素则表明在屏幕上的一个点,一个显示单位。
  • dp:dp和dip是一个意思,是Density independent pixel的缩写,全称是密度无关像素,在Android中规定与160dpi为基准,1dip=1px,如果密度是320dpi,则1dip=2px,以此类推。
  • dpi:是dots per inch的缩写,意思是每英寸像素数,表示对角线的像素值,以WVGA(800x480)分辨率、3.7英寸的密度为例,dpi=933/3.7=252,即此时机型的dpi为252。

93、数字签名有几种模式?
Android中的数据签名是由程序开发者完成的,并不需要权威的数字证书签名机构认证,它只是用来让应用程序包自我认证的。

在Android系统中,要求每一个应用程序都必须要有数字签名才能安装到系统中。也就是说,如果一个Android应用程序没有数字签名,是没有办法安装到系统中的。通过数字签名来标识应用程序作者与应用程序之间建立的信任关系。Android系统不会安装运行任何一款未经数字签名的APK程序,无论是在模拟器上还是在实际的物理设备上,但我们在日常的开发过程中,没有任何签名的操作,程序却可以在模拟机和真机上运行。实际上,这种运行是在调试模式的基础上完成的。

APK程序有两种签名的方式:一种是调试模式(debug mode),一种是发布模式(release mode)。

  • 1、调试模式
    在调试模式下,ADT会自动使用Debug密钥为应用程序签名,因此我们可以直接运行程序。但是Debug签名存在两个风险:
    (1)Debug签名的应用程序不能在Android Market上销售,它会强制你使用自己的签名。
    (2)debug.keystore在不同的机器上所生成的可能都不一样,就意味着如果你换了机器进行APK版本升级,将会出现程序不能覆盖安装的问题。如果开发的程序只有自己使用,卸载再安装即可。但如果软件有很多用户使用,这就相当于程序不具备升级功能,所以一定要有自己的数字证书来签名。
  • 2、发布模式
    要发布程序时,开发者就需要使用自己的数字证书给APK包签名。使用自己的数字证书给APK签名的方法有两种:
    (1)通过DOS命令来对APK签名。
    (2)使用ADT Export Wizard进行签名。
    需要注意的是,数字签名是有有效期的,Android只是在应用程序安装时才会检查证书的有效期。如果程序已经安装在系统中,即使证书过期也不会影响程序的正常功能。

94、使用相同的数字有哪些好处?

  • 1、有利于程序升级
    当新版程序和旧版程序的数字证书相同时,Android系统才会认为这两个程序是同一个程序的不同版本。如果新版程序和旧版程序的数字证书不相同,则Android系统认为他们是不同的程序,并产生冲突,会要求新程序更改包名。

  • 2、有利于程序的模块化设计和开发
    Android系统允许拥有同一个数字签名的程序运行在一个进程中,Android程序会将他们视为同一个程序。所以开发者可以将自己的程序分模块开发,而用户只需要在需要时下载适当的模块即可。

  • 3、可以通过权限的方式在多个程序间共享数据和代码
    Android提供了基于数字证书的权限赋予机制,应用程序可以和其他程序共享该功能或者数据给那些与自己拥有相同数字证书的程序。如果某个权限(permission)的protectionLevel是signature,则这个权限就只能授予那些跟该权限所在的包拥有同一个数字证书的程序。

95、代码混淆
混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前的代码完成相同的功能,而混淆后的代码很难被反编译,即使反编译成功也很难得出程序的真正语义。被混淆过得程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样,只是混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,在缺乏相应的函数名和程序注释的情况下,即使被反编译,也将难以阅读。同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢失,这些信息的丢失使程序变得更加难以理解。

混淆的作用不仅仅是保护代码,它也有精简编译后程序大小的作用。由于以上介绍的缩短变量和函数名以及丢失部分信息的原因,编译后jar文件体积大约能减少25%。

96、如何进行代码混淆?
Java是一种跨平台、编译-解释型语言,Java源代码编译成中间字节码存储于class文件中。由于跨平台的需要,Java字节码中包含了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量和方法,这些符号带有许多语义信息,很容易被反编译成Java源码。为了防止这种现象,我们可以使用Java混淆器对Java字节码进行混淆。

使用Java混淆器对Java字节码进行混淆具体步骤如下:

  • (1)在Android项目的根目录下创建一个proguard.cfg文件,用来配置混淆选项。
  • (2)在Android.mk中每个package类型的LOCAL_MODULE中的LOCAL_PACKAGE_NAME下面添加两行代码:
# 指定当前的应用程序打开混淆
LOCAL_PROGUARD_ENABLED:=full

# 指定混淆配置文件
LOCAL_PROGUARD_FLAG_FILES:=proguard.cfg
  • (3)编译时设置环境变量使用 . . /setenv.sh -bv user。
  • (4)如果在项目中使用了第三方的SDK,则在混淆代码时需要按照第三方SDK的要求来做。

需要注意的是,在实际工作中也可以使用第三方的混淆。

97、简述不同SDK版本的两种代码混淆方式
根据SDK的版本不同有两种不同的代码混淆方式,在高版本的SDK下混淆的原理和参数也与低版本的相差无几,只是在不同SDK版本的环境下引入混淆脚本的方式有所不同:

(1)低版本下,项目中同时包含proguard.cfg和project.properties文件,只需要在project.properties文件末尾添加proguard.comfig=proguard.cfg再将项目Export即可。

(2)高版本SDK下,项目中同时包含proguard-project.txt和project.properties文件,这时需要在proguard-project.txt文件中进行信息配置,然后再将项目Export即可。

98、如何实现APK“瘦身”?

APK瘦身的方式有以下几种:

  • 1、开启混淆,删除无用的Java文件。开启minifyEnabled(开启混淆,删除无用的Java文件),可减小项目中APK文件的大小,具体代码如下:
buildTypes{ 
	debug{ 
		minifyEnabled true
		proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
	}
}
  • 2、去除无用资源,同时去除工程中临时展示的图片。开启shrinkResources(去除无用资源),同时去除工程中临时展示的图片可减少APK文件的大小,具体代码如下:
debug{ 
	minifyEnabled true
	shrinkResources true
	proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard.cfg'
}
  • 3、删除无用的语言资源。删除无用的语言资源可减少APK文件的大小,具体代码如下:
defaultConfig{ 
	...
	resConfigs "zh"
	...
}
  • 4、使用TinyPNG有损压缩。TinyPNG是一种只能有损压缩技术(通过降低图片中的颜色数量,来减少存储图片所需要的数据)来降低PNG图片的大小。这样的压缩对图片的效果影响很小,但是可大大降低图片的大小,并且还能保持PNG的透明度。

  • 5、PNG图片换成JPG图片。对于非透明大图,JPG比PNG图片的大小有显著的优势,在启动页、活动页等之类的大图展示区采用JPG是非常明智的选择,这样可减少APK文件的大小。

  • 6、使用webp格式。从Android4.0+开始原生支持,但是不支持包含透明度的webp,直到Android4.2.1+才支持显示含透明度的webp,使用时要特别注意使用格式工厂进行转换。

  • 7、删除或者替换兼容包(v4、v7、v13)中无用的一些图。可以使用1 x 1像素的图片来替换v4、v7、v13中的一些图片资源。

  • 8、注意删除第三方库中使用的大图。

  • 9、so库的删除。建议只保留armeabi、x86文件夹下的so文件。

  • 10、通过v4包中的Drawable Compat,通过着色方案完成selector效果。具体代码如下:

Drawable icon = getResources().getDrawable(drawableId)
Drawable tintIcon = DeawableCompat.wrap(icon);
//着色一个selector
DrawableCompat.setTintList(tintIcon, getResources().getColorStateList(R.color.xx));
//着色一个颜色
DrawableCompat.setTintList(tintIcon, ColorStateList.valueOf(Color.BLUE));
imageView.setImageDrawable(tintIcon);
  • 11、使用shape文件替换图片。具体代码如下:
<?xml version="1.0" encoding="utf-8"?>
	<shape
		xmlns:android="http://schemas.android.com/apk/res/android"
		android:shape="rectangle">
		<corners android:reaius="10dp"></corners>
		<solid android:color="#e6d5d5"></solid>
	</shape>
  • 12、把so文件放置到网上或者sdcard内。

99、APK由哪些部分组成?

  • 1、Classes.dex:是Java源码编译后生成的Java字节码文件。

  • 2、resources.arsc:编译后的二进制资源文件。

  • 3、AmdroidManifest.xml:该文件是每个应用程序都必须定义和包含的,它描述了应用的名字、版本、权限、引用的库文件等信息。

  • 4、proguard.cfg:代码混淆的配置文件。

  • 5、project.properties:表示APK的target与sdk的依赖关系,这里的依赖关系是指该APK依赖了哪些工程。

  • 6、assets:该目录用于存放一些配置文件。

  • 7、armeabi:该目录存放于lib目录下,用于存放一些so文件或者jar包。

  • 8、META-INF:该目录下存放的是签名信息,用来保证APK包的完整性和系统的安全性。

  • 9、res:该目录下存放的是资源文件,包括图片、字符串、raw文件夹下面的音频文件,各种XML文件等。

100、Android 5.0 新特性

  • 1、全新的Material Design设计风格
  • 2、支持多种设备
  • 3、全新的通知中心设计
  • 4、支持64位ART虚拟机
  • 5、Overview(多任务视窗)
  • 6、设备识别解锁
  • 7、Ok Google语音指令
  • 8、Face unlock面部解锁

101、Android 6.0 新特性

  • 1、应用权限管理
  • 2、Android Pay
  • 3、指纹支持
  • 4、Doze电量管理
  • 5、App Links
  • 6、Now on Tap

102、Android 7.0 新特性

  • 1、多窗口模式
  • 2、Data Saver
  • 3、改进的Java 8语言支持
  • 4、自定义壁纸
  • 5、快捷回复
  • 6、Daydream VR支持
  • 7、后台省点
  • 8、快速设置
  • 9、Unicode 9支持和全新的emoji表情符号
  • 10、Google Assistant

103、安全
(1)使用PendingIntent时,禁止使用空Intent,同时禁止使用隐式Intent。

  • 1、使用PendingIntent时,使用了空Intent会导致恶意用户劫持修改Intent的内容。禁止使用一个空Intent去构造PendingIntent,构造PendingIntent的Intent一定要设置ComponentName或者action。
  • 2、PendingIntent的Intent接收方在使用该Intent时与发送方有相同的权限。在使用PengdingIntent时,PendingIntent中包装的Intent如果是隐式的Intent,容易遭到劫持,导致信息泄露。

(2)禁止使用常量初始化矢量参数构建 IvParameterSpec ,建议 IV 通过随机方式产生。(注:使用固定初始化向量,结果密码文本可 预测性会高得多,容易受到字典式攻击。IV 的作用主要是用于产生密文的第一个 block ,以使最终生成的密文产生差异(明文相同的情况下),使密码攻击变得更为困难,除此之外 IV 并无其它用途。因此 iv 通过随机方式产生是一种十分简便、有效的途径。)

(3)将 android:allowbackup 属性设置为 false ,防止 adb backup 导出数据。(注:在 AndroidManifest.xml 文件中为了方便对程序数据的备份和恢复在 Android API level 8 以后增加了 android:allowBackup 属性值。默认情况下这个 属性值为 true, 故当 allowBackup 标志值为 true 时,即可通过 adb backup 和 adb restore 来备份和恢复应用程序数据。)

(4) 在实现的 HostnameVerifier 子类中,需要使用 verify 函数效验服务器主机名的合法性,否则会导致恶意程序利用中间人攻击绕过主机名效验。(注:在握手期间,如果 URL 的主机名和服务器的标识主机名不匹配,则验证机制可以回调此接口的实现程序来确定是否应该允许此连接。如果回调内实现不恰当,默认接受所有域名,则有安全风险。)

(5)利用 X509TrustManager 子类中的 checkServerTrusted 函数效验服务器端证书的合法性。(注:在实现的 X509TrustManager 子类中未对服务端的证书做检验,这样会导致不被信任的证书绕过证书效验机制。)

(6)META INF 目录中不能包含如 .apk,.odex,.so 等敏感文件,该文件夹没有经过签名,容易被恶意替换。

(7)Receiver/Provider 不能在毫无权限控制的情况下,将 android:export 设置为 true 。

(8)阻止 webview 通过 file:schema 方式访问本地敏感数据。

(9)不要广播敏感信息,只能在本应用使用 LocalBroadcast ,避免被别的应用收到,或者 setPackage 做限制。

(10)不要把敏感信息打印到 log 中。(注:在 APP 的开发过程中,为了方便调试,通常会使用 log 函数输出一些关键流程的信息,这些信息中通常会包含敏感内容,如执行流程、明文的用户名密码等,这会让攻击者更 加容易的了解 APP 内部结构方便破解和攻击,甚至直接获取到有价值的敏感信息。)

(11)对于内部使用的组件,显示设置组件的 "android: 属性为 false 。(注:Android 应用使用 Intent 机制在组件之间传递数据,如果应用在使用 getIntent()、getAction() 、Intent.getXXXExtra() 获取到空数据、异常或者畸形数据时没有进行异常捕获,应用 就会发生 Crash ,应用不可使用(本地拒绝服务)。恶意应用可通过向受害者应用发送此类空数据、异常或者畸形数据从而使应用产生本地拒绝服务。)

(12)应用发布前确保 android:debuggable 属性设置为 false 。

(13)使用 Intent Scheme URL 需要做过滤。(注:如果浏览器支持 Intent Scheme Uri 语法,如果过滤不当,那么恶意用户可能通过浏览器 js 代码进行一些恶意行为,比如盗取 cookie 等。如果使用了 Intent.parseUri函数,获取的 intent 必须 严格过滤, intent 至少包含
addCategory(“android.intent.category.BROWSABLE”)、setComponent(null)、setSelector(null)3 个策略。)

(14)密钥加密存储或者经过变形处理后用于加解密运算,切勿硬编码到代码中。(注:应用程序在加解密时,使用硬编码在程序中的密钥,攻击者通过反编译拿到密钥可以轻易解密 APP 通信数据。)

(15)将所需要动态加载的文件放置在 apk 内部,或应用私有目录中,如果应用必须要把所加载的文件放置在可被其他应用读写的目录中 比如 sdcard)sdcard),建议对不可信的加载源进行完整性校验和白名单处理,以保证不被恶意代码注入。

(16)除非 min API level >=17 请注意 addJavascriptInterface 的使用。(注:API level >=17 允许 js 被调用的函数必须以 @JavascriptInterface 进行注解,因此不受影响; 对于 API level < 17 ,尽量不要使用 addJavascriptInterface ,如果一定要用,那么:①使用 https 协议加载 URL ,使用证书校验,防止访问的页面被篡改挂马;②对加载 URL 做白名单过滤、完整性校验等防止访问的页面被篡改;③如果加载本地 html 应该会 HTML 内置在 APK 中,以及对 HTML 页面进行完整性校验。)

(17)使用 Android 的 AES/DES/DESede 加密算法时,不要使用默认的加密模式ECB ,应显示指定使用 CBC 或 CFB 加密模式。(注:加密模式 ECB 、 CBC 、 CFB 、 OFB 等,其中 ECB 的安全性较弱,会使相同的铭文在不同的时候产生相同的密文,容易遇到字典攻击,建议使用 CBC 或 CFB 模式。①ECB Electronic codebook 电子密码本模式;②CBC Cipher block chaining 密码分组链接模式;③C FB Cipher feedback 密文反馈模式;④OFB Output feedback 输出反馈模式)。

(18)不要使用 loopback 来通信敏感信息。

(19)Android APP 在 HTTPS 通信中,验证策略需要改成严格模式。 说明: Android APP 在 HTTPS 通信中,使用 ALLOW_ALL_HOSTNAME_VERIFIER ,表示允许和所有的HOST 建立 SSL 通信,这会存在中间人攻击的风险,最终导致敏感信息可能会被劫持,以及其他形式的攻击。

(20)开放的 activity/service/receiver 等需要对传入的 intent 做合法性校验。

(21)不要通过Msg传递大的对象,会导致内存问题。

(22)不能使用System.out.println打印log,可以采用Log.d("");

(23)Log的tag不能是"",因为日志的tag是空字符串没有任何意义,不利于过滤日志。

104、Android Studio中Make Project、Clean Project、Rebuild Project、Build APK、Generate Signed APK、Analyse APK的作用

  • Make Project:编译Project下所有Module,一般是自上次编译后Project下有更新的文件,增量编译,不生成Apk。
  • Clean Project:删除之前编译后的编译文件,比较花费时间。部分版本的AS会自动重新编译整个Project,不生成Apk。
  • Rebuild Project:先执行Clean操作,删除之前编译的编译文件和可执行文件,然后重新编译新的编译文件,不生成Apk,效果同Clean Project。
  • Build APK:前面4个选项都是编译,没有生成apk文件(注:旧版本AS,新版本的AS会自动生成debug.apk),如果想生成Apk,需要点击Build APK。
  • Generate Signed APK:生成有签名的Apk(一般项目嵌入第三方,生成release包时必须混淆,否则无法生成Apk)。
  • Analyse APK:Android Studio逆向分析APK。

105、Android Studio 变量/参数 下划线
Android Studio 编辑页面中的变量/参数下面出现下划线,是指该变量为Reassigned parameter,即该变量被多次赋值。

取消设置 : Editor -> Language Defaults -> Identifiers -> Reassigned parameters

106、Java虚拟机和Dalvik虚拟机的区别?

  • Java虚拟机:
    1、Java虚拟机基于栈。基于栈的机器必须使用指令来载入和操作栈上数据,所需指令更多更多。
    2、Java虚拟机运行的是Java字节码。(Java类会被编译成一个或多个字节码.class文件)

  • Dalvik虚拟机:
    1、dalvik虚拟机是基于寄存器的。
    2、Dalvik运行的是自定义的.dex字节码格式。(java类被编译成.class文件后,会通过一个dx工具将所有的.class文件转换成一个.dex文件,然后dalvik虚拟机会从其中读取指令和数据。)
    3、常量池已被修改为只使用32位的索引,以简化解释器。
    4、一个应用,一个虚拟机实例,一个进程(所有android应用的线程都是对应一个linux线程,都运行在自己的沙盒中,不同的应用在不同的进程中运行。每个android dalvik应用程序都被赋予了一个独立的linux PID(app_*))

107、下拉状态栏会影响activity的生命周期吗?
答案:不会,Android下拉通知栏不会影响Activity的生命周期方法。

108、Android单选框与复选框

  • 单选框:RadioButton
    只能够选一个(排他选项),在RadioGroup中,可以嵌套多个RadioButton。
  • 复选框:CheckBox
    可以选择很多个或者全选

109、Android按钮不可点击

button.setEnabled(false)//按钮不可点击

110、

以上为已整理好的知识点

以下为待整理的Android面试知识点(请忽略)

1、屏幕适配的处理技巧都有哪些?
2、服务器只提供数据接收接口,在多线程或多进程条件下,如何保证数据的有序到达?
3、动态布局的理解
4、混合开发有了解吗?知道哪些混合开发的方式?说出它们的优缺点和各自使用场景?
5、怎么去除重复代码?
6、画出 Android 的大体架构图
7、Recycleview和ListView的区别
RecyclerView可以完成ListView,GridView的效果,还可以完成瀑布流的效果。同时还可以设置列表的滚动方向(垂直或者水平);
RecyclerView中view的复用不需要开发者自己写代码,系统已经帮封装完成了。
RecyclerView可以进行局部刷新。
RecyclerView提供了API来实现item的动画效果。
在性能上:
如果需要频繁的刷新数据,需要添加动画,则RecyclerView有较大的优势。
如果只是作为列表展示,则两者区别并不是很大。
8、动态权限适配方案,权限组的概念
9、下拉状态栏是不是影响activity的生命周期
10、如果在onStop的时候做了网络请求,onResume的时候怎么恢复?
11、ViewPager使用细节,如何设置成每次只初始化当前的Fragment,其他的不初始化?
12、Android中开启摄像头的主要步骤
13、Bitmap使用时候注意什么?
14、点击事件被拦截,但是想传到下面的View,如何操作?
15、微信主页面的实现方式
16、微信上消息小红点的原理
17、ACTION_DOWN没有拦截,ACTION_MOVE ACTION_UP还会拦截吗?
18、谈谈对接口与回调的理解
19、介绍下SurfaceView,它的继承方式是什么?它与View的区别(从源码角度,如加载,绘制等)
SurfaceView中采用了双缓冲机制,保证了UI界面的流畅性,同时SurfaceView不在主线程中绘制,而是另开辟一个线程去绘制,所以它不妨碍UI线程;
SurfaceView继承于View,他和View主要有以下三点区别:
(1)View底层没有双缓冲机制,SurfaceView有;
(2)view主要适用于主动更新,而SurfaceView适用与被动的更新,如频繁的刷新
(3)view会在主线程中去更新UI,而SurfaceView则在子线程中刷新;
SurfaceView的内容不在应用窗口上,所以不能使用变换(平移、缩放、旋转等)。也难以放在ListView或者ScrollView中,不能使用UI控件的一些特性比如View.setAlpha()
View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;必须在UI主线程内更新画面,速度较慢。
SurfaceView:基于view视图进行拓展的视图类,更适合2D游戏的开发;是view的子类,类似使用双缓机制,在新的线程中更新画面所以刷新界面速度比view快,Camera预览界面使用SurfaceView。
GLSurfaceView:基于SurfaceView视图再次进行拓展的视图类,专用于3D游戏开发的视图;是SurfaceView的子类,openGL专用。
20、差值器与估值器
21、ThreadLocal原理,实现及如何保证Local属性?
22、事件分发中的onTouch 和onTouchEvent 有什么区别,又该如何使用?
23、Oom是否可以try catch?为什么?
24、ContentProvider的权限管理(解答:读写分离,权限控制-精确到表级,URL控制)
25、如何通过广播拦截和abort一条短信?
26、广播是否可以请求网络?
27、计算一个view的嵌套层级
28、有没有尝试简化Parcelable的使用?
29、Android属性动画实现原理(22)
30、补间动画实现原理(22)
31、ActivityThread,AMS,WMS的工作原理
32、自定义View如何考虑机型适配
在onMeasure()的getDefaultSize()的默认实现中,当view的测量模式是AT_MOST或EXACTLY时,View的大小都会被设置成子View MeasureSpec的specSize.子view的MeasureSpec值是根据子View的布局参数和父容器的MeasureSpec值计算得来。当子view的布局参数是wrap_content时,对应的测量模式是AT_MOST,大小是parentSize。
33、SharedPreferenced是进程同步的吗?有什么方法做到同步?
34、谈谈多线程在Android中的使用
35、进程和 Application 的生命周期
36、封装View的时候怎么知道view的大小
37、AndroidManifest的作用与理解
38、图片库对比
39、图片加载原理
40、Glide源码解析?Glide使用什么缓存?Glide内存缓存如何控制大小?(23)
41、https中哪里用了对称加密,哪里用了非对称加密,对加密算法(如RSA)等是否有了解?
42、client如何确定自己发送的消息被server收到?
43、谈谈你对WebSocket的理解
44、WebSocket与socket的区别
45、谈谈你对安卓签名的理解
46、解释安卓为啥要加签名机制?
47、视频加密传输
48、App 是如何沙箱化,为什么要这么做?
49、权限管理系统(底层的权限是如何进行grant的)?
50、数据库框架对比和源码分析
51、数据库的优化
52、数据库数据迁移问题
53、对热修复和插件化的理解
54、插件化原理分析
55、模块化实现(好处,原因)
56、项目组件化的理解
57、描述清点击 Android Studio 的 build 按钮后发生了什么
58、谈谈对RxJava的理解。RxJava的作用,与平时使用的异步操作来比的优缺点。
59、从0设计一款App整体架构,如何去做?
60、Fragment如果在Adapter中使用应该如何解耦?
61、Binder机制及底层实现
62、对于应用更新这块是如何做的?(解答:灰度,强制更新,分区域更新)?
63、谈谈对进程共享和线程安全的认识
64、进程调度
65、谈谈对多进程开发的理解以及多进程应用场景
66、Android进程分类?
67、进程和 Application 的生命周期?
68、什么是协程?
69、系统启动流程是什么?(提示:Zygote进程 –> SystemServer进程 –> 各种系统服务 –> 应用进程)
70、一个应用程序安装到手机上时发生了什么
71、Hybrid做过吗?Hybrid通信原理是什么?
72、react native有多少了解?讲一下原理
73、weex了解吗?如何自己实现类似技术?
74、flutter了解吗?内部是如何实现跨平台的?
75、快应用了解吗?跟其它方式相比有什么优缺点?
76、说说你用过的混合开发技术有哪些?各有什么优缺点?
77、Gradle了解多少?groovy语法会吗?
78、java虚拟机和Dalvik虚拟机的区别?
Java虚拟机:
1、java虚拟机基于栈。 基于栈的机器必须使用指令来载入和操作栈上数据,所需指令更多更多。
2、java虚拟机运行的是java字节码。(java类会被编译成一个或多个字节码.class文件)
Dalvik虚拟机:
1、dalvik虚拟机是基于寄存器的
2、Dalvik运行的是自定义的.dex字节码格式。(java类被编译成.class文件后,会通过一个dx工具将所有的.class文件转换成一个.dex文件,然后dalvik虚拟机会从其中读取指令和数据
3、常量池已被修改为只使用32位的索引,以 简化解释器。
4、一个应用,一个虚拟机实例,一个进程(所有android应用的线程都是对应一个linux线程,都运行在自己的沙盒中,不同的应用在不同的进程中运行。每个android dalvik应用程序都被赋予了一个独立的linux PID(app_*))
79、
80、

    原文作者:浩比浩比
    原文地址: https://blog.csdn.net/MaybeForever/article/details/90612395
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。