生命周期函数 | 调用情景 |
---|---|
oncreate() | activity创建调用 |
onstart() | 当前activity是可见状态,没有焦点,不能交互,一般可在当前方法做一些动画的初始化操作 |
onResume() | 当前activity状态属于运行状态 (Running),可与用户进行交互 |
onPause() | 当另外一个activity覆盖当前的acitivty时,当前activity是可见的,但不能与用户交互状态 |
onStop() | 此时activity不可见,系统内存紧张的情况下,有可能会被回收。所以一般在当前方法可做资源回收 |
onDestory() | activity即将被销毁 |
onRestart() | 再次进入到当前activity的时候调用 |
横竖屏切换activity生命周期?
1、AndroidManifest.xml不设置Activity的android:configChanges时 或 设置Activity的android:configChanges="orientation"时,切屏会重新调用各个生命周期。生命周期如下:
onSaveInstanceState() -onPause() -onStop() -onDestory() -onCreate() -onStart() -onRestoreInstanceState() -onResume()
2、设置Activity的android:configChanges="orientation|screenSize"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged() 方法。
standard 标准模式 : 每次启动一个 Activity 都会重新创建一个新的实例,不管这个实例是否已经存在,此模式的 Activity 默认会进入启动它的 Activity 所属的任务栈中;
singleTop 栈顶复用模式 : 如果新 Activity 已经位于任务栈的栈顶,那么此 Activity 不会被重新创建,同时会回调 onNewIntent方法,如果新 Activity 实例已经存在但不在栈顶,那么Activity 依然会被重新创建;
singleTask 栈内复用模式 : 只要 Activity 在一个任务栈中存在,那么多次启动此 Activity 都不会重新创建实例,并回调onNewIntent 方法,此模式启动 Activity A,系统首先会寻找是否存在 A 想要的任务栈,如果不存在,就会重新创建一个任务栈,然后把创建好 A 的实例放到栈中;
singleInstance单实例模式 : 这是一种加强的 singleTask 模式,具有此种模式的 Activity 只能单独地位于一个任务栈中,且此任务栈中只有唯一一个实例
Activity 创建时通过 attach()初始化了一个 Window 也就是PhoneWindow,一个 PhoneWindow 持有一个DecorView 的实例,DecorView 本身是一个 FrameLayout,继承于 View,Activty 通过setContentView 将xml 布局控件不断 addView()添加到 View 中,最终显示到 Window 于我们交互
add不会重新初始化fragment,replace每次都会;
添加相同的fragment时,replace不会有任何变化,add会报IllegalStateException 异常;
replace 先 remove 掉相同 id 的所有 fragment,然后在add 当前的这个 fragment,而 add 是覆盖前一个fragment。所以如果使用 add 一般会伴随 hide()和show(),避免布局重叠;
使用 add,如果应用放在后台,或以其他方式被系统销毁,再打开时,hide()中引用的 fragment 会销毁,所以依然会出现布局重叠 bug,可以使用 replace 或使用 add时,添加一个 tag 参数
Handler
广播
接口
EventBus
生命周期 | 调用情景 | 工作内容 |
---|---|---|
onCreate() | 第一次创建时候调用 | |
onStartComand() | 服务启动时调用 | 一些数据加载工作 |
onBind() | 服务被绑定时调用 | |
onUnBind() | 服务被解绑时调用 | |
onDestroy() | 服务停止时调用 |
startService():
通过这种方式调用startService,onCreate()只会被调用一次,多次调用startSercie会多次执行onStartCommand()和onStart()方法。如果外部没有调用stopService()或stopSelf()方法,service会一直运行。
bindService():
如果该服务之前还没创建,系统回调顺序为onCreate()→onBind()。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法不会多次创建服务及绑定。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,回调顺序为onUnbind()→onDestroy()。
注意:
Service默认并不会运行在子线程中,也不运行在一个独立的进程中,它同样执行在主线程中(UI线程)。不要在Service里执行耗时操作,除非手动打开一个子线程,否则有可能出现主线程被阻塞(ANR)的情况。
ActivityManagerService是Android中最核心的服务 ,负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块类似。
Service不是独立的进程,也不是独立的线程,它是依赖于应用程序的主线程(比喻成没有界面的activity),也就是说,在更多时候不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。
IntentService是Service的子类,IntentService在执行onCreate操作的时候,内部开了一个线程,去你执行你的耗时操作。通过Handler looper message的方式实现了一个多线程的操作,同时耗时操作也可以被这个线程管理和执行,同时不会产生ANR的情况。
通过Binder实现通信:
Activity调用bindService(Intent service, ServiceConnection conn, int flags)方法,绑定一个继承Service的引用对象MsgService。通过实例化ServiceConnection接口内部类监听的方法获取MsgService中的Binder对象。如果想实现主动通知Activity的,还可以在MsgService中添加回调方法。
通过Broadcast实现通信:
Activity调用registerReceive(BroadcastReceiver recevier, IntentFilter filter)注册广播接收器,通过startService(Service service)启动一个集成Service的应用对象MsgService,之后MsgService可通过sendBoadcast(Intent intent)更新Actvity内容信息。
普通广播:
开发者自身定义 intent的广播(最常用),所有的广播接收器几乎会在同一时刻接受到此广播信息,接受的先后顺序随机;
有序广播:
发送出去的广播被广播接收者按照先后顺序接收,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,且优先级(priority)高的广播接收器会先收到广播消息。有序广播可以被接收器截断使得后面的接收器无法收到它;
本地广播:
仅在自己的应用内发送接收广播,也就是只有自己的应用能收到,数据更加安全,效率更高,但只能采用动态注册的方式;
粘性广播:
这种广播会一直滞留,当有匹配该广播的接收器被注册后,该接收器就会收到此条广播;
1.继承BroadcastReceiver,重写onReceive()方法。
2.通过Binder机制向ActivityManagerService注册广播。
3.通过Binder机制向ActivityMangerService发送广播。
4.ActivityManagerService查找符合相应条件的广播(IntentFilter/Permission)的BroadcastReceiver,将广播发送到BroadcastReceiver所在的消息队列中。
5.BroadcastReceiver所在消息队列拿到此广播后,回调它的onReceive()方法。
两种,动态注册和静态注册(Android8.0以后不能静态注册广播,官方说法是为了省电),区别:
动态注册的广播永远要快于静态注册的广播,不管静态注册的优先级设置的多高,不管动态注册的优先级有多低。
动态注册广播不是常驻型广播 ,也就是说广播跟随activity的生命周期。注意: 在activity调用ondestory(),移除广播接收器。静态注册是常驻型 (不能自动销毁),也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
在同一个优先级下,谁先启动的快,谁将先接收到广播。
BroadcastReceiver,LocalBroadcastReceiver 区别
1.应用场景不同
BroadcastReceiver用于应用之间的传递消息;而LocalBroadcastManager用于应用内部传递消息,比broadcastReceiver更加高效。
2.使用安全性不同
BroadcastReceiver使用的Content API,所以本质上它是跨应用的,所以在使用它时必须要考虑到不要被别的应用滥用;LocalBroadcastManager不需要考虑安全问题,因为它只在应用内部有效。
ContentProvider :四大组件的内容提供者,主要用于对外提供数据
实现各个应用程序之间的(跨应用)数据共享,比如联系人应用中就使用了ContentProvider,你在自己的应用中可以读取和修改联系人的数据,不过需要获得相应的权限。其实它也只是一个中间人,真正的数据源是文件或者SQLite等
一个应用实现ContentProvider来提供内容给别的应用来操作,通过ContentResolver来操作别的应用数据,当然在自己的应用中也可以。
ContentResolver:内容解析者,用于获取内容提供者提供的数据
ContentResolver.notifyChange(uri)发出消息
ContentObserver:内容监听器,可以监听数据的改变状态
目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它。触发器分为表触发器、行触发器,相应地ContentObsever也分为表ContentObserver、行ContentObserver,当然这是与它所监听的Uri MIME Type有关的
ContentResolver.registerContentObserver()监听消息
联系:使用ContentResolver来获取ContentProvider提供的数据,同时注册ContentObserver监听Uri数据的变化
Android中的Intent是一个非常重要且常用的类,可以用来在一个组件中启动App中的另一个组件或者是启动另一个App的组件,这里所说的组件指的是Activity、Service以及Broadcast。
有两种类型的Intent: explicit(显式) 的和 implict(隐式) 的。
intent主要有以下几种重要用途:
Android Intent 传递数据大小限制:
Intent携带信息的大小其实是受Binder限制。数据以Parcel对象的形式存放在Binder传递缓存中。 如果数据或返回值比传递buffer大,则此次传递调用失败并抛出TransactionTooLargeException异常。
Binder传递缓存有一个限定大小,通常是1Mb。但同一个进程中所有的传输共享缓存空间。
多个地方在进行传输时,即时它们各自传输的数据不超出大小限制,TransactionTooLargeException异常也可能会被抛出。
在使用Intent传递数据时,1Mb并不是安全上限。因为Binder中可能正在处理其它的传输工作。 不同的机型和系统版本,这个上限值也可能会不同。
传输大量数据,可以考虑URL之类的方法。
数值型 | byte整数类型 | 8(1字节) |
short整数类型 | 16(2字节) | |
ini整数类型 | 32(4字节) | |
long整数类型 | 64(8字节) | |
浮点型 | float单精度浮点类型 | 32(4字节) |
double双精度浮点类型 | 64(8字节) | |
字符型 | char字符类型 | 16(2字节) |
布尔类型 | boolean布尔类型 | 8(1字节) |
String在JAVA中不是基本数据类型,
toLowerCase() 方法用于将字符串中的大写字母转换成小写,
toUpperCase() 方法用于将字符串中的小写字母转换为大写。
基本数据类型= =比较的是值,非基本数据类型= =比较的是内存地址
equals比较的是内存地址
SharedPreferences :轻量级存储,以键值对的形式存储在xml中,一般用来保存应用中的设置属性,适合保存少量的数据
文件存储 :存储多媒体文件, 文件缓存
Sqlite存储 :适合存储结构化数据
Content Provider :进程(应用程序)间共享数据的一种存储方式
网络存储 :xml,json格式等等,通过HttpUrlConnection,HttpClient,或者SOAP协议获取数据
android自定义对象可序列化有两个选择一个是Serializable和Parcelable
所谓序列化就是将对象变成二进制流,便于存储和传输。
一、对象为什么需要序列化
1.永久性保存对象,保存对象的字节序列到本地文件。
2.通过序列化对象在网络中传递对象。
3.通过序列化对象在进程间传递对象。
二、当对象需要被序列化时如何选择所使用的接口
1.在使用内存的时候Parcelable比Serializable的性能高。
2.Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC(内存回收)。
3.Parcelable不能使用在将对象存储在磁盘上这种情况,因为在外界的变化下Parcelable不能很好的保证数据的持续性。
Intent
ContentProvider
Broadcast
AIDL
String是常量
StringBuffer是变量,线程安全
StringBuilder是变量,线程不安全,执行效率高
当字符赋值少使用String
字符赋值频繁使用StringBuilder
多个线程同步操作数据使用StringBuffer
View基于主线程刷新UI
SurfaceView子线程又可以刷新UI
measure----测量 :系统会先根据xml布局文件和代码中对控件属性的设置,来获取或者计算出每个View和ViewGrop的尺寸,并将这些尺寸保存下来。
Layout----布局 :根据测量出的结果以及对应的参数,来确定每一个控件应该显示的位置。
Draw----绘制 :确定好位置后,就将这些控件绘制到屏幕上。
dispatchTouchEvent -> onTouch(setOnTouchListener) -> onTouchEvent -> onClick
onTouch和onTouchEvent的区别
两者都是在dispatchTouchEvent中调用的,onTouch优先于onTouchEvent,如果onTouch返回true,那么onTouchEvent则不执行,及onClick也不执行。
onInterceptTouchEvent()是ViewGroup独有的
View的事件传递顺序有3个重要的方法:
dispatchTouchEvent() 是否消耗了本次事件,
onInterceptTouchEvent() 是否拦截了本次事件,
onTouchEvent() 是否处理本次事件,
滑动冲突分为同方向滑动冲突、不同方向滑动冲突:
例如ScrollView和ListView,同方向滑动冲突,可以计算ListView高度而动态设置ListView的高度,ScrollView高度可变。
例如ViewPager和ListView,不同方向滑动冲突,一个是横向滑动一个是竖直滑动,不同方向滑动可以判断滑动的x,y轴是横向还是竖直滑动,如果判断得到是横向滑动,就拦截ListView的事件,竖则反之。
requestLayout:
从方法名字可以知道,“请求布局”,那就是说,如果调用了这个方法,那么对于一个子View来说,应该会重新进行布局流程。但是,真实情况略有不同,如果子View调用了这个方法,其实会从View树重新进行一次测量、布局、绘制这三个流程,最终就会显示子View的最终情况。
invalidate:
当子View调用了invalidate方法后,会为该View添加一个标记位,同时不断向父容器请求刷新,父容器通过计算得出自身需要重绘的区域,直到传递到ViewRootImpl中,最终触发performTraversals方法,进行开始View树重绘流程(只绘制需要重绘的视图)。
postInvalidate:
postInvalidate是在非UI线程中调用,invalidate则是在UI线程中调用。
Handler,loop轮询检测发送消息到MessagerQuery,MessageQuery对Message入列,Handler回调方法处理消息,重写handMessage方法刷新ui
1、新建状态(new) :新创建一个线程对象。
2、就绪状态(Runnable) :线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得。
3、运行状态(Running) :就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked) :阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
5、死亡状态(Dead) :线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
Thread类中run()和start()方法的区别:
run() 方法:在线程内调用该Runnable对象的run()方法,可以重复多次调用;
start() 方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程;
wait和 sleep 的区别
wait 是Object的方法,是对象锁,锁定方法不让继续执行,执行notify方法后继续执行
sleep 是Thread的方法,sleep使线程睡眠,让出cpu,结束后自动继续执行
五种进程:前台进程,可见进程,服务进程,后台进程,空进程。
它们的回收顺序从先到后分别是:空进程,后台进程,服务进程,可见进程,前台进程。
public class SingleTon (){
private static SingleTon instance = new SingleTon();
private SingleTon (){}
public static Test getInstance(){
return instance;
}
}//饿汉式
public class SingleTon {
private static SingleTon singleTon = null;
private SingleTon() {}
public static SingleTon getInstance() {
if( singleTon == null ) {
synchronized( SingleTon.class ) {
if( singleTon == null ) {
singleTon = new SingleTon();
}
}
}
return singleTon;
}
}//懒汉式
android 的系统架构和其操作系统一样,采用了分层的架构。从架构图看, android 分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和 linux 核心层。
1.使用include复用布局文件。
2.使用merge标签避免嵌套布局。
3.使用stub标签仅在需要的时候在展示出来。
屏幕分辨率
含义:手机在横向、纵向上的像素点数总和
单位:px(pixel),1px=1像素点(UI设计师的设计图会以px作为统一计量单位)
Android手机常见的分辨率:320x480、480x800、720x1280、1080x1920
屏幕像素密度
含义:每英寸的像素点数 单位:dpi(dots per ich)
假设设备内每英寸有160个像素,那么该设备的屏幕像素密度=160dpi
适配方法
1.支持各种屏幕尺寸: 使用wrap_content, match_parent, weight
2.使用相对布局,禁用绝对布局。
3.使用LinearLayout的weight属性
4.使用.9图片
影响内存大小的三要素:图片宽高、色彩空间、缩放比
Bitamp 占用内存大小 = 宽度像素 x (inTargetDensity / inDensity) x 高度像素 x (inTargetDensity / inDensity)x 一个像素所占的内存.
常见的Bitmap优化:
修改主动缩放比:目标是修改最终图片加载的宽高,进而优化内存占用。具体就是设置inSampleSize值,如在适当的View上缩放显示适合的bitmap,实现bitmap的高效加载(Glide图片框架就是这样,让显示组件View的宽高的参与缩放比计算)。
修改被动缩放比:理解了被动缩放比的计算,设备dpi层级和图片dpi层级关系。最好的情况就是同样的图片在drawable文件不同的dpi层级包中都有适应具体大小的图片(这样在运行不同设备时,被动缩放比都是1)。否则在设备没有直接对应的dpi的drawable文件时,会影响被动缩放比,而且大多数时候是导致图片放大,且导致内存增大二次方。如刚才的计算过程如果把图片从drawable-xxhdpi(480dpi)移到drawable文件包(默认160)中,同样的加载代码最后内存就会放大9倍。
修改色彩空间:在明确的不需要透明度的情况,使用RGB_565替换ARGB_8888,可以直接达到内存缩小一半的功效。缺点就是有限定条件。(从网络上的博文描述,不推荐使用ARGB_4444替换,因为这样的图片质量太差)
如何在不压缩的情况下加载高清大图:
使用BitmapRegionDecoder进行布局加载。
帧动画(Frame Animation):
类似于一帧帧图片组成的电影,xml中多张图片组成,在UI线程中播放这个xml形成的动画。
补间动画(Tweened Animation):
分为四种形式,分别是 alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)。补间动画的实现,一般会采用xml 文件的形式;当然也可以用Java代码直接实现。
属性动画(Property Animation):
这是3.0之后加入的动画,为了弥补前面两种动画的不足。属性动画可以实现很多数学函数的路径动画。属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。
重载 :方法名相同,参数不同;
重载返回类型不做限制;
重载用于同一个类的所有方法;
一个方法在所在的类中可以被重载多次
覆盖 :父类和子类有相同的方法和参数;
覆盖要求返回类型必须一致;
覆盖只能用于子类覆盖父类的方法;
父类的一个方法只能被子类覆盖一次
在DEX文件中,method、field、class等的个数使用short类型来做索引,即两个字节(65535),method、field、class等均有此限制。
APK在安装过程中会调用dexopt将DEX文件优化成ODEX文件,dexopt使用LinearAlloc来存储应用信息,关于LinearAlloc缓冲区大小,不同的版本经历了4M/8M/16M的限制,超出
缓冲区时就会抛出INSTALL_FAILED_DEXOPT错误。
解决方案是Google的MultiDex方案
Java对象实例化过程中,主要使用到虚拟机栈、Java堆和方法区。
Java文件经过编译之后首先会被加载到jvm方法区中,jvm方法区中很重的一个部分是运行时常量池,用以存储class文件类的版本、字段、方法、接口等描述信息和编译期间的常量和静态常量。
方法区 :用于存储类结构信息的地方,包括常量池、静态变量、构造函数等。
Java堆(heap) :存储Java实例或者对象的地方。这块是gc的主要区域。
Java栈(stack) :Java栈总是和线程关联的,每当创建一个线程时,JVM就会为这个线程创建一个对应的Java栈。在这个java栈中又会包含多个栈帧,每运行一个方法就创建一个栈帧,用于存储局部变量表、操作栈、方法返回值等。每一个方法从调用直至执行完成的过程,就对应一个栈帧在java栈中入栈到出栈的过程。所以java栈是线程私有的。
程序计数器 :用于保存当前线程执行的内存地址,由于JVM是多线程执行的,所以为了保证线程切换回来后还能恢复到原先状态,就需要一个独立的计数器,记录之前中断的地方,可见程序计数器也是线程私有的。
本地方法栈 :和Java栈的作用差不多,只不过是为JVM使用到的native方法服务的。
强引用 :通常可以认为是通过new出来的对象,即使内存不足,GC进行垃圾收集的时候也不会主动回收。
Object obj = new Object();
软引用 :在内存不足的时候,GC进行垃圾收集的时候会被GC回收。
Object obj = new Object();
SoftReference softReference = new SoftReference<>(obj);
弱引用 :无论内存是否充足,GC进行垃圾收集的时候都会回收。
Object obj = new Object();
WeakReference weakReference = new WeakReference<>(obj);
虚引用 :和弱引用类似,主要区别在于虚引用必须和引用队列一起使用。
Object obj = new Object();
ReferenceQueue referenceQueue = new ReferenceQueue<>();
PhantomReference phantomReference = new PhantomReference<>(obj, referenceQueue);
引用队列:如果软引用和弱引用被GC回收,JVM就会把这个引用加到引用队列里,如果是虚引用,在回收前就会被加到引用队列里。
TCP是经过3次握手,4次挥手完成一串数据的传送
UDP是无连接的,知道IP地址和端口号,向其发送数据即可,不管数据是否发送成功
Socket是一种不同计算机,实时连接,比如说传送文件,即时通讯
java和c语言之间的桥梁,由于java是一种半解释语言,可以被反编译出来,一种重要涉及安全的代码就使用了C编程,再者很多底层功能调用C语言都实现了Java没必要重复造轮子,所以定义了JNI接口的实现
jockeyjs:https://github.com/tcoulter/jockeyjs
对协议进行统一的封装和处理。
1.避免创建不必要的对象,尽可能避免频繁的创建临时对象,例如在for循环内,减少GC的次数。
如果有需要拼接的字符串,那么可以优先考虑使用StringBuffer或者StringBuilder来进行拼接,而不是加号连接符,因为使用加号连接符会创建多余的对象,拼接的字符串越长,加号连接符的性能越低。
当一个方法的返回值是String的时候,通常需要去判断一下这个String的作用是什么,如果明确知道调用方会将返回的String再进行拼接操作的话,可以考虑返回一个StringBuffer对象来代替,因为这样可以将一个对象的引用进行返回,而返回String的话就是创建了一个短生命周期的临时对象。
尽可能地少创建临时对象,越少的对象意味着越少的GC操作。
2.尽量使用基本数据类型代替引用数据类型。
3.静态方法调用效率高于动态方法,也可以避免创建额外对象。
4.对于基本数据类型和String类型的常量要使用static final修饰,这样常量会在dex文件的初始化器中进行初始化,使用的时候可以直接使用。
5.多使用系统API,例如数组拷贝System.arrayCopy()方法,要比我们用for循环效率快9倍以上,因为系统API很多都是通过底层的汇编模式执行的,效率比较高。
6.使用增强型for循环语法
编译打包步骤:
1. 打包资源文件,生成R.java文件
打包资源的工具是aapt(The Android Asset Packaing Tool)(E:\Documents\Android\sdk\build-tools\25.0.0\aapt.exe)。
在这个过程中,项目中的AndroidManifest.xml文件和布局文件XML都会编译,然后生成相应的R.java,另外AndroidManifest.xml会被aapt编译成二进制。
存放在APP的res目录下的资源,该类资源在APP打包前大多会被编译,变成二进制文件,并会为每个该类文件赋予一个resource id。对于该类资源的访问,应用层代码则是通过resource id进行访问的。Android应用在编译过程中aapt工具会对资源文件进行编译,并生成一个resource.arsc文件,resource.arsc文件相当于一个文件索引表,记录了很多跟资源相关的信息。
2. 处理aidl文件,生成相应的Java文件
这一过程中使用到的工具是aidl(Android Interface Definition Language),即Android接口描述语言(E:\Documents\Android\sdk\build-tools\25.0.0\aidl.exe)。
aidl工具解析接口定义文件然后生成相应的Java代码接口供程序调用。如果在项目没有使用到aidl文件,则可以跳过这一步。
3. 编译项目源代码,生成class文件
项目中所有的Java代码,包括R.java和.aidl文件,都会变Java编译器(javac)编译成.class文件,生成的class文件位于工程中的bin/classes目录下。
4. 转换所有的class文件,生成classes.dex文件
dx工具生成可供Android系统Dalvik虚拟机执行的classes.dex文件,该工具位于(E:\Documents\Android\sdk\build-tools\25.0.0\dx.bat)。
任何第三方的libraries和.class文件都会被转换成.dex文件。dx工具的主要工作是将Java字节码转成成Dalvik字节码、压缩常量池、消除冗余信息等。
打包的工具apkbuilder位于 android-sdk/tools目录下。apkbuilder为一个脚本文件,实际调用的是(E:\Documents\Android\sdk\tools\lib)文件中的com.android.sdklib.build.ApkbuilderMain类。
在开发过程中,主要用到的就是两种签名的keystore。一种是用于调试的debug.keystore,它主要用于调试,在Eclipse或者Android Studio中直接run以后跑在手机上的就是使用的debug.keystore。
另一种就是用于发布正式版本的keystore。
7. 对签名后的APK文件进行对齐处理
如果你发布的apk是正式版的话,就必须对APK进行对齐处理,用到的工具是zipalign(E:\Documents\Android\sdk\build-tools\25.0.0\zipalign.exe)
对齐的主要过程是将APK包中所有的资源文件距离文件起始偏移为4字节整数倍,这样通过内存映射访问apk文件时的速度会更快。对齐的作用就是减少运行时内存的使用。
插件化:插件化是体现在功能拆分方面的,它将某个功能独立提取出来,独立开发,独立测试,再插入到主应用中。依次来较少主应用的规模。
热修复:热修复是体现在bug修复方面的,它实现的是不需要重新发版和重新安装,就可以去修复已知的bug。
热修复原理:利用PathClassLoader和DexClassLoader去加载与bug类同名的类,替换掉bug类,进而达到修复bug的目的,原理是在app打包的时候阻止类打上CLASS_ISPREVERIFIED标志,然后在热修复的时候动态改变BaseDexClassLoader对象间接引用的dexElements,这样才能抢先代替Bug类,完成系统不加载旧的Bug类.
插件化:插件化只是增肌新的功能类或者是资源文件,所以不涉及抢先加载旧的类这样的使命,就避过了阻止相关类去打上CLASS_ISPREVERIFIED标志和还有在热修复时动态改变BaseDexClassLoader对象间接引用的dexElements.
所以插件化比热修复简单,热修复是在插件化的基础上在进行替旧的Bug类
23种。
Builder模式(方便维护,使用者可以不用清楚内部构成情况就能直接调用方法;比如系统弹窗)
观察者模式(被观察者与观察者,只要有订阅关系,当被观察者发生改变时,就能通知观察者们;比如Event )
单例模式(一个进程/项目中只存在1个实例,为了节约内存资源)
工厂模式
MVC 是 Model,View,Controller 的缩写,从上图可以看出 MVC 包含三个部分:
… 模型( Model )对象:是应用程序的主体部分,所有的业务逻辑都应该写在该层。
… 视图( View )对象:是应用程序中负责生成用户界面的部分。也是在整个
MVC 架构中用户唯一可以看到的一层,接收用户的输入,显示处理结果。
… 控制器( Control )对象:是根据用户的输入,控制用户界面数据显示及更新
Model 对象状态的部分,控制器更重要的一种导航功能,想用用户出发的相
关事件,交给 M 层处理。
Android 鼓励弱耦合和组件的重用,在 Android 中 MVC 的具体体现如下:
模型层(Model):主要是获取数据功能,业务逻辑和实体模型。
视图层(View):对应于Activity或Fragment,负责视图的部分展示和业务逻辑用户交互
控制层(Presenter):负责完成View层与Model层间的交互,通过P层来获取M层中数据后返回给V层,使得V层与M层间没有耦合。
在MVP中 ,Presenter层完全将View层和Model层进行了分离,
把主要程序逻辑放在Presenter层实现,Presenter与具体的View层(Activity)是没有直接的关联,是通过定义接口来进行交互的,
从而使得当View层(Activity)发生改变时,Persenter依然可以保持不变。
View层接口类只应该只有set/get方法,及一些界面显示内容和用户输入,除此之外不应该有多余的内容。
绝不允许View层直接访问Model层,这是与MVC最大区别之处,也是MVP核心优点。
MVC:PC时代就有的架构方案,在Android上也是最早的方案,Activity/Fragment这些上帝角色既承担了V的角色,也承担了C的角色,小项目开发起来十分顺手,大项目就会遇到
耦合过重,Activity/Fragment类过大等问题。
MVP:为了解决MVC耦合过重的问题,MVP的核心思想就是提供一个Presenter将视图逻辑I和业务逻辑相分离,达到解耦的目的。
MVVM:使用ViewModel代替Presenter,实现数据与View的双向绑定,这套框架最早使用的data-binding将数据绑定到xml里,这么做在大规模应用的时候是不行的,不过数据绑定是
一个很有用的概念,后续Google又推出了ViewModel组件与LiveData组件。ViewModel组件规范了ViewModel所处的地位、生命周期、生产方式以及一个Activity下多个Fragment共享ViewModel数据的问题。LiveData组件则提供了在Java层面View订阅ViewModel数据源的实现方案。
概念:有些对象只有有限的生命周期,当他们的任务完成之后,它们将被垃圾回收,如果在对象的生命周期本该结束的时候,这个对象还被一系列的引用,着就会导致内存泄露。
解决方法:使用开源框架LeakCanary检测针对性解决
常见的内存泄露有:
单例造成的内存泄露,例如单例中的Context生命周期大于本身Context生命周期
线程使用Hander造成的内存卸扣,当activity已经结束,线程依然在运行更新UI
非静态类使用静态变量导致无法回收释放造成泄露
WebView网页过多造成内存泄露
资源未关闭造成泄露,例如数据库使用完之后关闭连接
产生原因:
1.内存中加载的数据量过于庞大,如一次性从数据库中取出过多的数据
2.集合类中有对对象的引用,使用后未清空
3.代码中存在死循环或循环过多产生过多重复的实体对象
4.使用的第三方软件中的bug
5.启动参数内存值设定的过小
解决方案:
1.修改JVM启动参数,直接增加内存(-Xms -Xms参数不要忘记加)
2.检查错误日志查看OutOfMemory错误前是否有其他异常或错误
3.对代码进行分步分析,找出可能发生溢出的位置
出现的原因有三种:
a)KeyDispatchTimeout(5 seconds) 主要类型按键或触摸事件在特定时间内无响应
b)BroadcastTimeout(10 seconds) BoradcastReceiver在特定的时间内无法处理
c)ServiceTimeout(20 seconds) 小概率类型Service在特定的时间内无法处理完成
避免ANR最核心的一点就是在主线程减少耗时操作。通常需要从那个以下几个方案下手:
a)使用子线程处理耗时IO操作
b)降低子线程优先级,使用Thread或者HandlerThread时,调用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)设置优先级,否则仍然会降低程序响应,因为默认Thread的优先级和主线程相同
c)使用Handler处理子线程结果,而不是使用Thread.wait()或者Thread.sleep()来阻塞主线程
d)Activity的onCreate和onResume回调中尽量避免耗时的代码
e)BroadcastReceiver中onReceiver代码也要尽量减少耗时操作,建议使用intentService处理。intentService是一个异步的,会自动停止的服务,很好解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题
1.节制的使用Service,当启动一个Service时,系统总是倾向于保留这个Service依赖的进程,这样会造成系统资源的浪费,可以使用IntentService,执行完成任务后会自动停止。
2.当界面不可见时释放内存,可以重写Activity的onTrimMemory()方法,然后监听TRIM_MEMORY_UI_HIDDEN这个级别,这个级别说明用户离开了页面,可以考虑释放内存和资源。
3.避免在Bitmap浪费过多的内存,使用压缩过的图片,也可以使用Fresco等库来优化对Bitmap显示的管理。
4.使用优化过的数据集合SparseArray代替HashMap,HashMap为每个键值都提供一个对象入口,使用SparseArray可以免去基本对象类型转换为引用数据类想的时间。
省电优化、内存优化、网络优化、图片优化、UI优化。
UI优化:
(1)减少层级,合理使用 RelativeLayout 和 LinerLayout,合理使用Merge,Include。
(2)提高显示速度,使用 ViewStub,它是一个看不见的、不占布局位置、占用资源非常小的视图对象。
(3)布局复用,可以通过标签来提高复用。
(4)尽可能少用wrap_content,wrap_content 会增加布局 measure 时计算成本,在已知宽高为固定值时,不用wrap_content 。
(5)删除控件中无用的属性。
省电优化:
(1)避免浮点运算。
(2)根据客户端图片的大小要求叫UI做相应大小的图提供给服务器,避免过大消耗更多流量和电量。
(3)不用的广播,服务记得及时关闭。
内存优化:
(?)增加相应的判断,以及异常处理。
(?)避免在主线程做耗时操作。
(1)对象引用:强引用、软引用、弱引用、虚引用四种引用类型,根据业务需求合理使用不同,选择不同的引用类型。
(2)减少不必要的内存开销:注意自动装箱,增加内存复用,比如有效利用系统自带的资源、视图复用、对象池、Bitmap对象的复用。
(3)使用最优的数据类型:比如针对数据类容器结构,可以使用ArrayMap数据结构,避免使用枚举类型,使用缓存Lrucache等。
(4)图片内存优化:点9图减少图片大小以及可以设置位图规格,根据采样因子做压缩,用一些图片缓存方式对图片进行管理等。
APK瘦身:安装包小可以降低用户的安装成本。
(1)做混淆优化代码。
(2)删除无用的代码及图片相应的本地库。
(3)Lint优化。
(4)zip压缩。
|| 和 | 都表示“或”,区别是 || 只要满足第一个条件,后面的条件就不再判断。 | 要对所有的条件进行判断
&& 和 & 都表示“与”,区别是&&只要第一个条件不满足,后面条件就不再判断。&要对所有条件进行判断
==是判断两个变量或实例是不是指向同一个内存空间
equals是判断两个变量或实例所指向的内存空间的值是不是相等