赞
踩
1)Activity:用户可操作的可视化界面,为用户提供一个完成操作指令的窗口。一个Activity通常是一个单独的屏幕,Activity通过Intent来进行通信。Android中会维持一个Activity Stack,当一个新Activity创建时,它就会放到栈顶,这个Activity就处于运行状态。
2)Service:服务,运行在手机后台,适合执行不需和用户交互且还需长期运行的任务。
3)ContentProvider:内容提供者,使一个应用程序的指定数据集提供给其他应用程序,其他应用可通过ContentResolver类从该内容提供者中获取或存入数据。它提供了一种跨进程数据共享的方式,当数据被修改后,ContentResolver接口的notifyChange函数通知那些注册监控特定URI的ContentObserver对象。
如果ContentProvider和调用者在同一进程中,ContentProvider的方法(query/insert/update/delete等)和调用者在同一线程中;如果ContentProvider和调用者不在同一进程,ContentProvider方法会运行在它自身进程的一个Binder线程中。
4)Broadcast Receiver: 广播接收者,运用在应用程序间传输信息,可以使用广播接收器来让应用对一个外部事件做出响应。
1)Activity:onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory()
onCreate():为Activity设置布局,此时界面还不可见;
onStart(): Activity可见但还不能与用户交互,不能获得焦点
onRestart(): 重新启动Activity时被回调
onResume(): Activity可见且可与用户进行交互
onPause(): 当前Activity暂停,不可与用户交互,但还可见。在新Activity启动前被系统调用保存现有的Activity中的持久数据、停止动画等。
onStop(): 当Activity被新的Activity覆盖不可见时被系统调用
onDestory(): 当Activity被系统销毁杀掉或是由于内存不足时调用
2)Service
a) onBind方式绑定的:onCreate->onBind->onUnBind->onDestory(不管调用bindService几次,onCreate只会调用一次,onStart不会被调用,建立连接后,service会一直运行,直到调用unBindService或是之前调用的bindService的Context不存在了,系统会自动停止Service,对应的onDestory会被调用)
b) startService启动的:onCreate->onStartCommand->onDestory(start多次,onCreate只会被调用一次,onStart会调用多次,该service会在后台运行,直至被调用stopService或是stopSelf)
c) 又被启动又被绑定的服务,不管如何调用onCreate()只被调用一次,startService调用多少次,onStart就会被调用多少次,而unbindService不会停止服务,必须调用stopService或是stopSelf来停止服务。必须unbindService和stopService(stopSelf)同时都调用了才会停止服务。
3)BroadcastReceiver
a) 动态注册:存活周期是在Context.registerReceiver和Context.unregisterReceiver之间,BroadcastReceiver每次收到广播都是使用注册传入的对象处理的。
b) 静态注册:进程在的情况下,receiver会正常收到广播,调用onReceive方法;生命周期只存活在onReceive函数中,此方法结束,BroadcastReceiver就销毁了。onReceive()只有十几秒存活时间,在onReceive()内操作超过10S,就会报ANR。
进程不存在的情况,广播相应的进程会被拉活,Application.onCreate会被调用,再调用onReceive。
4)ContentProvider:应该和应用的生命周期一样,它属于系统应用,应用启动时,它会跟着初始化,应用关闭或被杀,它会跟着结束。
1)使用Shared Preferences存储数据,用来存储key-value,pairs格式的数据,它是一个轻量级的键值存储机制,只可以存储基本数据类型。
2)使用文件存储数据,通过FileInputStream和FileOutputStream对文件进行操作。在Android中,文件是一个应用程序私有的,一个应用程序无法读写其他应用程序的文件。
3)使用SQLite数据库存储数据,Android提供的一个标准数据库,支持SQL语句。
4)使用Content Provider存储数据,是所有应用程序之间数据存储和检索的一个桥梁,它的作用就是使得各个应用程序之间实现数据共享。它是一个特殊的存储数据的类型,它提供了一套标准的接口用来获取数据,操作数据。系统也提供了音频、视频、图像和个人信息等几个常用的Content Provider。如果你想公开自己的私有数据,可以创建自己的Content Provider类,或者当你对这些数据拥有控制写入的权限时,将这些数据添加到Content Provider中实现共享。外部访问通过Content Resolver去访问并操作这些被暴露的数据。
5)使用网络存储数据
最常用的布局方式为Absolute Layout、Relative Layout、Linear Layout、FrameLayout、TableLayout。其中Linear Layout和Relative Layout是最常用的方式,他们可以通过在xml配置文件或者代码中进行布局。
1)Frame Layout是最简单的布局方式,放置的控件都只能罗列到左上角,控件会有重叠,不能进行复杂的布局。
2)Linear Layout可以通过orientation属性设置线性排列的方向是垂直还是纵向的,每行或每列只有一个元素,可以进行复杂的布局。
3)Absolute Layout可以让子元素指定准确的x、y坐标值,并显示在屏幕上。Absolute Layout没有页边框,允许元素之间相互重叠。它是绝对坐标,所以在实际中不提倡使用。
1)不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
2)设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
3)设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
1)通过Intent方式传递参数跳转
2)通过广播方式
3)通过接口回调方式
4)借助类的静态变量或全局变量
5)借助SharedPreference或是外部存储,如数据库或本地文件
1)共享变量(内存)
2)管道
3)handle机制
runOnUiThread(Runnable)
view.post(Runnable)
Android 跨进程通信,像intent,contentProvider,广播,service都可以跨进程通信。
intent:这种跨进程方式并不是访问内存的形式,它需要传递一个uri,比如说打电话。
contentProvider:这种形式,是使用数据共享的形式进行数据共享。
service:远程服务,
aidl
广播
创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。
handler,它的作用就是实现线程之间的通信。
handler整个流程中,主要有四个对象,handler,Message,MessageQueue,Looper。当应用创建的时候,就会在主线程中创建handler对象,
我们通过要传送的消息保存到Message中,handler通过调用sendMessage方法将Message发送到MessageQueue中,Looper对象就会不断的调用loop()方法
不断的从MessageQueue中取出Message交给handler进行处理。从而实现线程之间的通信。
1)standard:标准启动模式(默认),每启动一次Activity,都会创建一个实例,即使从ActivityA startActivity ActivityA,也会再次创建A的实例放于栈顶,当回退时,回到上一个ActivityA的实例。
2) singleTop:栈顶复用模式,每次启动Activity,如果待启动的Activity位于栈顶,则不会重新创建Activity的实例,即不会走onCreate->onStart,会直接进入Activity的onPause->onNewIntent->onResume方法
3) singleInstance: 单一实例模式,整个手机操作系统里只有一个该Activity实例存在,没有其他Actvity,后续请求均不会创建新的Activity。若task中存在实例,执行实例的onNewIntent()。应用场景:闹钟、浏览器、电话
4) singleTask:栈内复用,启动的Activity如果在指定的taskAffinity的task栈中存在相应的实例,则会把它上面的Activity都出栈,直到当前Activity实例位于栈顶,执行相应的onNewIntent()方法。如果指定的task不存在,创建指定的taskAffinity的task,taskAffinity的作用,进入指写taskAffinity的task,如果指定的task存在,将task移到前台,如果指定的task不存在,创建指定的taskAffinity的task. 应用场景:应用的主页面
BroadcastReceiver是一种全局监听器,用来实现系统中不同组件之间的通信。有时候也会用来作为传输少量而且发送频率低的数据,但是如果数据的发送频率比较高或者数量比较大就不建议用广播接收者来接收了,因为这样的效率很不好,因为BroadcastReceiver接收数据的开销还是比较大的。
从下到上就是:
linux kernel,lib dalvik vm ,application framework, app
intent间传送数据一般有两种常用的方法: 1、extra 2、data。
extra可以用Intent.putExtra放入数据。新启动的Activity可用Intent.getExtras取出Bundle,然后用Bundles.getLong,getInt,getBoolean,getString等函数来取放进去的值。
Data则是传输url。url可以是指我们熟悉的http,ftp等网络地址,也可以指content来指向ContentProvider提供的资源。Intent.setData可以放入数据,Intent.getData可以取出数据。
一个程序可以通过实现一个Content provider的抽象接口将自己的数据完全暴露出去,而且Content provider是以类似数据库中的表的方式将自己的数据暴露。Content provider存储和检索数据,通过它可以让所有的应用程序访问到,这也是应用程序之间唯一共享数据的方法。
要想使应用程序的数据公开化,可通过2种方法:创建一个数据自己的Content Provider或者将你的数据添加到一个已经存在的Content Provider中,前提是有相同数据类型并且有写入Content Provider的权限,Android提供了Content Resolverr,外界的程序可以通过Content Resolver接口访问Content Provider提供的数据
帧动画:指通过指定每一帧的图片和播放时间,有序的进行播放而形成动画效果,比如想听的律动条。
补间动画:指通过指定View的初始状态、变化时间、方式,通过一系列的算法去进行图形变换,从而形成动画效果,主要有Alpha、Scale、Translate、Rotate四种效果。注意:只是在视图层实现了动画效果,并没有真正改变View的属性,比如滑动列表,改变标题栏的透明度。
属性动画:在Android3.0的时候才支持,通过不断的改变View的属性,不断的重绘而形成动画效果。相比于视图动画,View的属性是真正改变了。比如view的旋转,放大,缩小。
垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能。
基本数据类型的==比较的值相等.
类的==比较的内存的地址,即是否是同一个对象,在不覆盖equals的情况下,同比较内存地址,原实现也为 == ,如String等重写了equals方法.
hashCode也是Object类的一个方法。返回一个离散的int型整数。在集合类操作中使用,为了提高查询速度。(HashMap,HashSet等比较是否为同一个)
如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。
如果两个对象不equals,他们的hashcode有可能相等。
如果两个对象hashcode相等,他们不一定equals。
如果两个对象hashcode不相等,他们一定不equals。
int 基本类型
integer 对象 int的封装类
String:字符串常量 不适用于经常要改变值得情况,每次改变相当于生成一个新的对象
StringBuffer:字符串变量 (线程安全)
StringBuilder:字符串变量(线程不安全) 确保单线程下可用,效率略高于StringBuffer
进程是cpu资源分配的最小单位,线程是cpu调度的最小单位。
进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。
一个进程内可拥有多个线程,进程可开启进程,也可开启线程。
一个线程只能属于一个进程,线程可直接使用同进程的资源,线程依赖于进程而存在。
final:修饰类、成员变量和成员方法,类不可被继承,成员变量不可变,成员方法不可重写
finally:与try...catch...共同使用,确保无论是否出现异常都能被调用到
finalize:类的方法,垃圾回收之前会调用此方法,子类可以重写finalize()方法实现对资源的回收
Serializable Java 序列化接口 在硬盘上读写 读写过程中有大量临时变量的生成,内部执行大量的i/o操作,效率很低。
Parcelable Android 序列化接口 效率高 使用麻烦 在内存中读写(AS有相关插件 一键生成所需方法) ,对象不能保存到磁盘中
Parcelable 消耗内存小
网络传输用Serializable 程序内使用Parcelable
Serializable 将数据持久化方便
Serializable 使用了反射 容易触发垃圾回收 比较慢
Set是最简单的一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。 Set接口主要实现了两个实现类:HashSet: HashSet类按照哈希算法来存取集合中的对象,存取速度比较快
TreeSet :TreeSet类实现了SortedSet接口,能够对集合中的对象进行排序。
List的特征是其元素以线性方式存储,集合中可以存放重复对象。
ArrayList() : 代表长度可以改变得数组。可以对元素进行随机的访问,向ArrayList()中插入与删除元素的速度慢。
LinkedList(): 在实现中采用链表数据结构。插入和删除速度快,访问速度慢。
Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 Map没有继承于Collection接口 从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
HashMap:Map基于散列表的实现。插入和查询“键值对”的开销是固定的。可以通过构造器设置容量capacity和负载因子load factor,以调整容器的性能。
LinkedHashMap: 类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点。而在迭代访问时发而更快,因为它使用链表维护内部次序。
TreeMap : 基于红黑树数据结构的实现。查看“键”或“键值对”时,它们会被排序(次序由Comparabel或Comparator决定)。TreeMap的特点在 于,你得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。
WeakHashMao :弱键(weak key)Map,Map中使用的对象也被允许释放: 这是为解决特殊问题设计的。如果没有map之外的引用指向某个“键”,则此“键”可以被垃圾收集器回收。
1)存储方式不同
HashMap内部有一个HashMapEntry<K, V>[]对象,每一个键值对都存储在这个对象里,当使用put方法添加键值对时,就会new一个HashMapEntry对象,
2)添加数据时扩容时的处理不一样,进行了new操作,重新创建对象,开销很大。ArrayMap用的是copy数据,所以效率相对要高。
3)ArrayMap提供了数组收缩的功能,在clear或remove后,会重新收缩数组,是否空间
4)ArrayMap采用二分法查找;
HashMap不是线程安全的,效率高一点、方法不是Synchronize的要提供外同步,有containsvalue和containsKey方法。
hashtable是,线程安全,不允许有null的键和值,效率稍低,方法是是Synchronize的。有contains方法方法。Hashtable 继承于Dictionary 类
1)LocalBroadcastReceiver仅在自己的应用内发送接收广播,也就是只有自己的应用能收到,数据更加安全。广播只在这个程序里,而且效率更高。只能动态注册,在发送和注册的时候采用LocalBroadcastManager的sendBroadcast方法和registerReceiver方法。
2)全局广播:发送的广播事件可被其他应用程序获取,也能响应其他应用程序发送的广播事件(可以通过 exported–是否监听其他应用程序发送的广播 在清单文件中控制) 全局广播既可以动态注册,也可以静态注册。
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));去掉。
7)Popupwindow弹出后,取得了用户操作的响应处理权限,使得其他UI控件不被触发。而AlertDialog弹出后,点击背景,AlertDialog会消失。
Padding是控件的内容相对控件的边缘的边距
Margin是控件边缘相对于其他控件的边距
servie是系统的组件,它由系统进程托管(servicemanager);它们之间的通信类似于client和server,是一种轻量级的ipc通信,这种通信的载体是binder,它是在linux层交换信息的一种ipc。而thread是由本应用程序托管。
1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。
如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。
既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。
举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。
因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。
Java程序中wait 和 sleep都会造成某种形式的暂停,它们可以满足不同的需要。wait()方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而sleep()方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。
RecyclerView可以完成ListView,GridView的效果,还可以完成瀑布流的效果。同时还可以设置列表的滚动方向(垂直或者水平);
RecyclerView中view的复用不需要开发者自己写代码,系统已经帮封装完成了。
RecyclerView可以进行局部刷新。
RecyclerView提供了API来实现item的动画效果。
在性能上:
如果需要频繁的刷新数据,需要添加动画,则RecyclerView有较大的优势。
如果只是作为列表展示,则两者区别并不是很大。
内存泄露是个折腾的问题。
什么时候会发生内存泄露?内存泄露的根本原因:长生命周期的对象持有短生命周期的对象。短周期对象就无法及时释放。
1) 静态集合类引起内存泄露
主要是hashmap,Vector等,如果是静态集合 这些集合没有及时setnull的话,就会一直持有这些对象。
2)remove 方法无法删除set集 Objects.hash(firstName, lastName);
经过测试,hashcode修改后,就没有办法remove了。
3)observer 我们在使用监听器的时候,往往是addxxxlistener,但是当我们不需要的时候,忘记removexxxlistener,就容易内存leak。
4)广播没有unregisterrecevier
5)各种数据链接没有关闭,数据库contentprovider,io,sokect等。cursor
6)内部类:
java中的内部类(匿名内部类),会持有宿主类的强引用this。
所以如果是new Thread这种,后台线程的操作,当线程没有执行结束时,activity不会被回收。
Context的引用,当TextView 等等都会持有上下文的引用。如果有static drawable,就会导致该内存无法释放。
7)单例
单例 是一个全局的静态对象,当持有某个复制的类A时,A无法被释放,内存leak。
当程序需要申请一段“大”内存,但是虚拟机没有办法及时的给到,即使做了GC操作以后
这就会抛出 OutOfMemoryException 也就是OOM
为了减少单个APP对整个系统的影响,android为每个app设置了一个内存上限。
避免oom主要在于减少内存对象的占用
1)ArrayMap/SparseArray代替hashmap
2)避免在android里面使用Enum
3)减少bitmap的内存占用
5)减少资源图片的大小,过大的图片可以考虑分段加载
内存对象的重复利用
大多数对象的复用,都是利用对象池的技术。
1)listview/gridview/recycleview contentview的复用
2)inBitmap 属性对于内存对象的复用ARGB_8888/RBG_565/ARGB_4444/ALPHA_8
这个方法在某些条件下非常有用,比如要加载上千张图片的时候。
3)避免在ondraw方法里面 new对象
4)StringBuilder 代替+
检测:
1、DDMS Heap发现内存泄露
dataObject totalSize的大小,是否稳定在一个范围内,如果操作程序,不断增加,说明内存泄露
2、使用Heap Tool进行内存快照前后对比
BlankActivity手动触发GC进行前后对比,对象是否被及时回收
定位:
1、MAT插件打开.hprof具体定位内存泄露:
查看histogram项,选中某一个对象,查看它的GC引用链,因为存在GC引用链的,说明无法回收
2、AndroidStudio的Allocation Tracker:
观测到期间的内存分配,哪些对象被创建,什么时候创建,从而准确定位
①Item布局,层级越少越好,使用hierarchyview工具查看优化。
②复用convertView
③使用ViewHolder
④item中有图片时,异步加载
⑤快速滑动时,不加载图片
⑥item中有图片时,应对图片进行适当压缩
⑦实现数据的分页加载
android 性能主要之响应速度 和UI刷新速度。
首先从函数的耗时来说,有一个工具TraceView 这是androidsdk自带的工作,用于测量函数耗时的。
UI布局的分析,可以有2块,一块就是Hierarchy Viewer 可以看到View的布局层次,以及每个View刷新加载的时间。
这样可以很快定位到那块layout & View 耗时最长。
还有就是通过自定义View来减少view的层次。
ANR:Application Not Responding,即应用无响应
ANR一般有三种类型:
1)KeyDispatchTimeout(5 seconds) –主要类型
按键或触摸事件在特定时间内无响应
2)BroadcastTimeout(10 seconds)
BroadcastReceiver在特定时间内无法处理完成
3)ServiceTimeout(20 seconds) –小概率类型
Service在特定的时间内无法处理完成
超时的原因一般有:
1.耗时的网络访问
2.大量的数据读写
3.数据库操作
4.硬件操作(比如camera)
5.调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候
6.service binder的数量达到上限
7.system server中发生WatchDog ANR
8.service忙导致超时无响应
9.其他线程持有锁,导致主线程等待超时
10.其它线程终止或崩溃导致主线程一直等待
public class CrashHandler implements Thread.UncaughtExceptionHandler { private static CrashHandler instance = null; public static synchronized CrashHandler getInstance() { if(instance == null) { instance = new CrashHandler(); } return instance; } public void init(Context context) { Thread.setDefaultUncaughtExceptionHandler(this); } @Override public void uncaughtException(Thread thread, Throwable ex) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("Thread:"); stringBuilder.append(thread.toString()); stringBuilder.append("\t"); stringBuilder.append(ex); TraceLog.i(stringBuilder.toString()); TraceLog.printCallStatck(ex); } }
关键是实现Thread.UncaughtExceptionHandler
然后是在application的oncreate里面注册。
重写onSaveInstanceState()方法,在此方法中保存需要保存的数据,该方法将会在activity被回收之前调用。通过重写onRestoreInstanceState()方法可以从中提取保存好的数据
app优化:(工具:Hierarchy Viewer 分析布局 工具:TraceView 测试分析耗时的)
App启动优化
布局优化
响应优化
内存优化
电池使用优化
网络优化
App启动优化(针对冷启动)
App启动的方式有三种:
冷启动:App没有启动过或App进程被killed, 系统中不存在该App进程, 此时启动App即为冷启动。
热启动:热启动意味着你的App进程只是处于后台, 系统只是将其从后台带到前台, 展示给用户。
介于冷启动和热启动之间, 一般来说在以下两种情况下发生:
(1)用户back退出了App, 然后又启动. App进程可能还在运行, 但是activity需要重建。
(2)用户退出App后, 系统可能由于内存原因将App杀死, 进程和activity都需要重启, 但是可以在onCreate中将被动杀死锁保存的状态(saved instance state)恢复。
优化:
Application的onCreate(特别是第三方SDK初始化),首屏Activity的渲染都不要进行耗时操作,如果有,就可以放到子线程或者IntentService中
优化
尽量不要过于复杂的嵌套。可以使用<include>,<merge>,<ViewStub>
响应优化
Android系统每隔16ms会发出VSYNC信号重绘我们的界面(Activity)。
页面卡顿的原因:
(1)过于复杂的布局.
(2)UI线程的复杂运算
(3)频繁的GC,导致频繁GC有两个原因:1、内存抖动, 即大量的对象被创建又在短时间内马上被释放.2、瞬间产生大量的对象会严重占用内存区域。
内存优化:参考内存泄露和内存溢出部分
电池使用优化(使用工具:Batterystats & bugreport)
(1)优化网络请求
(2)定位中使用GPS, 请记得及时关闭
网络优化(网络连接对用户的影响:流量,电量,用户等待)可在Android studio下方logcat旁边那个工具Network Monitor检测
API设计:App与Server之间的API设计要考虑网络请求的频次, 资源的状态等. 以便App可以以较少的请求来完成业务需求和界面的展示.
Gzip压缩:使用Gzip来压缩request和response, 减少传输数据量, 从而减少流量消耗.
图片的Size:可以在获取图片时告知服务器需要的图片的宽高, 以便服务器给出合适的图片, 避免浪费.
网络缓存:适当的缓存, 既可以让我们的应用看起来更快, 也能避免一些不必要的流量消耗.
Framework是android 系统对 linux kernel,lib库等封装,提供WMS,AMS,bind机制,handler-message机制等方式,供app使用。
简单来说framework就是提供app生存的环境。
1)Activity在attch方法的时候,会创建一个phonewindow(window的子类)
2)onCreate中的setContentView方法,会创建DecorView
3)DecorView 的addview方法,会把layout中的布局加载进来。
屏幕适配的方式:xxxdpi, wrap_content,match_parent. 获取屏幕大小,做处理。
dp来适配屏幕,sp来确定字体大小
drawable-xxdpi, values-1280*1920等 这些就是资源的适配。
wrap_content,match_parent, 这些是view的自适应
weight,这是权重的适配。
view的更新必须在UI thread中进行
surfaceview会单独有一个线程做ui的更新。
surfaceview 支持open GL绘制。
相关的滑动组件 重写onInterceptTouchEvent,然后判断根据xy值,来决定是否要拦截当前操作
1)自定义View的属性 编写attr.xml文件
2)在layout布局文件中引用,同时引用命名空间
3)在View的构造方法中获得我们自定义的属性 ,在自定义控件中进行读取(构造方法拿到attr.xml文件值)
4)重写onMesure
5)重写onDraw
自定义控件:
1、组合控件。这种自定义控件不需要我们自己绘制,而是使用原生控件组合成的新控件。如标题栏。
2、继承原有的控件。这种自定义控件在原生控件提供的方法外,可以自己添加一些方法。如制作圆角,圆形图片。
3、完全自定义控件:这个View上所展现的内容全部都是我们自己绘制出来的。比如说制作水波纹进度条。
View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()
第一步:OnMeasure():测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。
第二步:OnLayout():确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。
第三步:OnDraw():绘制视图。ViewRoot创建一个Canvas对象,然后调用OnDraw()。六个步骤:
①、绘制视图的背景;
②、保存画布的图层(Layer);
③、绘制View的内容;
④、绘制View子视图,如果没有就不用;
⑤、还原图层(Layer);
⑥、绘制滚动条。
必须可以。子线程 可以new 一个mainHandler,然后发送消息到UI Thread。
AsyncTask 运用的场景就是我们需要进行一些耗时的操作,耗时操作完成后更新主线程,或者在操作过程中对主线程的UI进行更新。
缺陷:AsyncTask中维护着一个长度为128的线程池,同时可以执行5个工作线程,还有一个缓冲队列,当线程池中已有128个线程,缓冲队列已满时,如果此时向线程提交任务,将会抛出RejectedExecutionException。
解决:由一个控制线程来处理AsyncTask的调用判断线程池是否满了,如果满了则线程睡眠否则请求AsyncTask继续处理。
拿主线程来说,主线程启动时会调用Looper.prepare()方法,会初始化一个Looper,放入Threadlocal中,接着调用Looper.loop()不断遍历Message Queue,
Handler的创建依赖与当前线程中的Looper,如果当前线程没有Looper则必须调用Looper.prepare()。Handler , sendMessage到MessageQueue,Looper不断
从MessageQueue中取出消息,回调handleMessage方法。
可以放在不同的Task中。需要为不同的activity设置不同的affinity属性,启动activity的Intent需要包含FLAG_ACTIVITY_NEW_TASK标记。
AsyncTask,HandlerThread,IntentService
AsyncTask原理:内部是Handler和两个线程池实现的,Handler用于将线程切换到主线程,两个线程池一个用于任务的排队,一个用于执行任务,当AsyncTask执行execute方法时会封装出一个FutureTask对象,将这个对象加入队列中,如果此时没有正在执行的任务,就执行它,执行完成之后继续执行队列中下一个任务,执行完成通过Handler将事件发送到主线程。AsyncTask必须在主线程初始化,因为内部的Handler是一个静态对象,在AsyncTask类加载的时候他就已经被初始化了。在Android3.0开始,execute方法串行执行任务的,一个一个来,3.0之前是并行执行的。如果要在3.0上执行并行任务,可以调用executeOnExecutor方法
HandlerThread原理:继承自Thread,start开启线程后,会在其run方法中会通过Looper创建消息队列并开启消息循环,这个消息队列运行在子线程中,所以可以将HandlerThread中的Looper实例传递给一个Handler,从而保证这个Handler的handleMessage方法运行在子线程中,Android中使用HandlerThread的一个场景就是IntentService
IntentService原理:继承自Service,它的内部封装了HandlerThread和Handler,可以执行耗时任务,同时因为它是一个服务,优先级比普通线程高很多,所以更适合执行一些高优先级的后台任务,HandlerThread底层通过Looper消息队列实现的,所以它是顺序的执行每一个任务。可以通过Intent的方式开启IntentService,IntentService通过handler将每一个intent加入HandlerThread子线程中的消息队列,通过looper按顺序一个个的取出并执行,执行完成后自动结束自己,不需要开发者手动关闭
Intent,Binder(AIDL),Messenger,BroadcastReceiver
Thread & AsyncTask
Thread 可以与Loop 和 Handler 共用建立消息处理队列
AsyncTask 可以作为线程池并行处理多任务
要想知道如何使用多进程,先要知道Android里的多进程概念。一般情况下,一个应用程序就是一个进程,这个进程名称就是应用程序包名。我们知道进程是系统分配资源和调度的基本单位,所以每个进程都有自己独立的资源和内存空间,别的进程是不能任意访问其他进程的内存和资源的。
那如何让自己的应用拥有多个进程?
很简单,我们的四大组件在AndroidManifest文件中注册的时候,有个属性是android:process,
1、这里可以指定组件的所处的进程。默认就是应用的主进程。指定为别的进程之后,系统在启动这个组件的时候,就先创建(如果还没创建的话)这个进程,然后再创建该组件。你可以重载Application类的onCreate方法,打印出它的进程名称,就可以清楚的看见了。再设置android:process属性时候,有个地方需要注意:如果是android:process=”:deamon”,以:开头的名字,则表示这是一个应用程序的私有进程,否则它是一个全局进程。私有进程的进程名称是会在冒号前自动加上包名,而全局进程则不会。一般我们都是有私有进程,很少使用全局进程。他们的具体区别不知道有没有谁能补充一下。
2、使用多进程显而易见的好处就是分担主进程的内存压力。我们的应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。当然还有其他好处,有心人会发现Android后台进程里有很多应用是多个进程的,因为它们要常驻后台,特别是即时通讯或者社交应用,不过现在多进程已经被用烂了。典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。还有就是防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。应该还有还有其他好处,这里就不多说了。
3、坏处的话,多占用了系统的空间,大家都这么用的话系统内存很容易占满而导致卡顿。消耗用户的电量。应用程序架构会变复杂,应为要处理多进程之间的通信。这里又是另外一个问题了。
视图动画,或者说补间动画。只是视觉上的一个效果,实际view属性没有变化,性能好,但是支持方式少。
属性动画,通过变化属性来达到动画的效果,性能略差,支持点击等事件。android 3.0
帧动画,通过drawable一帧帧画出来。
Gif动画,原理同上,canvas画出来。
overridePendingTransition
java中有三种创建线程的方式,或者说四种
1.继承Thread类实现多线程
2.实现Runnable接口
3.实现Callable接口
4.通过线程池
线程池的工作原理:线程池可以减少创建和销毁线程的次数,从而减少系统资源的消耗,当一个任务提交到线程池时
a. 首先判断核心线程池中的线程是否已经满了,如果没满,则创建一个核心线程执行任务,否则进入下一步
b. 判断工作队列是否已满,没有满则加入工作队列,否则执行下一步
c. 判断线程数是否达到了最大值,如果不是,则创建非核心线程执行任务,否则执行饱和策略,默认抛出异常
Handler,Message,looper和MessageQueue构成了安卓的消息机制,handler创建后可以通过sendMessage将消息加入消息队列,然后looper不断的将消息从MessageQueue中取出来,回调到Hander的handleMessage方法,从而实现线程的通信。
从两种情况来说,第一在UI线程创建Handler,此时我们不需要手动开启looper,因为在应用启动时,在ActivityThread的main方法中就创建了一个当前主线程的looper,并开启了消息队列,消息队列是一个无限循环,为什么无限循环不会ANR?因为可以说,应用的整个生命周期就是运行在这个消息循环中的,安卓是由事件驱动的,Looper.loop不断的接收处理事件,每一个点击触摸或者Activity每一个生命周期都是在Looper.loop的控制之下的,looper.loop一旦结束,应用程序的生命周期也就结束了。我们可以想想什么情况下会发生ANR,第一,事件没有得到处理,第二,事件正在处理,但是没有及时完成,而对事件进行处理的就是looper,所以只能说事件的处理如果阻塞会导致ANR,而不能说looper的无限循环会ANR
另一种情况就是在子线程创建Handler,此时由于这个线程中没有默认开启的消息队列,所以我们需要手动调用looper.prepare(),并通过looper.loop开启消息
主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。