Intent intent = new Intent(MainActivity.this, TestActivity.class);
startActivity(intent);
Intent intent = new Intent();
intent.setAction("com.dev.test");
intent.addCategory("com.dev.testcat");
startActivity(intent);
android:process = “:jincheng”
android:process = “www.wjb.jincheng”
假设包名为www.wjb则第一个进程名为www.wjb:jincheng第二个进程名为ww.wjb.jincheng,切以“:”开头的为当前应用的私有进程,其他应用的组件不能与它跑在同一进程中,不以“:”开头的进程为全局进程,其他应用通过ShareUID方式可以和它跑在同一个进程中。
val lp = btn_test.layoutParam
lp.width+=100lp.height+=100
btn_test.requestLayout()
//或者
//btn_test.layoutParams = lp
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
onUserInteraction 每当Key,Touch,Trackball事件分发到当前Activity就会被调用。如果你想当你的Activity在运行的时候,能够得知用户正在与你的设备交互,你可以override该方法。
这个回调方法和onUserLeaveHint是为了帮助Activities智能的管理状态栏Notification;特别是为了帮助Activities在恰当的时间取消Notification。所有Activity的onUserLeaveHint 回调都会伴随着onUserInteraction。这保证当用户相关的的操作都会被通知到,例如下拉下通知栏并点击其中的条目。注意在Touch事件分发过程中,只有Touch Down 即Touch事件的开始会触发该回调,不会在move 和 up 分发时触发(从Activity 源码中 dispatchTouchEvent 方法中确实是这么做的)。
onUserLeaveHint作为Activity的生命周期回调的部分,会在用户决定将Acitivity放到后台时被调用。例如:当用户按下Home键,onUserLeaveHint就会被调用。但是当来电话时,来电界面会自动弹出,onUserLeaveHint就不会被调用。当该方法被调用时,他会恰好在onPause调用之前。
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
mDecor就是顶级View---->DecorView.也就是Activity中通过setContentView所设置的View,顶级View也叫根View,一般都是ViewGroup
((ViewGroup)getWindwo.getDecorView().findViewById(android.r.id.content)).getChildAt(0)可以获取到Activity设置的View
顶级View对点击事件的分发过程
点击事件到达顶级View,会调用ViewGroup的dispatchTouchEvent方法,其逻辑为:如果顶级ViewGroup拦截事件,也就是onInterceptTouchEvent返回true,则事件由ViewGroup处理,如果这个ViewGroup设置了OnTouchListener,则onTouchEvent会被调用,否则,onTouchEvent会被调用。也就是说如果都提供的话,onTouch会屏蔽掉onTouchEvent,因为设置了监听优先级更高。在onTouchEvent中,如果设置了longClickListener,则onClick会被调用。如果顶级ViewGroup不拦截事件,则事件会传递给它事件链上的子View,这个时候子View的dispatchTouchEvent会被调用,如此循环。
@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {
.....
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.
final boolean intercepted;if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags &
FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
// restore action in case it was
changed } else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an
initial down
// so this view group continues to intercept touches.
intercepted = true;
}
.....
}
ViewGroup content = (ViewGroup)fingdViewById(android.id.content)
如何拿到我们设置的View的方式如下
content.getChildAt(0)
通过源码可以指定DecorView其实是一个FrameLayout,View层的时间都先经过DecorView,然后才传递到我们的View。
View的onMeasure的源码
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec));
}
setMeasuredDimension(int measuredWidth, int measuredHeight)方法会设置View的宽高测量值。观察
getDefaultSize方法。
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
protected int getSuggestedMinimumHeight() {
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
以getSuggestedMinimumWidth为例,如果View没有设置背景,则返回值View的宽度为mMinWidth
,mMinWidth对应的是android:MinWidth这个属性设置的值,因此View的宽度即为android:MinWidth属性指定的值。如果这些属于没有指定,则值为0;如果指定了背景,则View的宽度为max(mMinWidth, mBackground.getMinimumWidth())。mMinWidth的意义我们已经知道,mBackground.getMinimumWidth()源码为:(Drawable)
public int getMinimumHeight() {
final int intrinsicHeight = getIntrinsicHeight();
return intrinsicHeight > 0 ? intrinsicHeight : 0;
}
getMinimumWidth返回的是Drawable的原始高度,如果没有高度则返回0。比如ShapeDrawable无原始宽高,BitmapDrawable有原始宽高(图片的尺寸)
*getSuggestedMinimumWidth()和getSuggestedMinimumHeight()的返回值就是View在UNSPECIFIED情况下测量的宽高。
ViewGroup的measure
ViewGroup提供了measureChildren方法
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
对每个子元素进行了mesure,通过measureChild()方法
protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec =
getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec =
getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
该方法就是取出子元素的LayoutParams,然后再通过getChildMeasureSpec()方法创建子元素的MeasureSpec,最近再将MeasureSpec传递给View的 measure()方法进行测量。
1.绘制背景(background.graw(canvas))
2.绘制自己(onDraw)
3.绘制children(dispathDraw)
4.绘制装饰(onDrawScrollBars)
1.继承View重写onDraw方法
2.继承ViewGroup派生的特殊Layout
3.继承特点的View(如TextView)
4.继承特点的ViewGroup(如LinearLayout)
1.让View支持warp_content
2.如果有必要,支持padding
3.尽量不要再View中使用Handler,没必要
4.View中如果有线程或者动画,需要及时停止
5.View带有滑动嵌套情形时,需要处理好滑动冲突
BitmapDrawable 表示一张图片,也可以通过XML的方式描述以设置更多的效果(可以可以代表一张.9图)
以下是各个属性的含义:
ShapeDrawable可以理解为颜色构造的图形,可以是纯色,也可以是渐变色
以下是各个属性的含义:
LeveIListDrawable 对应标签,一个Drawable集合,没一个Drawable都有一个对应等级(level)的概念,根据不同的等级切换对应的Drawable。(ImageView有个方法setImageLevel可以来切换Drawable)
TransitionDrawable 对应标签,用于实现两个Drawable之间的淡入淡出效果。
InsetDrawable 对应标签,可以将其他的Drawable内嵌到自己当中,并可以在四周流出一定的间距。(面试题:为一个充满整个屏幕的LinearLayout布局指定背景图,是否可以让背景图不充满屏幕?请用代码描述实现过程。解决此题,可以使用嵌入(Inset)图像资源来指定图像,然后像使用普通图像资源一样使用嵌入图像资源。)
ScaleDrawable 对应标签,可以根据自己的等级(level)将指定的Drawable缩放到一定比例。
ClipDrawable 对应标签,可以根据自己的等级(level)来裁剪另一个Drawable。
可以分为View动画和属性动画,属性动画API11后才支持,之前的版本可通过兼容库使用属性动画。
平移动画标签对应
以下是各个属性的含义:
缩放标签对应
以下是各个属性的含义:
旋转标签对应 以下是各个属性的含义: 透明度标签对应 还有一些常用属性,比如android:duration,表示动画持续时间,android:fillArter,表示动画结束后View是否停留在结束位置。 自定义View动画主要是矩阵变化的过程 作用于ViewGroup,指定一个动画,子元素的出场就都会具有这种动画。ListView的每个item的动画就是这种方式。 Activirty有默认的切换效果,这个效果我们也可以自定义。通过overridePendingTransition(enterAnim: Int, exitAnim: Int)这个方法实现。通过overridePendingTransition方法必须在StartActivity或者finsh方法后面,否则动画将不起作用 常用的动画类有:ValueAnimator,ObjectAnimator,AnimatorSet。ObjectAnimator继承与ValueAnimator。AnimatorSet是动画集合。属性动画也可以通过代码动态实现和XML定义。 插值器是根据时间的流逝的百分比计算当前属性改变的百分比 **属性动画原理原理:**属性动画要求动画作用的对象提供该属性的get,set方法,属性动画根据外界传递过来的初始值和最终值,以动画的效果多次去调用set方法,每次传递的set值都不一样,随着时间的推移,传递的值越来越接近最终值。同时这个对象的属性必须能够通过某种方式映射出来,比如带来UI的改变之类(如果不满足,动画无效果,但是不是Crash) 1.OOM,主要是帧动画 Window是一个抽象类,具体实现是PhoneWindow FLAG_NOT_FOCUSABLE FLAG_NOT_TOUCH_MODAL FLAG_SHOW_WHEN_LOCKED Type代表Windw的类型,Window有3重类型。应用Window,子Window,系统Window。 Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl建立联系,因此Window并不是实际存在的,它是以View的形式存在。WindowManager的三个接口,addView,updateViewLayout,removeView都是针对View的。 WindowManager是一个接口,真正的实现是WindowManagerImpl。 WindowManagerImp并没有处理Winodw的三大操作,而是交给了WindowManagerGlobal(mGlobal)处理。 是典型的桥接模式。 mViews存储的是所有的Window所对应的View 3.通过ViewRootImpl来更新界面并完成Window的添加过程 这个步骤由ViewRootImpl的setView方法完成。View的绘制过程全部是由ViewRootImpl完成,addView过程也不例外,setView内部有个requestLayout(),通过这个方法完成一部刷新请求。 scheduleTraversals()方法实际是View的绘制入口。 WindowManagerGlobal的removeView通过findViewLocked先找到待删除View的索引,查找过程就是建立数组的遍历。然后再通过removeViewLocked方法做进一步的删除。 removeViewLocked是通过ViewRootImpl来完成删除操作的。WindowManager中提供了两个接口,removeView和removeViewImmediate,分别表示异步删除和同步删除(Immediate,立即的) die只是发送了一个请求删除的消息后,就立刻返回了。这个时候View并没有完成删除操作,所以最后悔将其添加到mDyingViews中,mDyingViews表示带删除的列表。 WindowManagerGlobal的updateViewLayout完成更新。 通过setLayoutParams方法更新LayoutParams。 ViewRootImpl中会通过scheduleTraversals()方法对View重新测量,布局,绘制。 Acctivity的启动过程最终会有ActivityThread中的 performLaunchActivity()方法来完成启动过程,在这个方法内部会通过调用类加载器创建Activity的实例对象,并调用attach方法为其关联运行过程 attach方法中,系统会创建Activity附属的Window对象并为其设置回调接口,Window是通过PolicyManager的makeNewWindow方法实现的。 可以看出,Activity将具体实现交给了Window。而Window的具体实现是PhoneWindow。PhoneWindow的setContentView遵循以下步骤: Activity的onContentChanged是个空实现,我们可以在子Activity中处理这个回调。 遵循如下几个步骤: DilaLog的上下文必须采用Activity的Context,不能使用Application,是因为没有应用token导致的。系统Window比较特殊,可以不需要token。 Toast因为有定时取消功能,所以内部使用了Handler,且有两类IPC过程。 除了BroadcastReceiver以外,另外三大组件必须在AndroidManifest中注册,BroadcastReceiver可以在AndroidManifest中注册也可以在代码中注册。 startActivity()方法有好几种重载方式(好像只看到两种),但是最终都是调用startActivityForResult 上面代码里的参数mMainThread.getApplicationThread()的类型的ApplicationThread,ApplicationThread是ActivityThread的内部类,ApplicationThread和ActivityThread在Activity的启动过程中发挥着很重要的作用。 启动Activity真正的实现由ActivityManager.getService().startActivity方法来完成。(书中说是由ActivityManagerNative.getDefault()完成,可能是Android系统更新后改了方法。但是内部源码与书中介绍大致相同,getDefault()就是我看到源码中的getService()方法。) 可以看出是由AMS 里的startActivity方法进行Activity的启动的(ActivityManager.getService().startActivity)。 可以看到熟悉的"Unable to find explicit activity class "+ ((Intent)intent).getComponent().toShortString()+ "; have you declared this activity in your 看出Activity的启动又交给了startActivityAsUser()方法了。(由于我电脑的源码与书中不一致,我的是API28,应该是被更改过,先看书中源码,笔记中就不再贴出源码,大致过程更改的应该不会太多,先理解书中的)由于源码不同,大致情况是启动Activity的任务在ActivityStackSupervisor和ActivityStack中不断的传递,最后Activity的启动过程回到了ApplicationThread。ApplicationThread是主线程,也就是UI线程ActivityThread的内部类。 最后由ApplicationThread的scheduleLaunchActivity方法来启动Activity,scheduleLaunchActivity的实现非常简单,就是发送了一个启动Activity的信息交给Handler处理。这个Handler就是之前所知道的H。发送的消息就是H.LAUNCH_ACTIVITY。在H中,对消息LAUNCH_ACTIVITY的处理方式就是调用ActivityThread的handleRelaunchActivity方法。 handleLaunchActivity方法调用performLaunchActivity()方法最终来完成Activity的启动。 该小段附近的源码均为performLaunchActivity()方法的源码 2.通过Instrumentation的newActivity方法使用类加载器创建Activity对象 3.通过LoadedAPK的makeApplication方法来尝试创建Application对象 可以看出,如果Application已经存在,就不会创建了这就意味着一个应用只有一个Application。 4.创建ContextImpl对象,并通过Activity的attach方法来完成一些重要数据的初始化 ContextImpl是一个很重要的数据结构,是Context的具体体现,Context的大部分逻辑都是由ContextImpl来完成的。ContextImpl是通过Activity的attach来个Activity建立关联的,除此之外,在attach方法中还会完成Window的创建,并建立自己和Window的关联,这样当Window接收到外部输入事件后就可以将事件传递给Activity。 不管通过哪个startActivity()方法最终都是调用startActivityForResult方法开启动Activty,在startActivityForResult方法内部会调用Instrumentation.execStartActivity方法,在execStartActivity方法中会调用ActivityManagerNative.getDefault().startActivity,也就是ActivityManagerService(AMS)的startActivity方法。在startActivity方法中,启动Activity的任务在ActivityStackSupervisor和ActivityStack中不断的传递,最后Activity的启动过程回到了ApplicationThread,由ApplicationThread的scheduleLaunchActivity方法发送一个消息H.LAUNCH_ACTIVITY给Handler,H接收到信息后,调用ActivityThread的handleRelaunchActivity方法,handleRelaunchActivity方法内部调用performLaunchActivity()方法最终完成Activity的启动。 Service分为两种工作状态,一种是启动状态,用于执行后台计算,一种是绑定状态,主要用于其他组件和Service的交互。两种状态是可以共存的。 mBase就是Context(ContextImpl)。Activity被创建的时候attach就会将一个ContextImpl对象关联起来,这个ContextImpl就是mBase。 通过ActivityManager.getService().startService来启动了一个服务(和Activity一样,书中是ActivityManagerNative.getDefault().startService) 所以是由AMS的mServices对象完成了后续的启动过程。mServices的类型是ActiveServices,是一个辅助AMS进行Service管理的类,包括Service的启动绑定和停止等。 与Activity启动过程类似,也是发送了一个H.CREATE_SERVICE信息给Handler H来完成。H对这个消息的处理就是调用handleCreateService()方法。 这个时候Service已经启动了,这个时候ActivityThread会通过handleServiceArgs方法调用Service的onStartCommand方法。 与Service的启动过程相似,也是通过ContextImpl的bindServiceCommon完成。 由Activity的启动过程分析知道mServices其实是ActiveServices,在AMS的bindService方法中调用了ActiveServices的bindServiceLocked方法。与前面的类似,在bindServiceLocked方法中经过几次传递,最后还是回到了ApplicationThread中,通过scheduleBindService方法发送了一个H.BIND_SERVICE H接收到消息后,交给ActivityThread的handleBindService方法处理,handleBindService方法内部会根据Service的token取出Service对象,然后调用Service的onBind方法,完成绑定过程。(onBind方法会返回一个Binder对象给客户端使用) mServices是ActiveServices早就知道了,publishServiceLocked方法最核心的一句代码是c.conn.connected(r.name, service, false); 静态注册的广播在应用安装时由系统自动完成注册,具体来说是有PMS(PackageManagerService)完成,其他三大组件也都是在应用安装时由PMS完成解析并注册的。 以下分析动态广播注册的过程: ContextImpl直接向AMS发送了一个异步请求用户发送广播,在ASM的broadcastIntent方法中调用了broadcastIntentLocaked方法,在broadcastIntentLocaked方法内部会根据intent-filter查找匹配的广播接收者,并且经过一系列的条件过滤,最终会将满足条件的广播接受者添加到BroadcastQueue中,接着BroadcastQueue会将广播发送给相应的广播接收者。 当进程启动时,ContentProvider会同时启动,并发布到AMS中,ContentProvider的onCreate要先与Application的onCreate,这是四大组件中少有的现象。 当一个应用启动时,入口方法为main方法,main方式是一个静态方法,在main方法中会创建ActivityThread的实例,并创建主线程的消息队列,然后在ActivityThread中的attach方法中会远程调用AMS的attachApplication方法,并将ApplicationThread提供给AMS。ApplicationThread是一个Binder对象,它的接口是IApplicationThread,主要用于ActivityThread和AMS通信。ActivityThread会创建Application对象和并加装ContenProvider,ActivityThread会先加载ContentProvider,然后再调用Application的onCreate方法。 ContentProvider一般是单例的,到底是不是单例一般是通过它的android:mulitprocess属性决定,为false的时候是单例。 ContentProvider的四个方法任何一个都可以触发ContentProvider的启动过程。 AMS会先启动目标ContentProvider所在的进程,然后再启动ContentProvider。AMS的attachApplication方法调用attachApplicationLocked方法,attachApplicationLocked又调用ActivityThread的bindApplication,这是个进程间调用。ActivityThread的bindApplication会发送一个BIND_APPLICATION类型的消息给mH,mH是一个Handler,它接收到消息后会调用ActivityThread的handleBindApplication方法。handleBindApplication方法完成了Application的创建以及ContentProvider的创建。可以分为以下四个步骤: 1.创建ContextIml和Instrumentation MessageQueue 消息队列,内部是采用单链表的数据结构来存储信息的列表 Handler的主要作用是将一个任务切换到某个指定的线程中去执行。为什么只主线程才能更新UI呢?因为在ViewRootImpl中有如下代码对UI操作做了验证: 为什么不允许在子线程访问UI Looper是运行在创建Handler所在的线程中的,所以Handler的业务逻辑就被切换到了创建Handler所在的线程中执行了。 Handler的post方法将一个Runable投递到Handler内部的Looper去处理。(也可以通过send的一系列方法发送,post最终也是通过send完成发送的) ThreadLocal是一个线程内部的数据存储类,是一个泛型类,通过它可以在指定的线程中存储数据,数据存储后只有在指定的线程中可以获得存储的数据。日常开发中ThreadLocal使用的很少,某些特殊的情况下,如Looper,ActivityThread,AMS中使用到了。 ThreadLocal的作用就是当某些数据是以线程为作用域,且不同线程具有不同的数据副本的时候,就可以考虑采用 ThreadLocal。ThreadLocal还有一个使用场景就的复杂逻辑下的对象传递,比如监听器的传递 不同的线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自的的线程中取出一个数组,然后在从数组中根据当前ThreadLocal的索引去查找队友的value值。显然不同线程中的数组是不同的,这就是为什么通过ThreadLocal可以在不同的线程中维护一套数据副本且彼此互不干扰 ThreadLocal内部会从各自的的线程中取出一个数组,通过getMap(Thread t) 消息队列主要有两个操作:插入和读取。读取本身对伴随着删除操作。插入和读取对应的方法分别为enqueueMessage()和next() Looper.loop()方法调用后,消息循环系统才会真的起作用。 Handler的主要工作是负责消息的发送和接收过程。消息的发送可以通过post和send的一系列方法来实现。post的一系列方法最终是通过send的一系列方法实现的。 dispatchMessage(Handler)处理消息过程如下: 2.检查mCallback是否为null,不为null就调用mCallback的handleMessage()方法来处理消息。mCallback的类型是Callback,且是个接口。 Android的主线程就是ActivityThread,主线程的入口是main()方法,通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()方法开启主线程循环。 ActivityThread需要一个Handler来和消息队列交互,这个Handler就是H,H里面定义了一组消息类型,主要包含了四大组件启动和停止等过程。 AsyncTask,HandlerThread,IntentService本质都是线程。 线程是操作系统调度的最小单元,也是一种受限的系统资源,也就是说线程不能无限制的产出,线程的创建和销毁都有相应的开销。 主线程也叫UI线程就是ActrivityThread,里面有个main方法,里面会初始化Looper,和执行loop方法。 HandlerThread继承Thread,只是在run方法里创建了looper,并调用了loop()方法。(Looper的构造方法源码中,会创建一个消息队列(MessagerQueue)) 由于HandlerThread的run方法是一个无限循环(因为调用了Looper.loop()),所以当确定不再需要使用HandlerThread的时候应该通过它的quit或者quitSafely方法终止线程的执行。 IntentService是一种特殊的Service,继承Service并且是一个抽象类,因此必须创建它的子类才能使用IntentService。IntentService可用于执行后台任务,任务执行完成后悔自动停止。 IntentService第一次被启动的时候,调用用onCreate()方法,内部会创建一个HandlerThread ,然后通过HandlerThread 的Looper去创建一个ServiceHandler对象mServiceHandler。这样通过mServiceHandler 线程池的优点: 比较常用的构造方法如下 corePoolSize 线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使他们处于闲置状态。(当ThreadPoolExecute的allowCoreThreadTimeOut属性设置为true的时候,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepAliveTime所指定。当等待时间超过keepAliveTime所指定的时间后,核心线程就会被终止) ThreadPoolExecute执行任务遵循的规则 Android有常见的4中线程池,它们都是直接或者间接的通过配置ThreadPoolExecute来实现自己的特性 作用: 是一个线程数量固定的线程池,当线程处于空闲状态时,它们不会被回收,除非线程池被关闭了。当所有任务都处于活动状态时,新任务都会处于等待状态,知道有线程空闲出来。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,意味着它能更加快速地相应外界的请求。通过源码可以看到它只有核心线程,(核心线程数等于最大线程数)并且核心线程没有超时机制,另外任务队列也是没有限制大小的。 作用: 是一种线程数量不固定的线程池(因为Integer.MAX_VALUE是一个很大很大的数, 作用: 核心线程数量是固定的,非核心线程的数量是没有限制的。并且非核心线程限制时会被回收。主要用于执行定时任务和具有固定周期的重复任务 作用: 只有一个核心线程。它确保所有的任务都在同一个线程中按顺序执行,SingleThreadExecutor 特征: 作用: 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。 特征: BitmapFactory提供了4个方法加载图片: 优先级:内存>存储设备>网络 1.图片压缩功能的实现(BitmapFactory.Option,inSampleSize)
以下是各个属性的含义:
7.1.2自定义View动画
7.2 View动画的特殊使用场景
7.2.1 LayoutAnimation
7.2.2 Activity的切换效果
7.3 属性动画
XML需要定义在res\animator目录下
以下是各个属性的含义:
7.3.1插值器和估值器
估值器根据当前属性改变的百分比计算改变后的属性值7.3.2对任意属性做动画
7.4 使用动画的注意事项
2.内存泄露,属性动画有一类无限循环动画,需要及时停止。View 动画不存在此问题
3.兼容性问题,3.0以下的系统有兼容性问题
4.View动画的问题。View动画是对View的影像做动画,并不是改变View的状态,因此有时候会出现动画完成后,View影像无法隐藏的问题,需要调用view.clearAnimation()清除View动画解决问题
5.不要使用PX,尽量使用DP
6.动画元素交互,3.0之前的系统不管是属性动画还是View动画,新位置均无法触发单机事件,同时老位置可以。3.0后属性动画可以,View动画任然只有原位置可以触发单机效果
7.硬件加速,建议开启,提升动画流畅性。8.理解Window和WindowManager的场景Flag
WindowManager是外界访问Window的入口,Window的具体实现位于WindowManagerServicer中
WindowManager和WindowManagerServer的交互是一个IPC过程
Android中所有的视图都是通过Window来呈现的,不管是Activity还是Dialog,Toast,他们的视图都是依附在Window上的,因此Window实际上是View的直接管理者。就连Activity的setContentView在底层也是通过Window来完成的( getWndow())public void setContentView(@LayoutRes int layoutResID) {
getWndow().setContentView(layoutResID);
initWindowDecorActionBar();
}
8.1WindowManager的常见Flags
Constant Value: 8 (0x00000008)
设置之后window永远不会获取焦点,所以用户不能给此window发送点击事件
焦点会传递给在其下面的可获取焦点的window
这个flag同时会启用 FLAG_NOT_TOUCH_MODAL flag , 不管你有没有手动设置
设置这个flag同时表明了这个window不会和软键盘交互,
(这句话的翻译我不知道对不对)所以window会独立于激活的软键盘之上(这句话的意思就是window会在Z轴上置于输入法之上,所以window可以全屏使用来覆盖住输入法,你可以使用 FLAG_ALT_FOCUSABLE_IM 来修改这个行为)
Constant Value: 32 (0x00000020)
即使这个window是可获取焦点的,
也允许window之外点击事件传递给其他在其之后的window
如果不设置这个值,则window消费掉所有点击事件,不管这些点击事件是不是在window的范围之内
//如果要做悬浮框,我想这个flag肯定得设置,但api>=23就别想了这个flag简而言之就是说,当前window区域以外的点击事件传递给下层window,当前window区域以内的点击事件自己处理
Constant Value: 524288 (0x00080000)
一个特殊的flag,使得window可以在锁屏状态下显示
这个flag会使得window比keyguard或其他锁屏界面具有更高的层级
可以配合FLAG_KEEP_SCREEN_ON使用,点亮屏幕,在显示keyguard window之前显示你的window.
可以配合FLAG_DISMISS_KEYGUARD使用来自动解锁没密码的keyguards
这个flag只能应用在最顶层的全屏window上用人话说就是可以让window显示在锁屏界面上8.2WindowManager的Type
Window是分层的,每个Window都有对应的z-ordered,层级大的会覆盖层级小的Window的上面。
应用Window对应着一个Activity,层级在1~99
子Window不能单独存在,需要附属在特定的父子Window中,比如Dialog,层级在1000~1999
系统Window是需要声明权限才能创建的系统Window,比如Toast和系统状态栏,层级在2000~29998.3Window的内部机制
8.3.1Window的添加过程
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(),
mParentWindow);
}
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
WindowManagerGlobal的addView分为以下几步:
1.检查参数是否合法,如果是子Window那么还需要调整一些布局参数
2.创建ViewRootImpl并将View添加到列表中private final ArrayList
mRoots存储的是所有Window所对应的ViewRootImpl
mParams存储的是所有Window所对应的布局参数
在addView中,通过以下方式将Window的一些列对象添加到列表中root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView);
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
在steView方法中,会通过WindowSession最终来完成Window的添加过程。
mWindowSession的类型是IWindowSession,是一个Binder对象,真正的实现类是Session。所以Window的添加过程是一次IPC调用。
Session内部会通过WindowManagerService来实现Window的添加。8.3.2Window的删除过程
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " +view + " but the ViewAncestor is attached to " + curView);
}
}
一般来说都是异步。异步删除由ViewRootImpl的die方法完成。private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
die方法内部只是做了简单判断,如果是异步删除,就发送一个MSG_DIE的消息,ViewRootImpl的Handler会处理此消息,并调用doDie方法,如果是同步删除,就不发生消息,直接调用doDie方法。
doDie方法内部会调用dispatchDetachedFromWindow方法,也是实现真正移除View的操作。dispatchDetachedFrom2Window做了以下4件事:
1.垃圾回收相关工作,比如清除数据和消息,移除回调
2.通过Session的remove方法移除Window代码为mWindowSession.remove(mWindow);,这同样是一个IPC过程。最终会调用WindowManagerService的removeView方法。
3.调用View的dispatchDetachedFromWindow方法
4.调用WindowManagerGlobal的doRemoveView方法刷新数据。包括mRoots,mParams以及mDyingViews,需要将当前Winodw所关联的这三类对象从列表中删除。8.3.3Window的更新过程
view.setLayoutParams(wparams);
ViewRootImpl还会通过WindowSession来更新Winodw视图。这个过程最终也是由WindowManagerService的relayoutWindow()来具体实现,同样是一个IPC过程。8.4Window的创建过程
8.4.1 Activity的winodw创建过程
activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback);
Activity的视图由setContent方法提供 public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
1.如果没有DecorView就创建,由installDecor()方法完成创建
2.将View添加到DecorView的mContntParent中。(Activity的setContentView只是将Activity的布局文件添加到DecorView的mContntParent中)
3.回调Activity的 onContentChanged方法通知Activity视图已经发生改变。public void onContentChanged() {
8.4.2 Dialog的winodw创建过程
1.创建Window
创建后的实际对象其实就是PhoneWindow。
2.初始化DecorView,并将Dialog的视图添加到DecorView中
和Activity相似,也是通过Window的setContentView方法
3.将DecorView添加到Window中
Dialog的show方法会通过WindowManager将DecorView添加到Window中mWindowManager.addView(mDecor, l);
8.4.2 Toast的winodw创建过程
Toast的显示也隐藏都是通过NMS实现。9.四大组件的工作过程
Activity,Service,BroadcastReceiver需要借用Intent调用,ContentProvider不需要
Activity是用户可以感知的,其他三大组件对用户来说是不可感知的。
Service组件有两种运行状态,绑定状态和启动状态,Activity只有一种运行启动状态。尽管Service组件是用于执行后台计算的,但是它本身是运行在主线程中的,所以耗时的后台计算任然需要在单独的线程中去完成。处于绑定状态的Service可以很方便的和外界进行通信。
BroadcastReceiver是一种消息型组件。用于在不同组件,甚至不同应用中进行消息传递。静态注册是在AndroidManifest中注册,这种广播在应用安装的时候就会被系统解析,动态注册是在代码中通过Context.RegisterRecever()实现,并且在不需要的时候需要通过Context.unRegisterRecever()来解除广播,动态注册的广播必须在应用启动才能注册和接收广播。BroadcastReceiver可以用来实现低耦合的观察者模式。由于BroadcastReceiver的特性,不适合执行耗时操作,BroadcastReceiver一般来说不需要停止,也没有停止的概念。
BroadcastReceiver不适合执行耗时操作原因有二:
1.BroadcastReceiver 一般处于主线程。 耗时操作会导致 ANR
2.BroadcastReceiver 启动时间较短。 如果一个进程里面只存在一个 br组件。并且在其中开启子线程执行耗时任务。 系统会认为该进程是优先级最低的 空进程。很容易将其杀死。
那么如何在BroadcastReceiver中执行耗时操作呢,一般有两种方式:
1.在当前BroadcastReceiver中另起线程操作
2.由当前BroadcastReceiver启动新的Service,在新的Service中操作(第一种方法并不推荐。因为大家都知道,安卓在内存不足或其他资源不够的情况下会作清理。而BroadcastReceiver在onReceive()调用后,就只剩下一个线程在跑了,没有service的级别高!)
ContentProvider是一种数据共享型组件,用于向其他组件甚至其他应用共享数据,内部需要实现增删改查四种操作,它内部维持这一份数据集合。内部的增删改查操作需要维护好线程同步,因为这个几个方法是在Binder线程池中被调用的。ContentProvider组件也不需要手动停止。9.1 Activity的工作过程
9.1.1 Activity的启动过程分析
(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options)方法。Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( his, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);
Instrumentation的execStartActivity方法源码如下:public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token,
Activity target, Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i
ActivityManager.getService().startActivity的到的其实是一个IActivityManager类型的Binder对象。因此他的具体实现是AMS,可以发现,AMS的这个Binder对象是一个单例模式对外提供(Singleton)public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton
重新看一下Instrumentation的execStartActivity方法源码。在ActivityManager.getService() .startActivity方法下面有有个checkStartActivityResult(result, intent);方法。看方法名也知道,是检查启动Activity的结果。public static void checkStartActivityResult(int res, Object intent) {
if (!ActivityManager.isStartResultFatalError(res)) {
return;
}
switch (res) {
case ActivityManager.START_INTENT_NOT_RESOLVED:
case ActivityManager.START_CLASS_NOT_FOUND:
if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
throw new ActivityNotFoundException( "Unable to find explicit activity class " +
((Intent)intent).getComponent().toShortString() + "; have you declared this activity in
your AndroidManifest.xml?");
throw new ActivityNotFoundException( No Activity found to handle " + intent);
case ActivityManager.START_PERMISSION_DENIED:
throw new SecurityException("Not allowed to startactivity " + intent);
case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
throw new AndroidRuntimeException( "FORWARD_RESULT_FLAG used while also requesting a result");
case ActivityManager.START_NOT_ACTIVITY:
throw new IllegalArgumentException( PendingIntent is not an activity");
case ActivityManager.START_NOT_VOICE_COMPATIBLE:
throw new SecurityException( "Starting under voice control not allowed for:
" + intent);
case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:
throw new IllegalStateException("Session calling startVoiceActivity does not
match active session");
case ActivityManager.START_VOICE_HIDDEN_SESSION:
throw new IllegalStateException( "Cannot start voice activity on a hidden
session");
case ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION:
throw new IllegalStateException( Session calling startAssistantActivity does
not match active session");
case ActivityManager.START_ASSISTANT_HIDDEN_SESSION:
throw new IllegalStateException( "Cannot start assistant activity on a hidden
session");
case ActivityManager.START_CANCELED:
throw new AndroidRuntimeException("Activity could not be started for " + intent); default:
throw new AndroidRuntimeException("Unknown error code " + res + " when starting " + intent);
}
}
AndroidManifest.xml?"就是启动的Activity没有在AndroidManifest中注册时抛出的异常。
继续看AMS 里的startActivity方法是如何进行Activity的启动的(ActivityManager.getService().startActivity。)@Override
public final int startActivity(IApplicationThread caller, String callingPackage,Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,UserHandle.getCallingUserId());
}
private class ApplicationThread extends IApplicationThread.Stub {
......
}
@Override
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) {
......
final Activity a = performLaunchActivity(r, customIntent);
......
}
9.1.2 performLaunchActivity()方法所在的事
1.从ActivityClientRecord中获取待启动Activity的组件信息@Override
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) {
......
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo,r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity( mInitialApplication.getPackageManager()); r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName, r.activityInfo.targetActivity);
}
......
}
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e);
}
}
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
......
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!instrumentation.onException(app, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException( "Unable to create application " + app.getClass().getName() =+ ": " + e.toString(), e);
}
}
......
}
与第二步一样,都是通过Application的创建也是通过Instrumentation来完成的,都是通过类加载器完成的。
创建完毕后会调用callApplicationOnCreate方法来调用Application的onCreate()方法。public void callApplicationOnCreate(Application app) {
app.onCreate();
}
ContextImpl appContext = createBaseContextForActivity(r);
......
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
......
activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback);
......
9.1.3 总结Activity的工作过程
9.2 Service的工作过程
9.2.1 Service的启动过程
@Override
public ComponentName startService(Intent service) {
return mBase.startService(service);
}
ContextImpl的startService调用了startServiceCommon方法,private ComponentName startServiceCommon(Intent service, boolean
requireForeground, UserHandle user) {
......
ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), requireForeground, getOpPackageName(), user.getIdentifier());
......
}
startService方法内内有段代码try {
res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, requireForeground, callingPackage, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
在ActiveServices的startServiceLocked方法中经过一堆的传递,最后也是回到了ApplicationThread中。由scheduleCreateService开始启动:public final void scheduleCreateService(IBinder token, ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s);
}
handleCreateService()方法主要做了以下几件事:
1.通过类加载器创建Service的实例
2.创建Application对象,并调用其onCreate。当然Application对象创建也是一次,如果已有就不会再创建了
3.创建ContextImpl对象,并通过Service的attach方法完成建立联系,与Activity类型,毕竟Activity和
Service都是一个Context。
5.最后调用Service的onCreate方法将Service对象存储到ActivityThread中的一个列表中,这个列表的定义如下final ArrayMap
9.2.2 Service的绑定过程
bindServiceCommon会通过AMS完成Service的具体绑定过程。public int bindService(IApplicationThread caller,....){
......
synchronized(this) {
return mServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, callingPackage, userId);
}
}
消息给Handler H,public final void scheduleBindService(IBinder token,...)
{
......
sendMessage(H.BIND_SERVICE, s);
}
这个时候客户端并不知道已经绑定完成了,由我们熟悉的AMS的publishService方法来完成通知客户端的任务。public void publishService(IBinder token, ...)
{
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed
in Intent");
}
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
throw new IllegalArgumentException("Invalid service
token");
}
mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}
}
connected方法内部post方法将RunConnection方法运行在主线程中,在RunConnection方法中通过ServiceConnection对象的onServiceConnected方法完成最后的通知。9.3 BroadcastRecevier的工作过程
9.3.1 广播的注册过程
由ContextWrapper开始进入过程的。最后是由AMS的registerRecevier完成广播的注册的。9.3.1 广播的发送和接收过程
广播的接收最后会通过ApplicationThread来完成广播的接收。9.4 ContentProvider的工作过程
2.创建Application对象
3.启动当前进程的ContentProvider并调用onCreate方法
4.调用Application的onCreate方法9.5 最简单的总结
10.Android的消息机制
Looper消息循环,MessageQueue只是消息的存储单元,Looper则处理消息。Looper会无限循环的去查看是否有新的消息,如果有的话就处理消息,否则就一直等待。Looper中还有一个特殊的概念:ThreadLocal,ThreadLocal并不是线程,而是在每个线程中存储数据。
Handler创建的时候会采用当前线程的Looper来构造消息循环系统,就是通过ThreadLocal。因为ThreadLocal可以在不同的线程中互不干扰的存储并提供数据。通过ThreadLocal可以很轻松的获得到每个线程的Looper。
线程是默认没有Looper的,如果需要使用Handler就必须为线程创建Looper,主线程也是UI线程也是ActivityThread,被创建的时候就会初始化Looper,所以UI线程可以默认使用Handler10.1 Android的消息机制概述
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException("Only the original thread that created a view
hierarchy can touch its views.");
}
}
因为Android的UI控件不是线程安全的,在多线程中并发访问会导致UI控制处于不可预期状态。
为什么不在UI控件的访问添加锁机制
1.加上锁机制会使UI访问操作变的复杂
2.加上锁机制会使UI访问的效率降低,因为锁机制会阻塞某些线程的执行10.2 Android的消息机制分析
10.2.1 ThreadLocal的工作原理
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
10.2.2 消息队列的工作原理
之前的笔记中说了消息队列并不是队列,其实内部是单链表的数据结构维护的消息列表。单链表的优势在于插入和删除。10.2.3 Looper的工作原理
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue();
mThread = Thread.currentThread();
}
通过Looper.prepare()创建一个Looper
通过Looper.loop()来开启消息循环
quit 会直接退出Looper
quitSafely 会设定一个退出标记,然后把消息队列中已有的消息处理完毕后会安全的退出public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
for (;;) {
Message msg = queue.next();
// might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
......
try { msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
}
10.2.4 Handler的工作原理
Handler发送消息的过程仅仅是像消息对应插入一条信息。MessageQueue的next()方法就是返回这条信息给Looper,Looper接收到信息后就开始处理了。消息最后由Looper交给Handler处理。即Handler的dispatchMessage方法会被调用。这个时候Handler就进入了处理消息的阶段。/** * Handle system messages here. */
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
1.检查Message的callback是否为null,不为null就通过 handleCallback()方法 。Message的callback是一个Runable对象,实际上就是Handler的post方法所传递的Runable参数。handleCallback方法为:private static void handleCallback(Message message) {
message.callback.run();
}
(Callback可以用来创建一个Handler,当我们不想派生子类时,可以用Callback来创建一个Handler)
3.最后调用Handler的handleMessage()方法处理信息。10.3 主线程的消息循环
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
Looper.loop();
......
}
class H extends Handler {
public static final int BIND_APPLICATION = 110;
......
}
11.Android的线程和线程池
当系统存在大量的线程时,系统会通过时间片轮转的方式调度每个线程,所以说不可能做到绝对的线程并行,除非线程数量小于CPU核心数。11.1主线程和子线程
主线程的作用是运行四大组件以及处理它们和用户的交互,而子线程的作用则是执行耗时任务。11.2Android中的线程形态
11.2.1 AsyncTask
1.onPreExecute:执行后台耗时操作前被调用,通常用于进行初始化操作。
2.doInBackground:必须重写,异步执行后台线程要完成的任务,耗时操作将在此方法中完成。
3.onProgressUpdate:当在doInBackground方法中调用publishProgress方法更新任务执行进度后,将调用此方法.通过此方法我们可以知晓任务的完成进度。
4.onPostExecute:当doInBackground方法完成后,系统将自动调用此方法,并将doInBackground方法返回的值传入此方法.通过此方法进行UI的更新。
1.AsyncTask的类必须在主线程加载。
2.AsyncTask的对象必须在主线程中创建
3.execute方法必须在住线程调用
4.不要在程序中直接调用它的4个核心方法
5.一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行异常
6.Android1.6以前AsyncTask是串行执行任务的,1.6的时候AsyncTask开始采用线程池里处理并行任务,但是从3.0开是,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程串行执行任务。尽管如此,3.0以后版本中,任然可以通过AsyncTask的executeOnExecute方法并行的执行任务。
execute()内部会调用executeOnExecutor()方法@MainThread
public final AsyncTask
11.2.2 HandlerThread
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
11.2.3 IntentService
在实现上IntentService封装了HandlerThread和Handler。(从它的onCreate方法可以看出)@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a staticv startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
发送的消息最终都会在HandlerThread 中执行。11.3Android中的线程池
1,.重用线程池里的线程,避免因为线程的创建和销毁而带来的性能开销。
2.能有效的控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
3.能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
Android中的线程池都是直接或间接通过配置ThreadPoolExecute来实现的。11.3.1ThreadPoolExecute
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit, BlockingQueue
maximumPoolSize线程池所能容纳的最大线程数,当活动线程数达到这个值的时候,后续线程会被阻塞。
keepAliveTime非核心线程,闲置时的超时时长,超过这个时长,非核心线程就会被回收。当ThreadPoolExecute的allowCoreThreadTimeOut属性设置为true的时候,keepAliveTime同样会作用于核心线程。
unit 用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒),TimeUnit.MINUTES(分钟),
workQueue 线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。
threadFactory线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口,里面只有一个方法public interface ThreadFactory {
Thread newThread(Runnable r);
}
1.如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
2.如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
3.如果步骤2无法将任务插入到任务队列中,这往往是由于任务队列已满。这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
4.如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecute会调用RejectedExecuetionHandler的RejectedExecuetion方法通知调用者。
ThreadPoolExecute的配置参数在AsyncTask中就有体现,AsyncTask对THREAD_POOL_EXECUTOR这个线程池进行了配置:
11.3.2线程池的分类
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L,TimeUnit.MILLISECONDS,new
LinkedBlockingQueue
特征:
(1)线程池中的线程处于一定的量,可以很好的控制线程的并发量
(2)线程可以重复被使用,在显示关闭之前,都将一直存在
(3)超出一定量的线程被提交时候需在队列中等待
创建方式:
(1)Executors.newFixedThreadPool(int nThreads);//nThreads为线程的数量
(2)Executors.newFixedThreadPool(int nThreads,ThreadFactory threadFactory);//nThreads为线程的数量,threadFactory创建线程的工厂方式
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的 ThreadFactory 创建新线程。
特征:
(1)线程池中数量没有固定,可达到最大值(Interger. MAX_VALUE)
(2)线程池中的线程可进行缓存重复利用和回收(回收默认时间为1分钟)
(3)当线程池中,没有可用线程,会重新创建一个线程
创建方式:
Executors.newCachedThreadPool();
public static ScheduledExecutorService newScheduledThreadPool(int
corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue());
}
特征:
(1)线程池中具有指定数量的线程,即便是空线程也将保留
(2)可定时或者延迟执行线程活动
(3)线程池中数量没有固定,可达到最大值(Interger. MAX_VALUE)
创建方式:
(1)Executors.newScheduledThreadPool(int corePoolSize);// corePoolSize线程的个数
(2)newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory);// corePoolSize线程的个数,threadFactory创建线程的工厂
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
的意义在于统一所有外界的任务到同一个线程中,这个使得这些任务之间不需要处理线程同步的问题
(1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行
创建方式:
(1)Executors.newSingleThreadExecutor() ;
(2)Executors.newSingleThreadExecutor(ThreadFactory threadFactory);// threadFactory创建线程的工厂方式
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));
}
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, nteger.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue());
}
(1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行
(2)可定时或者延迟执行线程活动
创建方式:
(1)Executors.newSingleThreadScheduledExecutor() ;
(2)Executors.newSingleThreadScheduledExecutor(ThreadFactory threadFactory);//threadFactory创建线程的工厂12.Bitmap的加载和Cache
12.1 Bitmap的高效加载
1.decodeFile 从文件系统加载图片,间接调用了decodeStream方法
2.decodeResource 从资源中加载图片,间接调用了decodeStream方法
3.decodeStream 从输入流加载图片
4.decodeByteArray 从字节数组中加载图片
12.2 Android中的缓存策略
目前最常用的缓存算法是LRU算法。
常用的缓存策略一般为两种:
通过二者的完美结合,就可以实现一个具有很高实用价值的ImagerLoder。
强引用 直接的对象引用
软引用 当一个对象只有软引用存在时,系统内存不足时此对象会被gc回收
弱引用 当一个对象只有弱引用存在时,此对象随时会被gc回收12.3 ImageLoder的实现
2.内存缓存和磁盘缓存的实现(使用LruCache,DiskCache)
3.同步加载和异步加载的接口设计(先去内存读取,再去磁盘读取,最后再通过线程池今晚网络读取)13.综合技术
14.JNI和NKD编程
15.Android的性能优化
首先删除布局中无用的控件和层级,其次选择性能较低的ViewGroup,比如能用线性布局和相对布局实现的界面,优先选择线性布局。因为RelativeLayout相对来说功能比较复杂,它的过程需要花费更多的CPU时间。FrameLayout,LinearLayout都是简单高效的ViewGroup,因此可以多考虑使用它们。
采用标签提高程序初始化效率,一般与标签配合使用。
ViewStub继承View,非常的轻量级,且宽高都是0,ViewStub在开发者很多界面正常时间不会显示,比如网络异常界面。通过ViewStub可以做到使用的时候在加载。提高了程序初始化时的性能。
避免在onDraw方法执行大量操作。体现在两方面:
1.onDraw中不要创建新的局部对象,因为onDraw方法可能会被多次调用,这样一瞬间就会产生大量的临时对象。
2.onDraw中不要执行耗时任务,也不能执行成千上万次的循环操作,这会造成View的绘制过程不流畅。
1.静态变量导致的内存泄露
2.单例模式导致的内存泄露
3.属性动画导致的内存泄露
4.ListView和Bitmap优化
5.线程优化