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

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

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
    内容提供器,使一个应用程序的指定数据集提供给其他应用程序,其他应用可通过ContentResolver类从该内容提供者中获取或存入数据。它提供了一种跨进程数据共享的方式,当数据被修改后,ContentResolver接口的notifyChange函数通知那些注册监控特定URI的ContentObserver对象。
    ①如果ContentProvider和调用者在同一进程中,ContentProvider的方法(query/insert/update/delete等)和调用者在同一线程中;
    ②如果ContentProvider和调用者不在同一进程,ContentProvider方法会运行在它自身进程的一个Binder线程中。
  • Content Provider
    广播接收器,运用在应用程序间传输信息,可以使用广播接收器来让应用对一个外部事件做出响应。

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

<resources>
	<string name="app_name">HelloWorldstring>
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、序列化
序列化的作用:

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

两种序列化的方法:

  • 实现Serializable接口(是Java SE本身就支持的),implement Serializable就可以。
  • 实现Parcelable接口(是Android特有的功能,效率比实现Serializable接口更高效,可用于Intent数据传递,也可用于进程间通信(IPC))

二者的不同:

  • 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标签中进行注册。

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
    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将所有权限归成了两类,一类是普通权限,一类是危险权限。

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

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

28、通知(Notification)
当某个应用程序希望向用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知来实现。

  • 1、在活动中创建
  • 2、在广播接收器里创建
  • 3、在服务里创建

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来调用系统的播放器。
(2)使用VideoView控件播放视频:VideoView控件需要与MediaController类相结合来播放视频。
(3)使用MediaPlayer与SurfaceView播放视频:可以直接从内存或者DMA等硬件接口中取得图像数据,是个非常重要的绘图容器。

31、Material Design

  • ToolBar(标题栏)
    由于ActionBar设计的原因,被限定只能位于活动的顶部,而ToolBar的强大之处在于不仅继承了ActionBar的所有功能,而且灵活性很高。
    android.support.v7.widget.Toolbar
    
  • DrawerLayout(滑动菜单)
    DrawerLayout是一个布局,在布局中允许放入两个直接子控件,第一个子控件是主屏幕中显示的内容,第二个子控件是滑动菜单中显示的内容。其中,layout_gravity这个属性是必须指定的,因为我们需要告诉DrawerLayout滑动菜单在屏幕的左边还是右边,指定left表示滑动菜单在左边,指定right表示滑动菜单在右边。而指定start表示会根据系统语言进行判断,如果系统语言是从左往右,比如英语、汉语,滑动菜单就在左边,如果系统语言是从右往左,比如阿拉伯语,滑动菜单就在右边。
    android.support.v4.widget.DrawerLayout
    
  • NavigationView(滑动菜单)
    NavigationView是Design Support库中提供的一个控件,它不仅是严格按照Material Design的要求来设计的,而且还可以将滑动菜单页面的实现变得非常简单。
    android.support.design.widget.NavigationView
    
  • FloatingActionButton(悬浮按钮)
    FloatingActionButton是Design Support库中提供的一个控件,这个控件可以帮助我们轻松的实现悬浮按钮的效果。
    android.support.design.widget.FloatingActionButton
    
  • Snackbar(提示工具)
    Snackbar并不是Toast的替代品,它们两者之间有着不同的应用场景。Toast的作用是告诉用户现在发生了什么,用户只能被动的接收这个事情,因为没有什么办法能让用户进行选择。而Scackbar则在这方面进行了扩展,它允许在提示当中加入一个可交互按钮,当用户点击按钮的时候可以执行一些额外的逻辑操作。
  • CoordinatorLayout(布局)
    CoordinatorLayout是一个加强版的FrameLayout,在普通情况下的作用和FrameLayout基本一致,但是,CoordinatorLayout可以监听其所有子控件的各种事情,然后自动帮助我们做出最为合理的响应。比如:如果出现控件相互遮挡,CoordinatorLayout会帮助我们自动调整。
  • CardView(卡片式布局)
    CardView是用于实现卡片式布局效果的重要控件。
    android.support.v7.widget.CardView
    
  • AppBarLayout
    AppBarLayout实际上是一个垂直方向上的LinearLayout,他在内部做了很多滚动事件的封装,可以有效的解决控件遮挡问题(比如:RecyclerView遮挡Toolbar的问题)。
    android.support.design.widget.AppBarLayout
    
  • SwipeRefershLayout(下拉刷新)
    SwipeRefershLayout是用于实现下拉刷新功能的核心类,是由support-v4库提供的。我们把想要实现下拉刷新功能的控件放置到SwipeRefershLayout中,就可以迅速让这个控件支持下拉刷新。
    android.support.v4.widget.SwipeRefershLayout
    
  • CollapsingToolbarLayout(可折叠标题栏)
    CollapsingToolbarLayout可以让Toolbar的效果变得更加丰富,CollapsingToolbarLayout是不能独立存在的,它在设计的时候就被限定只能作为AppBarLayout的直接子布局来使用。而AppBarLayout又必须是CoordinatorLayout的子布局。
    android.support.design.widget.CollapsingToolbarLayout
    
  • 未完待续。。。

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系统中的进程之间不能共享内存,所以需要提供一些机制在不同的进程之间进行数据通信。

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、ANR的定位和修正
ANR(Application Not Responding)即应用无响应。可以通过查看/data/anr/traces.txt查看ANR信息。
(1)ANR一般有三种类型

  • KeyDispatchTimeout(5 seconds) –主要类型
    按键或触摸事件在特定时间内无响应
  • BroadcastTimeout(10 seconds)
    BroadcastReceiver在特定时间内无法处理完成
  • ServiceTimeout(20 seconds) –小概率类型
    Service在特定的时间内无法处理完成

