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、四大组件
(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。
2、定义一个应用程序名的字符串,有以下两种方式来引用它:
<resources>
<string name="app_name">HelloWorldstring>
resources>
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()。不同点是:
16、JSON
一个最基本的JSON对象是有两个花括号:"{“和”}"。
17、JSONObject
在JSONObject这个类中获取value存在两类方法 optXXX和getXXX ,二者的区别如下:
(1)getString在值为null时会抛出异常,我们需要对该异常进行捕获处理;
(2)optString在值为null时不会抛出异常,而是返回fallback的值,因此我们无需进行异常处理;
因此,如果不想对异常进行捕获处理则使用optXXX,否则就需要对相应的异常进行捕获操作。
18、序列化
序列化的作用:
两种序列化的方法:
二者的不同:
总结: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、数据加密
注:采用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(属性动画)。
27、运行时权限
Android将所有权限归成了两类,一类是普通权限,一类是危险权限。
Android在6.0及以上系统在使用危险权限的时候都必须进行运行时权限处理。
28、通知(Notification)
当某个应用程序希望向用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知来实现。
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
android.support.v7.widget.Toolbar
android.support.v4.widget.DrawerLayout
android.support.design.widget.NavigationView
android.support.design.widget.FloatingActionButton
android.support.v7.widget.CardView
android.support.design.widget.AppBarLayout
android.support.v4.widget.SwipeRefershLayout
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、消息推送
消息推送指从服务器端向客户端发送连接,传输一定的信息。推送是自动传送消息给用户,来减少用户在网络上的搜索时间。推送的实现方式有两种,具体描述如下:
消息推送的实现原理:
常见的第三方消息推送SDK:友盟SDK,极光推送等。
35、View的绘制流程
每一个视图的绘制过程必须经过3个主要阶段:OnMeasure()——>OnLayout()——>OnDraw()
//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中的引用
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轴上的距离。
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的核心,通过这两级缓存极大提高了程序的效率并且有效的降低了对用户所造成的流量消耗,只有当这两级缓存都不可用时才需要从网络中拉取图片。而内存缓存、磁盘缓存、网络拉取三者合起来就是三级缓存策略。
48、Binder的内存拷贝过程
相比其他的IPC通信,比如消息机制、共享内存、管道、信号量等,Binder仅需一次内存拷贝,即可让目标进程读取到更新数据,同共享内存一样相当高效,其他的IPC通信机制大多需要2次内存拷贝(见49解释)。Binder内存拷贝的原理为:进程A为Binder客户端,在IPC调用前,需将其用户空间的数据拷贝到Binder驱动的内核空间,由于进程B在打开Binder设备(/dev/binder)时,已将Binder驱动的内核空间映射到自己的进程空间,所以进程B可以直接看到Binder驱动内核空间的内容改动。
49、传统IPC机制的通信原理(2次内存拷贝)
(1)传统的通信方式
进程间通信(IPC)是指在不同进程之间传播或交换信息。
(2)通信原理
(3)存在问题
传统IPC机制存在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的特点
53、ANR的定位和修正
ANR(Application Not Responding)即应用无响应。可以通过查看/data/anr/traces.txt查看ANR信息。
(1)ANR一般有三种类型
(2)很多种ANR错误出现的场景:
(3)原因
根本原因是:主线程被卡了,导致应用在5秒时间未响应用户的输入事件。
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 之间通信的几种方式
57、Android 为每个应用程序分配的内存大小是多少?
Google原生OS的默认值是16M,但是各个厂家的系统会对这个值进行修改,不同厂商的值不同。
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)原理
(2)解决方法
通过上面的分析我们知道了出现错乱的原因是异步加载及对象被复用造成的,如果每次getView能给对象一个标识,在异步加载完成时比较标识与当前行item的标识是否一致,一致则显示,否则不做处理即可。
61、Activity之间的通信方式
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)区别
(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)冷启动的优化
主要是视觉上的优化,解决白屏问题,提高用户体验,能做的优化如下:
(5)为什么冷启动会有白屏黑屏问题?
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的四次挥手
以上为已整理好的知识点
以下为待整理的知识点(请忽略)