Activity、Service、BroadcastReceiver、ContentProvider
onCreate onStart onResume onPause onStop onDestroy onRestart
正常情况:
异常情况:
取决于android:configChanges="orientation|screenSize"设置与否
Dialog对于Activity生命周期没有影响,当按下home键时,activity回调生命周期方法 onPause > onStop
A 跳到 B : A onPause > B onCrate > B onStart > B onResume > A onStop
B 回到 A : B onPause > A onRestart > A onStart > A onResume > B onStop > B onDestory
下拉状态栏不会影响Activity的生命周期
正常流程:
onCreate > onStart【onAttatch > onCreate > onCreateView > onActivityCreated > onStart】 > onResume 【onResume】 > onPause 【onPause】 > onStop 【onStop】> onDestroy【onDestroy > onDestroyView > onDetach】
回到前台:
onRestart > onStart【onStart】 > onResume【onResume】
每启动一次Activity,就创建一个Activity实例
任务栈顶有该Activity实例,回调onNewIntent(); 栈顶无实例,创建新实例
应用场景:登录页面、接收推送的资讯详情页
该Activity的taskAffity属性指定的任务栈,如果不存在,则创建该任务栈,创建Activity实例;任务栈存在,如果栈内有该Activity实例,回调onNewIntent(),并将任务栈中在其上面的Activity全部弹出栈; 栈内无实例,创建新Activity实例
应用场景:主页、浏览器打开网页的Activity(设置SingleTask,设置IntentFilter允许隐式启动)
SingleTask的加强版,如果存在该Activity实例,则回调onNewIntent();无实例,则创建新的任务栈和新的Activity实例,该实例是全局的,该任务栈中也将有且仅有这一个Activity实例,通过该Activity启动的其他Activity也将在其他单独的任务栈中创建
应用场景:来电提醒、闹钟提醒这种全局唯一的页面,项目中还未用到过
复用Activity,回调onNewIntent()的情况:
A为SingleTask,B为Standard,启动顺序:A > B > A ,生命周期切换如下
A : onCreate() > onStart() > onResume() > onPause()
B : onCreate() > onStart() > onResume()
A : onStop()
B : onPause()
A : onNewIntent() > onRestart() > onStart() > onResume()
B : onStop() > onDestroy()
异常销毁时会回调:
Activity > Fragment:
Fragment > Activity:
Fragment > Fragment:
startService:
onCreate > onStartCommand > onDestory
bindService:
onCreate > onBind > onUnbind > onDestory
1.启动
通过startService()启动,一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,任务完成后自行停止
2.绑定
通过调用bindService() 绑定启动,绑定服务的生命周期会跟调用者关联起来,调用者退出,服务也会跟着销毁
Service
Service 是长期运行在后台的应用程序组件。
Service 不是一个单独的进程,它和应用程序在同一个进程中,Service 也不是一个线程,它和线程没有任何关系,所以它不能直接处理耗时操作。如果直接把耗时操作放在 Service 的 onStartCommand() 中,很容易引起 ANR .如果有耗时操作就必须开启一个单独的线程来处理。
IntentService
IntentService 是继承于 Service 并处理异步请求的一个类,在 IntentService 内有一个工作线程来处理耗时操作,启动 IntentService 的方式和启动传统 Service 一样,同时,当任务执行完后,IntentService 会自动停止,而不需要我们去手动控制。另外,可以启动 IntentService 多次,而每一个耗时操作会以工作队列的方式在IntentService 的 onHandleIntent 回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。
参考:Android开发之Service与IntentService的区别与使用场景
参考:Service与Activity之间通信的2种方式
ContentProvider是Android四大组件之一,管理对中央数据存储区的访问。它封装数据,为存储和访问数据提供统一的接口,并提供用于定义数据安全性的机制。 主要作用是向其他应用提供数据,并保护数据访问的安全性。
ContentProvider 内容提供者,用于对外提供数据
ContentResolver 内容解析者,用于获取内容提供者提供的数据
ContentObserver 内容监听器,可以监听数据的改变状态
BroadCastReceiver 是 Android 四大组件之一,主要用于接收系统或者 app 发送的广播事件,通过android系统的Binder机制实现
有序广播 sendOrderedBroadcast();
按照被接收者的优先级顺序,在被接收者中依次传播。比如有三个广播接收者 A,B,C,优先级是 A >B > C。那这个消息先传给 A,再传给 B,最后传给 C。每个接收者有权终止广播,比如 B 终止广播,C 就无法接收到。此外 A接收到广播后可以对结果对象进行操作,当广播传给 B 时,B 可以从结果对象中取得 A 存入的数据
普通广播 sendBroadcast();
完全异步,逻辑上可以被任何广播接收者接收到。优点是效率较高。缺点是一个接收者不能将处理结果传递给下一个接收者,并无法终止广播 intent 的传播。
系统广播
本地广播
清单文件注册(静态注册),只要应用程序被部署到手机上,就立刻生效,不管进程是否处于运行状态;
代码注册(动态注册),代码运行了,广播接收者才生效,如果代码运行结束,广播接收者,就失效;对于动态广播,有注册就必然得有注销,否则会导致内存泄露,重复注册、重复注销也不允许
AlertDialog、popupWindow是View,Activity是Android系统中的四大组件之一,可以用于显示View。
区别:
(1)Popupwindow在显示之前一定要设置宽高,Dialog无此限制。
(2)Popupwindow默认不会响应物理键盘的back,除非显示设置了popup.setFocusable(true);而在点击back的时候,Dialog会消失。
(3)Popupwindow不会给页面其他的部分添加蒙层,而Dialog会。
(4)Popupwindow没有标题,Dialog默认有标题,可以通过dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);取消标题
(5)二者显示的时候都要设置Gravity。如果不设置,Dialog默认是Gravity.CENTER。
(6)二者都有默认的背景,都可以通过setBackgroundDrawable(new ColorDrawable(android.R.color.transparent));去掉
最关键的区别:
AlertDialog是非阻塞式对话框:AlertDialog弹出时,后台还可以做事情;
PopupWindow是阻塞式对话框:PopupWindow弹出时,程序会等待,在PopupWindow退出前,程序一直等待,只有当我们调用了dismiss方法的后,PopupWindow退出,程序才会向下执行。
参考:AlertDialog和PopupWindow的区别
Application 和 Activity 都是 Context 的子类, Activity的Context维护的是Activity的生命周期,而Application的Context维护的事应用的生命周期。
Context数量 = Activity数量 + Service数量 + 1; 1就是Application数量
**(1)Frame Animation(帧动画)**主要用于播放一帧帧准备好的图片,类似GIF图片,优点是使用简单方便、缺点是需要事先准备好每一帧图片;
**(2)Tween Animation(补间动画)**仅需定义开始与结束的关键帧,而变化的中间帧由系统补上,优点是不用准备每一帧,缺点是只改变了对象绘制,而没有改变View本身属性。因此如果改变了按钮的位置,还是需要点击原来按钮所在位置才有效。
**(3)Property Animation(属性动画)**是3.0后推出的动画,优点是使用简单、降低实现的复杂度、直接更改对象的属性、几乎可适用于任何对象而仅非View类,缺点是需要3.0以上的API支持,限制较大!但是目前国外有个开源库,可以提供低版本支持!
参考:Android三大动画使用总结、Android 有近10种动画,你都知道吗?
第一种:帧布局(框架布局)FrameLayout,在这个布局中,所有的子元素统统放于这块区域的左上角,并且后面的子元素直接覆盖在前面的子元素之上,将前面的子元素部分和全部遮挡。
第二种:线性布局 LinearLayout,最常用的一种布局方式,所有子控件的对齐方式,取决于如何定义 orientation的属性:vertical 垂直方向 ,如果按照这种方向所有的子控件将按照垂直的方式分布在布局上,每行只允许有一个子元素,horizontal水平方向 ,这时子控件将会以水平的方向分布在布局中。
第三种:绝对布局 AbsoluteLayout,又可以叫做坐标布局,可以直接指定子元素的绝对位置,这种布局简单直接,直观性强,但是由于手机屏幕尺寸差别比较大,使用绝对定位的适应性会比较差。
第四种:相对布局 RelativeLayout,允许子元素指定它们相对于其父元素或兄弟元素的位置,这是实际布局中最常用的布局方式之一。它灵活性大很多,当然属性也多,操作难度也大,属性之间产生冲突的的可能性也大,使用相对布局时要多做些测试。
第五种:表格布局 TableLayout,表格布局TableLayout以行列的形式管理子元素,每一行是一个TableRow布局对象,当然也可以是普通的View对象,TableRow里每放一个元素就是一列,总列数由列数最多的那一行决定。
第六种:网格布局 GridLayout,在Android 4.0中,新引入的GridLayout网格布局,GridLayout布局使用虚细线将布局划分为行,列和单元格,也支持一个控件在行,列上都有交错排列。而GridLayout使用的其实是跟LinearLayout类似的API,只不过是修改了一下相关的标签而已,所以对于开发者来说,掌握GridLayout还是很容易的事情。
第七种:约束布局
参考:
我们知道View是通过刷新来重绘视图,系统通过发出VSSYNC信号来进行屏幕的重绘,刷新的时间间隔是16ms,如果我们可以在16ms以内将绘制工作完成,则没有任何问题,如果我们绘制过程逻辑很复杂,并且我们的界面更新还非常频繁,这时候就会造成界面的卡顿,影响用户体验,为此Android提供了SurfaceView来解决这一问题。
View和SurfaceView的区别:
1 . View适用于主动更新的情况,而SurfaceView则适用于被动更新的情况,比如频繁刷新界面。
2 . View在主线程中对页面进行刷新,而SurfaceView则开启一个子线程来对页面进行刷新。
3 . View在绘图时没有实现双缓冲机制,SurfaceView在底层机制中就实现了双缓冲机制。
双缓冲主要是为了解决反复局部刷屏带来的闪烁。把要画的东西先画到一个内存区域里,然后整体的一次性画出来。
参考:Android中的SurfaceView详解
Serializable使用IO读写存储在硬盘上。序列化过程使用了反射技术,并且期间产生临时对象。优点代码少。
Parcelable是直接在内存中读写,我们知道内存的读写速度肯定优于硬盘读写速度,所以Parcelable序列化方式性能上要优于Serializable方式很多。但是代码写起来相比Serializable方式麻烦一些。
参考:浅谈Android中Serializable和Parcelable使用区别
1.SQLite:
SQLite是一个轻量级的数据库,支持基本SQL语法,是常被采用的一种数据存储方式,最大支持2TB容量。Android为此数据库提供了一个名为SQLiteDatabase的类,封装了一些操作数据库的API。
Android上SQLite的使用
Android ORM 框架:GreenDao 使用详解
2.SharedPreference:
其本质就是一个xml文件,常用于存储较简单的参数设置。
commit和apply方法的区别:
SharedPreferences中的commit和apply方法
3.File:
即常说的文件(I/O)存储方法,常用于存储大数量的数据,但是缺点是更新数据将是一件困难的事情。
4.ContentProvider:
Android 系统中能实现所有应用程序共享的一种数据存储方式,由于数据通常在各应用间的是互相私密的,所以此存储方式较少使用,但是其又是必不可少的一种存储方式。 例如音频,视频,图片和通讯录,一般都可以采用此种方式进行存储。每个ContentProvider都会对外提供一个公共的URI,其它应用通过ContentResolver,使用这个URI进行数据操作
5.网络存储:
从网络读取数据和写入数据。 Android提供了通过网络来实现数据的存储和获取的方法。
我们可以调用WebService返回的数据或是解析HTTP协议实现网络数据交互。
开发过程中,根据设计目标、性能需求、空间需求等找到 合适的数据存储方式
一、xml解析
DOM、SAX、PULL
参考:XML数据的三种解析方式
二、json
手动解析、GSON、FastJson
GSON、FastJson的区别?
参考:JSON解析、Gson与FastJson比较
三、xml和json区别
5.0特性
6.0特性
7.0特性
8.0特性
9.0特性
参考:Android 1.5到10.0 都有哪些新特性?、Android5.0,6.0,7.0,8.0新特性整理
参考:Android 屏幕适配:最全面的解决方案、smallestWidth适配、
今日头条适配方案、Android 刘海屏适配方案
Android 6.0把权限分成正常权限和危险权限,AndroidManifest中声明的正常权限系统会自动授予,而危险权限则需要在使用的时候用户明确授予。
对于危险权限:
1.检查权限 ContextCompat.checkSelfPermission
2.申请权限 ActivityCompat.requestPermissions
3.处理权限请求响应 onRequestPermissionsResult
参考:Android动态权限申请
Android 8.0系统的通知栏适配
Android 8.0系统的应用图标适配
Android的UI访问是没有加锁的,多个线程可以同时访问更新操作同一个UI控件。也就是说访问UI的时候,android系统当中的控件都不是线程安全的,这将导致在多线程模式下,当多个线程共同访问更新操作同一个UI控件时容易发生不可控的错误,而这是致命的。所以Android中规定只能在UI线程中访问UI,这相当于从另一个角度给Android的UI访问加上锁,一个伪锁。提高移动端更新UI的效率和和安全性.
子线程更新UI的正确姿势:
1;使用Handler
2;使用Activity的runOnUiThread方法把更新UI的代码创建在Runnable中即可
3;使用AsyncTask
4;HandlerThread
原因:
异步加载 + Listview的view复用
解决方案:
1.findViewByTag (由于ListView中的ImageView控件都是重用的,移出屏幕的控件很快会被进入屏幕的图片重新利用起来,那么getView()方法就会再次得到执行,而在getView()方法中会为这个ImageView控件设置新的Tag,这样老的Tag就会被覆盖掉,于是这时再调用findVIewWithTag()方法并传入老的Tag,就只能得到null了,而我们判断只有ImageView不等于null的时候才会设置图片,这样图片乱序的问题也就不存在了。)
2.Volley当中提供的控件NetworkImageView(原理就是如果这个控件已经被移出了屏幕且被重新利用了,那么就把之前的请求取消掉,仅此而已)
参考:Android ListView异步加载图片乱序问题,原因分析及解决方案
Recyclerview是Android 5.0新增的一个列表控件。顾名思义,recycler view,只负责回收和复用视图,高度的解耦,可灵活定制,轻松实现Listview、GridView、瀑布流的效果。
优点:
item复用
把ViewHolder的实现封装起来,规范了ViewHolder,把item的view写入ViewHolder中,可以通过复用ViewHolder来实现view的复用
灵活、可定制化高、可拓展性高
显示方式:通过LayoutManager控制
item分割线:通过ItemDecoration控制
item动画:通过ItemAnimator控制
item点击事件:自定义
参考:
1、区别
2、ListView采用的是RecyclerBin的回收机制在一些轻量级的List显示时效率更高
参考:
每个安卓应用程序必须有一个AndroidManifest.xml文件,在app/manifests目录中。主要作用用于告诉系统一些应用的信息:
Handler
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
handler在两个子线程间传递数据
AsyncTask
参考:Android AsyncTask 源码解析
IntentService
IntentService是一种特殊的Service,它继承了Service并是一个抽象类,它使用工作线程逐一处理所有启动请求。如果您不要求服务同时处理多个请求,这是最好的选择。 您只需实现 onHandleIntent() 方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。
IntentService 执行以下操作:
综上所述,您只需实现 onHandleIntent() 来完成客户端提供的工作即可。(不过,您还需要为服务提供小型构造函数。)
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
}
(1)对于 Animation 动画:
他的实现机制是,在每次进行绘图的时候,通过对整块画布的矩阵进行变换,从而实现一种视图坐标的移动,但实际上其在 View 内部真实的坐标位置及其他相关属性始终恒定.
(2)对于 Animator 动画:
Animator 动画的实现机制说起来其实更加简单一点,因为他其实只是计算动画开启之后,结束之前,到某个时间点得时候,某个属性应该有的值,然后通过回调接口去设置具体值,其实 Animator 内部并没有针对某个 view 进行刷新,来实现动画的行为,动画的实现是在设置具体值的时候,方法内部自行调取的类似 invalidate 之类的方法实现的.也就是说,使用 Animator ,内部的属性发生了变化.
主要类:
android调用Js:
通过WebView的loadUrl()
通过WebView的evaluateJavascript()
Js调用Android:
通过WebView的addJavascriptInterface()进行对象映射
通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息
基本优化:
(1)给WebView加一个加载进度条
(2)加快HTML网页加载完成的速度,等页面finish再加载图片
(3)自定义WebView页面加载出错界面
(4) 可以代码创建WebView,使用完后及时销毁,防止内存泄漏
(5) 漏洞:
1.任意代码执行漏洞:4.2后使用@JavascriptInterface,之前使用其他调用方式
2.密码明文存储漏洞:WebSettings.setSavePassword(false)
3.域控制不严格漏洞:禁止file 协议
参考:
Linux 内核
硬件抽象层 (HAL)
Android Runtime
原生 C/C++ 库
Java API 框架
系统应用
参考:如何在低版本SDK调用高版本API?
一个Android 已封装好的轻量级异步类
属于抽象类,即使用时需 实现子类
主要用来执行耗时任务,然后更新UI
参考:AsyncTask最详细使用教程
原理:
AsyncTask的实现原理 = 2个线程池 + Handler
缺陷:
AsyncTask在并发执行多个任务时发生异常。其实还是存在的,在3.0以前的系统中还是会以支持多线程并发的方式执行,支持并发数也是我们上面所计算的128,阻塞队列可以存放10个;也就是同时执行138个任务是没有问题的;而超过138会马上出现java.util.concurrent.RejectedExecutionException;而在在3.0以上包括3.0的系统中会为单线程执行
参考:
Handler 机制:
Handler是Android中的消息传递机制,因为在Android中只能在UI线程进行UI更新操作,所以在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现工作线程对UI的更新处理,最终实现异步消息的处理。保证UI线程安全。
关键类:
对应关系:
Handler的工作流程:
异步通信准备
消息发送
消息循环
消息处理
总结完成,大家可能还会问,那么在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。
参考:深入理解 Looper、Handler、Message三者关系
Handler:在android中负责发送和处理消息,通过它可以实现工作线程与主线程之间的消息通讯。
Thread:Java进程中执行运算的最小单位。分为主线程和工作线程,一个进程中至少有一个线程,就是主线程
HandlerThread:本质是继承Thread类 & 封装Handler类 , 为异步消息通信提供便利
参考: 教你使用HandlerThread
ThreadLocal即线程变量,它为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal的实现是以ThreadLocal对象为键。任意对象为值得存储结构。**这个结构被附带在线程上,**也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。
参考:
分类:
继承View,创建新View
继承ViewGroup,创建新Layout
继承现有特定的View进行扩展,如继承Textview自定义字体
继承现有特定的ViewGroup,进行自定义组合控件
适配:
getMeasureWidth()/getMeasuredHeight()
在measure()过程结束后就可以获取到了
getMeasureXXX()方法中的值是通过setMeasuredDimension()方法来进行设置的
getHeight()/getWidth()
要在layout()过程结束后才能获取到
getXXX()方法中的值则是通过视图右边的坐标减去左边的坐标计算出来的。
View的绘制从ActivityThread类中Handler处理RESUME_ACTIVITY事件开始,在执行performResumeActivity之后,创建Window以及DecorView并调用WindowManager的addView方法添加到屏幕上,addView又调用ViewRootImpl的setView方法,最终执行performTraversals方法,依次执行performMeasure,performLayout,performDraw。也就是view绘制的三大过程。
measure过程测量view的视图大小,最终需要调用setMeasuredDimension方法设置测量的结果,如果是ViewGroup需要调用measureChildren或者measureChild方法进而计算自己的大小。
layout过程是摆放view的过程,View不需要实现,通常由ViewGroup实现,在实现onLayout时可以通过getMeasuredWidth等方法获取measure过程测量的结果进行摆放。
draw过程先是绘制背景,其次调用onDraw()方法绘制view的内容,再然后调用dispatchDraw()调用子view的draw方法,最后绘制滚动条。ViewGroup默认不会执行onDraw方法,如果复写了onDraw(Canvas)方法,需要调用 setWillNotDraw(false);清除不需要绘制的标记。
从UI加载流程来分析:
UI加载流程由Activity#setContentView()开始,最终调用的是PhoneWindow#setContentView(),在其中又会调用honeWindow#installDecor()创建DecorView,然后加载我们自定义布局资源,最后通过Window#addView()将布局添加到屏幕上,addView又调用ViewRootImpl的setView方法,最终执行performTraversals方法,依次执行performMeasure,performLayout,performDraw。也就是view绘制的三大过程。
由此,可看出Activity算是控制器,Window算是实际承载View的容器,而View就是显示视图
事件分发其实就是MotionEvent事件分发过程,当一个MotionEvent产生以后,系统需要把这个事件传递给一个具体的View,这个过程就是事件分发。
传递顺序:
Activity 》 Window 》 ViewGroup 》 View
主要方法:
dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()
分发步骤:
事件总是先传递给Activity,Activity再传递给Window,Window传递给顶级View,然后按照事件分发机制分发事件。
传递到Viewgroup时,它的dispatchTouchEvent()会被调用,在其中如果它的onInterceptTouchEvent()返回true就表示它要拦截这个事件,接着事件就会交给这个ViewGroup处理,它的onTouchEvent()就会被调用。
如果它的onInterceptTouchEvent()返回false就表示它不拦截这个事件,这个事件就会继续传递给它的子View
接着子View的dispatchTouchEvent()会被调用,如此反复直到事件被处理。
当一个View需要处理事件时,如果它设置了onTouchLister,那么其中的onTouch()方法会被调用。如果onTouch()方法返回true,则View的onTouchEvent()不会被调用;如果返回false,则View的onTouchEvent()会被调用。 (onTouchLister > onTouchEvent > onClickListener)
如果这个View的onTouchEvent()返回false,那么它的父容器的onTouchEvent()将会被调用,以此类推,如果所有元素都不处理这个事件,最终会回传给Activity,Activity的onTouchEvent()会被调用。
事件传递是由外向内的,子元素也可以通过requestDisallowInterceptTouchEvent()方法干预分发过程,但是ACTION_DOWN事件除外。
事件冲突的解决:
内部拦截法
父容器不做任何拦截,所有的事件都交个子元素,如果子元素需要就直接消耗掉,否则返回给父容器处理,需要配合requestDisallowInterceptTouchEvent()方法使用
外部拦截法
重写父容器的onInterceptTouchEvent()方法,在其中做出相应的拦截
参考:Android事件分发机制详解
调用requestLayout()方法的时机是:当前View发生了一些改变,这个改变使得现有的View失效,所以调用requestLayout()方法对View树进行重新布局,过程包括了measure()和layout()过程,但不会调用draw()过程,即不会发生重新绘制视图过程。
调用onLayout()的时机是:View需要给自己设置大小和位置了或者ViewGroup需要给子View和ViewGroup自身时调用。
自定义一个view时,重写onDraw()。画自己
自定义一个ViewGroupdispatchDraw()会调用drawChild()。画孩子
参考:requestLayout(),onLayout(),onDraw(),drawChild() 区别与联系?
view.invalidate(),会触发onDraw()和computeScroll()。前提是该view被附加在当前窗口上
view.postInvalidate(); //是在非UI线程上调用的
参考:invalidate() 和 postInvalidate() 的区别
递归调用getParent()
参考:计算一个ViewGroup的嵌套层级
当application在Linux平台开启时,系统会给这个application创建一个进程(process)来运行同时分配内存资源给该application,当程序结束运行时,该进程结束系统回收内存资源
SparseArray有两个优点:
1.避免了自动装箱(auto-boxing),
2.数据结构不会依赖于外部对象映射。
我们知道HashMap 采用一种所谓的“Hash 算法”来决定每个元素的存储位置,存放的都是数组元素的引用,通过每个对象的hash值来映射对象。而SparseArray则是用数组数据结构来保存映射,然后通过折半查找来找到对象。但其实一般来说,SparseArray执行效率比HashMap要慢一点,因为查找需要折半查找,而添加删除则需要在数组中执行,而HashMap都是通过外部映射。但相对来说影响不大,最主要是SparseArray不需要开辟内存空间来额外存储外部映射,从而节省内存。
参考:Android为什么推荐使用SparseArray来替代HashMap?
玩转Android Bitmap
Android 之 Bitmap
参考:Volley 源码解析
参考:okhttp源码解析
布局优化
核心思想就是减少布局的层级,层级减少了,绘制时间就减少了,性能自然提高
绘制优化
避免在ondraw()中执行大量的操作,Google建议的是每帧的绘制不超过16ms,总之尽量减少onDraw()中绘制的复杂度,循环次数
内存泄露优化
响应速度优化
将耗时操作:数据库查询,网络请求,复杂的逻辑计算通过handler,Asynctask,runonuiThread异步操作,保证UI界面的流畅性;避免ANR的出现,一般Activity是5秒,广播是10秒
Listview和Bitmap优化
电量优化
APK瘦身优化
参考:Android应用开发性能优化完全分析
在 Android 上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框。
用户可以选择让程序继续运行,但是,他们在使用你的应用程序时,并不希望每次都要处理这个对话框。因此,在程序里对响应性能的设计很重要,这样,系统不会显示ANR 给用户。
不同的组件发生 ANR 的时间不一样,Activity是 5 秒,BroadCastReceiver 是 10 秒,Service是20s
解决方案:
将所有耗时操作,比如访问网络,Socket 通信,查询大量 SQL 语句,复杂逻辑计算等都放在子线程中去,然后通过 handler、runonUITread、AsyncTask 等方式更新 UI。无论如何都要确保用户界面操作的流畅度。
定位:
分析ANR,除了检查代码的生命周期函数是否有耗时操作,还可以分析traces日志(data/anr/traces.txt),分析角度主要包括:
1.栈信息,一般可以知道在哪段代码附近发生了ANR,可能不是直接原因,但一般在问题点附近。
2.CPU用量,看负载比例和平均负载,判断是不是有别的App占用了过多的CPU。
3.IO Wait,看IOWait的占比是否很高,判断是否在等待IO。
参考:深入理解ANR
Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。我们平常看到的OutOfMemory的错误,通常是堆内存溢出。
虽然JAVA有垃圾回收机制,但也存在内存泄露。如果我们一个程序中,已经不再使用某个对象,但是因为仍然有引用指向它,垃圾回收器就无法回收它,当然 该对象占用的内存就无法被使用,这就造成了内存泄露。内存泄漏严重了就会导致内存溢出OOM
造成内存泄漏的原因:
是否可以 try catch?
一般不适合这么处理,try catch是用来捕获检查性异常的
Java中管理内存除了显式地catch OOM之外还有更多有效的方法:比如SoftReference, WeakReference, 硬盘缓存等。
在JVM用光内存之前,会多次触发GC,这些GC会降低程序运行的效率。
如果OOM的原因不是try语句中的对象(比如内存泄漏),那么在catch语句中会继续抛出OOM
参考:Android OOM出现常见原因及解决办法
线程池
参考:Java多线程引发的性能问题以及调优策略
参考:你的 APP 为何启动那么慢?
1.SVN优缺点
优点:
1、 管理方便,逻辑明确,符合一般人思维习惯。
2、 易于管理,集中式服务器更能保证安全性。
3、 代码一致性非常高。
4、 适合开发人数不多的项目开发。
缺点:
1、 服务器压力太大,数据库容量暴增。
2、 如果不能连接到服务器上,基本上不可以工作,看上面第二步,如果服务器不能连接上,就不能提交,还原,对比等等。
3、 不适合开源开发(开发人数非常非常多,但是Google app engine就是用svn的)。但是一般集中式管理的有非常明确的权限管理机制(例如分支访问限制),可以实现分层管理,从而很好的解决开发人数众多的问题。
2.Git优缺点
优点:
1、适合分布式开发,强调个体。
2、公共服务器压力和数据量都不会太大。
3、速度快、灵活。
4、任意两个开发者之间可以很容易的解决冲突。
5、离线工作。
缺点:
1、学习周期相对而言比较长。
2、不符合常规思维。
3、代码保密性差,一旦开发者把整个库克隆下来就可以完全公开所有代码和版本信息。
Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。在Android Studio中,我们可以使用Gradle来完成APK的编译及打包工作
使用Gradle的原因:
(1)、使用领域专用语言(Domain Specific Language)来描述和处理构建逻辑。
(2)、基于Groovy。DSL可以混合各种声明元素,用代码操控这些DSL元素达到逻辑自定义。
(3)、支持已有的Maven或者Ivy仓库基础建设。
(4)、非常灵活,允许使用best practices,并不强制让你遵照它的原则来。
(5)、其它插件时可以暴露自己的DSL和API来让Gradle构建文件使用。
(6)、允许IDE集成,是很好的API工具。
在Android Studio中,你可以在Gradle中配置以下内容:
(1)、配置插件及插件的属性
(2)、配置远程仓库,像jcenter和maven
(3)、配置所依赖的第三方库,jar包等
(4)、配置多渠道打包的信息
(5)、配置应用的签名信息,编译版本信息等等