(2)很多种ANR错误出现的场景:

  • 1、主线程当中执行IO/网络操作,容易阻塞。
  • 2、主线程当中执行了耗时的计算。(比如自定义控件中的onDraw()方法)
    在onDraw()方法里面创建对象容易导致内存抖动(绘制动作时会大量不间断调用,产生大量垃圾对象导致GC很频繁,就造成了内存抖动),内存抖动就容易造成UI出现掉帧、卡顿等问题。
  • 3、BroadCastReceiver没有在10秒内完成处理。
  • 4、BroadCastReceiver的onReceived代码中也要尽量减少耗时的操作,建议使用IntentService处理。
  • 5、Service执行了耗时的操作,因为Service也是在主线程当中执行的,所以耗时操作应该在Service里面开启子线程来做。
  • 6、使用AsyncTask处理耗时的IO等操作。
  • 7、Activity的onCreate和onResume回调中尽量耗时的操作。

(3)原因
根本原因是:主线程被卡了,导致应用在5秒时间未响应用户的输入事件。

  • 1、耗时的网络访问
  • 2、大量的数据读写
  • 3、数据库操作
  • 4、硬件操作(比如camera)
  • 5、调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候
  • 6、Service Binder的数量达到上限
  • 7、System Server中发生WatchDog ANR
  • 8、Service忙导致超时无响应
  • 9、其他线程持有锁,导致主线程等待超时
  • 10、其它线程终止或崩溃导致主线程一直等待

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,用来执行具体的展示工作。
Android学习笔记:Android基础知识点(不断更新中)_第1张图片
(上图来源于网络,侵删)

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事件分发(待整理!!!)
(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、ContentProvider、ContentResolver、ContentObserver 之间的关系
(1)ContentProvider
实现各个应用程序间数据共享,用来提供内容给别的应用操作。如联系人应用中就使用了ContentProvider,可以在自己应用中读取和修改联系人信息,不过需要获取相应的权限。它也只是一个中间件,真正的数据源是文件或SQLite等。
(2)ContentResolver
内容解析者,用于获取内容提供者提供的数据,通过ContentResolver.notifyChange(uri)发出消息。
(3)ContentObserver
内容监听者,可以监听数据的改变状态,观察特定Uri引起的数据库变化,继而做一些相应的处理,类似于数据库中的触发器,当ContentObserver所观察的Uri发生变化时,便会触发它。

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、Android中touch事件的传递机制

(1)Touch事件传递的相关API有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent。
(2)Touch事件相关的类有View、ViewGroup、Activity。
(3)Touch事件会被封装成MotionEvent对象,该对象封装了手势按下、移动、松开等动作。
(4)Touch事件通常从Activity#dispatchTouchEvent发出,只要没有被消费,会一直往下传递,到最底层的View。
(5)如果Touch事件传递到的每个View都不消费事件,那么Touch事件会反向向上传递,最终交由Activity#onTouchEvent处理。
(6)onInterceptTouchEvent为ViewGroup特有,可以拦截事件。
(7)Down事件到来时,如果一个View没有消费该事件,那么后续的MOVE/UP事件都不会再给它。

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、冷启动与热启动(待整理!!!)
(1)区别

  • App冷启动
    当应用启动时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用, 这个启动方式就叫做冷启动(后台不存在该应用进程)。冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上。
  • App热启动
    当应用已经被打开, 但是被按下返回键、Home键等按键时回到桌面或者是其他程序的时候,再重新打开该app时, 这个方式叫做热启动(后台已经存在该应用进程)。热启动因为会从已有的进程中来启动,所以热启动就不会走Application这步了,而是直接走MainActivity(包括一系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化一个MainActivity就行了,而不必创建和初始化Application。

(2)冷启动的流程
当点击app的启动图标时,安卓系统会从Zygote进程中fork创建出一个新的进程分配给该应用,之后会依次创建和初始化Application类、创建MainActivity类、加载主题样式Theme中的windowBackground等属性设置给MainActivity以及配置Activity层级上的一些属性、再inflate布局、当onCreate/onStart/onResume方法都走完了后最后才进行contentView的measure/layout/draw显示在界面上。

(3)冷启动的生命周期
Application构造方法 –> attachBaseContext()–>onCreate –>Activity构造方法 –> onCreate() –> 配置主体中的背景等操作 –>onStart() –> onResume() –> 测量、布局、绘制显示

(4)冷启动的优化
主要是视觉上的优化,解决白屏问题,提高用户体验,能做的优化如下:

  • 1、减少onCreate()方法的工作量
  • 2、不要让Application参与业务的操作
  • 3、不要在Application进行耗时操作
  • 4、不要以静态变量的方式在Application保存数据
  • 5、减少布局的复杂度和层级
  • 6、减少主线程耗时

(5)为什么冷启动会有白屏黑屏问题?

  • 原因
    加载主题样式Theme中的windowBackground等属性设置给MainActivity发生在inflate布局当onCreate/onStart/onResume方法之前,而windowBackground背景被设置成了白色或者黑色,所以我们进入app的第一个界面的时候会造成先白屏或黑屏一下再进入界面。
  • 解决思路
    ①设置与启动页的背景相同windowBackground背景,如果你的启动页是张图片那么可以直接给windowBackground这个属性设置该图片那么就不会有一闪的效果了。
    ②采用常见的处理方法,设置背景是透明的,给人一种延迟启动的感觉。将背景颜色设置为透明色,这样当用户点击桌面APP图片的时候,并不会"立即"进入APP,而且在桌面上停留一会,其实这时候APP已经是启动的了,只是我们把Theme里的windowBackground的颜色设置成透明的。
    ③以上两种方法是在视觉上显得更快,但其实只是一种表象,让应用启动的更快,有一种思路,将Application中的不必要的初始化动作实现懒加载,比如,在SpashActivity显示后再发送消息到Application,去初始化,这样可以将初始化的动作放在后边,缩短应用启动到用户看到界面的时间。

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的四次挥手

以上为已整理好的知识点

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

你可能感兴趣的:(Android,Android学习笔记)