本文梳理自:https://lrh1993.gitbooks.io/android_interview_guide/content/
onPause()->onSaveInstanceState()-> onStop()->onDestroy()->onCreate()->onStart()->onRestoreInstanceState->onResume()
onCreate
和onRestoreInstanceState
方法来恢复Activity的状态的区别:
onSaveInstanceState
方法调用时机
A的onPause() -> B的onCreate() -> B的onStart() -> B的onResume() -> B显示,同时A在屏幕上不可见 -> A的onStop()
onPause中不能进行耗时操作,会影响到新Activity的显示。因为onPause必须执行完,新的Activity的onResume才会执行
参数Intent是启动复写onNewIntent的Activity时传入的Intent
在SingleTask/SingleInstance/SingleTop中都会回调
重新启动自身:onPause -> onNewIntent -> onResume
onRestart()
不会执行TaskAffinity
指定任务栈
taskAffinity属性的值为字符串,且中间必须有包名分隔符
只能搭配SingleTask()启动模式和allowTaskReparenting属性配对使用
(1)按运行进程分
本地服务(Local Service):依附于主进程
远程服务(Remote Service):独立进程,不依附于主进程
android:process="remote"
(2)按运行类型分
(3)按使用方式分类
类别 | 区别 |
---|---|
startService 启动的服务 |
主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService |
bindService 启动的服务 |
方法启动的服务要进行通信。停止服务使用unbindService |
同时使用startService 、bindService 启动的服务 |
停止服务应该同时使用stopService 与unbindService |
(1)startService / stopService
(主要用于不可交互的后台服务)
生命周期顺序:onCreate->onStartCommand->onDestroy
方法触发次数
onCreate
只会在第一次startService时被触发
每次 startService 都会触发onStartCommand
不论 startService 多少次,stopService 一次就会停止服务
Service结束时机:
如果系统资源不足,android系统也可能结束服务
调用stopService,或自身的stopSelf方法
在设置中,通过应用->找到自己应用->停止
使用场景:
(2)bindService / unbindService
(主要用于可交互的后台服务)
生命周期顺序:onCreate->onBind->onUnBind->onDestroy
方法触发次数:
onCreate
和 onBind
方法只会在第一次bindService时执行
之后每次 bindService 都不会触发任何回调
onStartCommand
方法始终不会调用
Service结束时机:
调用unbindService
来接触绑定、断开连接
调用该Service的Context不存在了(如Activity被Finish——即通过bindService启动的Service的生命周期依附于启动它的Context)
使用场景
(3)混合型(两种方式一起用)
unBindService
将不会停止Service,必须调用stopService
或Service自身的stopSelf
来停止服务创建与销毁
startForeground()
stopForeground()
将前台服务降为后台服务,然后通过stopService()
将前台服务中止TaskStackBuilder
在Notification通知栏中的使用
mBuilder = new NotificationCompat.Builder(this)
.setContent(view)
.setSmallIcon(R.drawable.icon).setTicker("新资讯")
.setWhen(System.currentTimeMillis())
.setOngoing(false)
.setAutoCancel(true);
Intent intent = new Intent(this, NotificationShow.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(NotificationShow.class);
stackBuilder.addNextIntent(intent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
//PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pendingIntent);
TaskStackBuilder.create(this)
创建一个stackBuilder实例addParentStack()
为跳转后的Activity添加一个父Activity(在activity中的manifest中添加parentActivityName即可)
不同组件之间通信(包括应用内 / 不同应用之间)
与 Android 系统在特定情况下的通信+
多线程通信
广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型
广播发送者 和 广播接收者的执行 是 异步的
onReceive()
(1)定义广播接收者实体类
BroadcastReceiver
onReceive(Context context, Intent intent)
(2)注册广播接收器
静态注册(XML中的receiver
标签)
android:exported=["true" | "false"]
:决定此broadcastReceiver能否接收其他App的发出的广播。如果有intent-filter
,默认值为true,否则为false
:匹配intent-filter
中的action,用于指定广播接收器将接收的广播类型
动态注册(通过调用Context的registerReceiver()
方法)
动态广播最好在Activity的onResume()注册、onPause()注销
有注册就必须有注销,否则会内存泄漏
(3)广播发送者向AMS发送广播(五类广播类型)
类型一:普通广播(Normal Broadcast)
Intent intent = new Intent();
//对应BroadcastReceiver中intentFilter的action
intent.setAction(BROADCAST_ACTION);
//发送广播
sendBroadcast(intent);
类型二:系统广播(System Broadcast)
根据系统相关条件变化而自动发送广播
每个广播都有特定的Intent - Filter(包括具体的action)
只需要定义Reciver接受即可
类型三:有序广播(Ordered Broadcast)
sendOrderedBroadcast(intent)
定义:发送出去的广播被广播接收者按照先后顺序接收
特点1:先接收的广播接收者可以对广播进行截断,即后接收的广播接收者不再接收到此广播;
特点2:先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播
类型四:粘性广播(Sticky Broadcast)
类型五:App应用内广播(Local Broadcast)
局部广播
解决安全性&效率性问题
将全局广播设为局部广播:1⃣
注册时将exported
设为false 2⃣ 在广播发送和接收时,增设相应权限permission,用于权限验证 3⃣ 发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中(intent.setPackage(packageName)
)
使用封装好的LocalBroadcastManager
类,使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的context变成了LocalBroadcastManager的单一实例
原理:ContentProvider的底层是采用 Android中的Binder机制
理解:相当于各个进程与自身数据部分的一个中间件,各进程通过暴露自己的ContentProvider对数据进行统一、标准化的操作
优点:
(1)安全:
(2)解耦了底层数据的存储方式:
定义:Uniform Resource Identifier,即统一资源标识符
作用:唯一标识 ContentProvider & 其中的数据
Uri uri = Uri.parse("content://com.carson.provider/User/1")
作用:指定某个扩展名的文件用某种应用程序来打开
格式:类型组成 = 类型 + 子类型
ContentProvider根据 URI 返回MIME类型
ContentProvider.geType(uri);
组织数据方式:表格
主要方法(增删改查)
供外部进程调用,运行在ContentProvider进程的Binder线程池中(不是主线程)
得到数据类型,即返回当前 Url 所代表数据的MIME类型:public String getType(Uri uri)
(1)增:public Uri insert(Uri uri, ContentValues values)
(2)删:public int delete(Uri uri, String selection, String[] selectionArgs)
(3)改:public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
(4)查:public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
ContentProvider类并不会直接与外部进程交互,而是通过ContentResolver类
作用:统一管理不同 ContentProvider间的操作
通过 URI 即可操作 不同的ContentProvider 中的数据
外部进程通过 ContentResolver类 从而与ContentProvider类进行交互
出现原因:一款应用可能存在多个Content Provider
具体使用:
ContentProvider
的增删改查调用相同(1)ContentUris类
withAppendedId()
:向URI追加一个id
Uri uri = Uri.parse("content://cn.scu.myprovider/user")
Uri resultUri = ContentUris.withAppendedId(uri, 7);
// 最终生成后的Uri为:content://cn.scu.myprovider/user/7
parseId()
:从URL中获取ID
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7")
long personid = ContentUris.parseId(uri);
//获取的结果为:7
(2)UriMatcher类
作用:
使用:
// 步骤1:初始化UriMatcher对象
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
//常量UriMatcher.NO_MATCH = 不匹配任何路径的返回码
// 即初始化时不匹配任何东西
// 步骤2:在ContentProvider 中注册URI(addURI())
int URI_CODE_a = 1;
int URI_CODE_b = 2;
matcher.addURI("cn.scu.myprovider", "user1", URI_CODE_a);
matcher.addURI("cn.scu.myprovider", "user2", URI_CODE_b);
// 若URI资源路径 = content://cn.scu.myprovider/user1 ,则返回注册码URI_CODE_a
// 若URI资源路径 = content://cn.scu.myprovider/user2 ,则返回注册码URI_CODE_b
// 步骤3:根据URI 匹配 URI_CODE,从而匹配ContentProvider中相应的资源(match())
@Override
public String getType (Uri uri){
Uri uri = Uri.parse(" content://cn.scu.myprovider/user1");
switch (matcher.match(uri)) {
// 根据URI匹配的返回码是URI_CODE_a
// 即matcher.match(uri) == URI_CODE_a
case URI_CODE_a:
return tableNameUser1;
// 如果根据URI匹配的返回码是URI_CODE_a,则返回ContentProvider中的名为tableNameUser1的表
case URI_CODE_b:
return tableNameUser2;
// 如果根据URI匹配的返回码是URI_CODE_b,则返回ContentProvider中的名为tableNameUser2的表
}
}
(3)ContentObserver类
作用:观察 Uri引起ContentProvider 中的数据变化(增删改),并 通知外界(即访问该数据访问者)
具体使用:
// 步骤1:注册内容观察者ContentObserver
getContentResolver().registerContentObserver(uri);
// 通过ContentResolver类进行注册,并指定需要观察的URI
// 步骤2:当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
public class UserContentProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
db.insert("user", "userid", values);
getContext().getContentResolver().notifyChange(uri, null);
// 通知访问者
}
}
// 步骤3:解除观察者
getContentResolver().unregisterContentObserver(uri);
// 同样需要通过ContentResolver类进行解除
FragmentTransaction.addToBackStack(String tag)
虽然实例不会被销毁,但是视图层次依然会被销毁。当再次返回该界面时,视图层仍旧是重新按照代码绘制视图
会调用onDestoryView
和onCreateView
,只是未执行onDestroy
如果不希望视图重绘(保持数据),就要使用hide/show
,而不要用replace
transaction.remove()
然后transaction.add()
的组合
transaction.remove()
: 从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁
transaction.add()
: 向Activity中添加一个Fragment
直接使用add/replace/hide/show的话,都要commit其效果才会在屏幕上显示出来(ransatcion.commit()
提交事务)
三个方法:
(1)如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
(2)如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()
或者findFragmentById()
获得任何Fragment实例,然后进行操作
(3)Fragment中可以通过getActivity()得到当前绑定的Activity的实例,然后进行操作
优化:
目标:降低Fragment与Activity的耦合,而且Fragment更不应该直接操作别的Fragment
解决方法:通过在Fragment中定义回调接口,在Activity中实现接口来实现堆Fragment的统一管理
通过检查onCreate的参数Bundle savedInstanceState就可以判断,当前是否发生Activity的重新创建
savedInstanceState==null
时,才进行创建Fragment实例Fragment也有onSaveInstanceState
的方法,在此方法中进行保存数据,然后在onCreate
或者onCreateView
或者onActivityCreated
进行恢复都可以
public class Activity extends android.app.Activity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println(msg.what);
}
};
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
...............耗时操作
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
}
}).start();
}
}
Handler线程切换的原理
线程2持有在线程1中创建的Handler的引用
Handler接收消息是在创建它的线程当中
MessageQueue,Handler和Looper三者之间的关系
Looper是保存在ThreadLocal中的
每个线程可以有多个Handler
Looper中维护一个MessageQueue
使用消息机制:
初始化Looper:Looper.prepare()
开启Looper:Looper.loop()
loop()进入循环模式,直到消息为空时退出循环:
当遇到Message 时,读取MessageQueue的下一条Message,把Message分发给相应的target(handler)。
当next()取出下一条消息时,队列中已经没有消息时,next()会无限循环,产生阻塞。等待MessageQueue中加入消息,然后重新唤醒。
创建handler
发送消息
sendMessageAtTime()
,在该方法内调用enqueueMessage()
接收消息:next()
原理:next()
轻易不会返回null,当消息队列为空时,next方法会阻塞,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回继续轮询
nativePollOnce()
是阻塞操作
, nextPollTimeoutMillis
代表下一个消息到来前,还需要等待的时长
当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。
Message next() {
final long ptr = mPtr;
if (ptr == 0) { //当消息循环已经退出,则直接返回
return null;
}
int pendingIdleHandlerCount = -1; // 循环迭代的首次为-1
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回继续执行下面的Synchronized代码块
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,为空则退出循环。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//当异步消息触发时间大于当前时间,则设置下一次轮询的超时时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获取一条消息,并返回
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
//设置消息的使用状态,即flags |= FLAG_IN_USE
msg.markInUse();
return msg; //成功地获取MessageQueue中的下一条即将要执行的消息
}
} else {
//没有消息
nextPollTimeoutMillis = -1;
}
//消息正在退出,返回null
if (mQuitting) {
dispose();
return null;
}
...............................
}
}
分发消息:dispatchMessage()
优先级最高:Message的回调方法:message.callback.run()
优先级次之:Handler中Callback的回调方法:Handler.mCallback.handleMessage(msg)
优先级最低:Handler的默认方法:Handler.handleMessage(msg)
主要发生的Touch事件有如下四种:
三个重要方法的关系(此为Activity事件分发中的dispatchTouchEvent()源码)
// 点击事件产生后,会直接调用dispatchTouchEvent()方法
public boolean dispatchTouchEvent(MotionEvent ev) {
//代表是否消耗事件
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
//如果onInterceptTouchEvent()返回true则代表当前View拦截了点击事件
//则该点击事件则会交给当前View进行处理
//即调用onTouchEvent ()方法去处理点击事件
consume = onTouchEvent (ev) ;
} else {
//如果onInterceptTouchEvent()返回false则代表当前View不拦截点击事件
//则该点击事件则会继续传递给它的子元素
//子元素的dispatchTouchEvent()就会被调用,重复上述过程
//直到点击事件被最终处理为止
consume = child.dispatchTouchEvent (ev) ;
}
return consume;
}
默认事件传递情况:(如图下所示)
onTouchEvent()
(子类的onTouchEvent会return super.onTouchEvent()
)假设View C希望处理这个点击事件
C被设置成可点击的(Clickable)
覆写C的onTouchEvent方法返回true
拦截DOWN的后续事件:假设ViewGroup B没有拦截DOWN事件(还是View C来处理DOWN事件),但它拦截了接下来的MOVE事件。
DOWN事件传递到C的onTouchEvent方法,返回了true。
在后续到来的第一个MOVE事件,B的onInterceptTouchEvent方法返回true拦截该MOVE事件,但该事件并没有传递给B;这个MOVE事件将会被系统变成一个CANCEL事件传递给C的onTouchEvent方法
后续又来了一个MOVE事件,该MOVE事件才会直接传递给B的onTouchEvent()
C再也不会收到该事件列产生的后续事件。
事件分发机制的三部分组成
事件最先传到Activity的dispatchTouchEvent()进行事件分发
调用Window类实现类PhoneWindow的superDispatchTouchEvent()
调用DecorView的superDispatchTouchEvent()
最终调用DecorView父类的dispatchTouchEvent(),即ViewGroup的dispatchTouchEvent()
dispatchTouchEvent()
源码分析// 发生ACTION_DOWN事件或者已经发生过ACTION_DOWN,并且将mFirstTouchTarget赋值,才进入此区域,主要功能是拦截器
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
//disallowIntercept:是否禁用事件拦截的功能(默认是false),即不禁用
//可以在子View通过调用requestDisallowInterceptTouchEvent方法对这个值进行修改,不让该View拦截事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//默认情况下会进入该方法
if (!disallowIntercept) {
//调用拦截方法
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
// 当没有触摸targets,且不是down事件时,开始持续拦截触摸。
intercepted = true;
}
进入拦截判断的两个条件
(1)如果当前事件的MotionEvent.ACTION_DOWN
,则进入判断,调用ViewGroup onInterceptTouchEvent()
方法的值,判断是否拦截
(2)如果mFirstTouchTarget != null
,即已经发生过MotionEvent.ACTION_DOWN
,并且该事件已经有ViewGroup的子View进行处理了,那么也进入判断,调用ViewGroup onInterceptTouchEvent()
方法的值,判断是否拦截
如果不是以上两种情况,即已经是MOVE或UP事件了,并且之前的事件没有对象进行处理,则将intercepted
设置成true,开始拦截接下来的所有事件
mFirstTouchTarget
为null),那么接下来的一些列事件都不会交给他处理dispatchTransformedTouchEvent()
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
由于其中传递的child不为空,所以就会调用子元素的dispatchTouchEvent()
如果子元素的dispatchTouchEvent()方法返回true,那么mFirstTouchTarget就会被赋值,同时跳出找寻newTouchTarget(即上面的mFirstTouchTarget)的for循环
对mFirstTouchTarget
赋值
//添加TouchTarget,则mFirstTouchTarget != null。
newTouchTarget = addTouchTarget(child, idBitsToAssign);
//表示以及分发给NewTouchTarget
alreadyDispatchedToNewTouchTarget = true;
其中在addTouchTarget(child, idBitsToAssign);
内部完成mFirstTouchTarget被赋值
如果mFirstTouchTarget为空,将会让ViewGroup默认拦截所有操作
如果遍历所有子View或ViewGroup,都没有消费事件。ViewGroup会自己处理事件
dispatchTouchEvent()
源码分析public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
(1)mOnTouchListener!= null
//mOnTouchListener是在View类下setOnTouchListener方法里赋值的
public void setOnTouchListener(OnTouchListener l) {
//即只要我们给控件注册了Touch事件,mOnTouchListener就一定被赋值(不为空)
mOnTouchListener = l;
}
(2)(mViewFlags & ENABLED_MASK) == ENABLED
(3)mOnTouchListener.onTouch(this, event)
//手动调用设置
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
如果在onTouch方法返回true,就会让上述三个条件全部成立,从而整个方法直接返回true。
如果在onTouch方法里返回false,就会去执行onTouchEvent(event)方法。
onTouchEvent(event)
的源码public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
//如果该控件是可以点击的就会进入到下两行的switch判断中去;
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
//如果当前的事件是抬起手指,则会进入到MotionEvent.ACTION_UP这个case当中。
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
// 在经过种种判断之后,会执行到关注点1的performClick()方法。
//请往下看关注点1
if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
//关注点1
//请往下看performClick()的源码分析
performClick();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
mPrivateFlags |= PRESSED;
refreshDrawableState();
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;
case MotionEvent.ACTION_DOWN:
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPrivateFlags |= PREPRESSED;
mHasPerformedLongPress = false;
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
break;
case MotionEvent.ACTION_CANCEL:
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
removeTapCallback();
break;
case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY();
// Be lenient about moving outside of buttons
int slop = mTouchSlop;
if ((x < 0 - slop) || (x >= getWidth() + slop) ||
(y < 0 - slop) || (y >= getHeight() + slop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
// Need to switch from pressed to not pressed
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
}
}
break;
}
//如果该控件是可以点击的,就一定会返回true
return true;
}
//如果该控件是不可以点击的,就一定会返回false
return false;
}
performClick()
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}
只要mOnClickListener不为null,就会去调用onClick方法
public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
mOnClickListener = l;
}
onTouch()
的执行高于onClick()
(1)如果在回调onTouch()里返回false,就会让dispatchTouchEvent方法返回false,那么就会执行onTouchEvent();如果回调了setOnClickListener()来给控件注册点击事件的话,最后会在performClick()方法里回调onClick()
(2)如果在回调onTouch()里返回true,就会让dispatchTouchEvent方法返回true,那么将不会执行onTouchEvent(),即onClick()也不会执行;
onTouch()
和onTouchEvent()
的区别分析三个重要方法得知
dispatchTouchEvent()和 onTouchEvent()消费事件、终结事件传递(返回true)
而onInterceptTouchEvent 并不能消费事件,它相当于是一个分叉口起到分流导流的作用,对后续的ACTION_MOVE和ACTION_UP事件接收起到非常大的作用
ACTION_MOVE和ACTION_UP事件的传递结论
如果在某个对象(Activity、ViewGroup、View)的dispatchTouchEvent()消费事件(返回true),那么收到ACTION_DOWN的函数也能收到ACTION_MOVE和ACTION_UP
如果在某个对象(Activity、ViewGroup、View)的onTouchEvent()消费事件(返回true),那么ACTION_MOVE和ACTION_UP的事件从上往下传到这个View后就不再往下传递了,而直接传给自己的onTouchEvent()并结束本次事件传递过程。
public abstract class AsyncTask
class DownloadTask extends AsyncTask {
@Override
protected void onPreExecute() {
progressDialog.show();
}
@Override
protected Boolean doInBackground(Void... params) {
try {
while (true) {
int downloadPercent = doDownload();
publishProgress(downloadPercent);
if (downloadPercent >= 100) {
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
progressDialog.setMessage("当前下载进度:" + values[0] + "%");
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
if (result) {
Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
}
}
}
继承抽象类并实现四个方法后,通过new DownloadTask().execute();
创建一个实例来开始任务
AsyncTask对象必须在UI线程中创建
一个任务实例只能执行一次,如果执行第二次将会抛出异常
通过在doInBackground
中显式调用publishProgress(downloadPercent);
来回调onProgressUpdate
public AsyncTask() {
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
mFuture = new FutureTask(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
result = doInBackground(mParams);
postResult(result);
,传递给内部的Handler跳转到主线程中execute()
execute()
调用了executeOnExecutor()
方法
executeOnExecutor()
方法中,执行耗时任务是在exec.execute(mFuture)
SerialExecutor 是个静态内部类
是所有实例化的AsyncTask对象公有的
SerialExecutor 内部维持了一个队列,通过锁使得该队列保证AsyncTask中的任务是串行执行的,即多个任务需要一个个加到该队列中,然后执行完队列头部的再执行下一个
调用 scheduleNext()
方法,调用THREAD_POOL_EXECUTOR执行队列头部的任务
在线程池中调用execute()
方法执行具体的耗时任务(即构造函数Call方法中所定义的内容)
实行完耗时任务后,postResult()
发送消息给InternalHandler
MESSAGE_POST_RESULT
(即执行完了doInBackground()方法并传递结果),那么就调用finish()方法。private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
AsyncTask不与任何组件绑定生命周期,所以在Activity/或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(boolean);
如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。
屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。
避免重复开启销毁线程解决方案:
HandlerThread
可以用来执行多个耗时操作,而不需要多次开启线程
Handler
和Looper
实现的每一个HandlerThread对应一个Thread
存在意义:是一个对Looper做了内部封装的Thread
一个继承了线程的类(HandlerThread),搭配Handler使用,将Thread的Looper绑定到Handler上,然后在Handler的handlerMessage方法中检查是否有耗时逻辑需要处理
(1)创建Handler的实例对象
HandlerThread handlerThread = new HandlerThread("myHandlerThread");
(2)启动创建的HandlerThread线程
handlerThread.start();
(3)通过handlerThread将线程的looper与Handler绑定到一起
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
checkForUpdate();
if(isUpdate){
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
}
};
例子:
public class MainActivity extends AppCompatActivity {
private static final int MSG_UPDATE_INFO = 0x100;
Handler mMainHandler = new Handler();
private TextView mTv;
private Handler mThreadHandler;
private HandlerThread mHandlerThread;
private boolean isUpdate = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTv = (TextView) findViewById(R.id.tv);
initHandlerThread();
}
private void initHandlerThread() {
mHandlerThread = new HandlerThread("xujun");
mHandlerThread.start();
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
checkForUpdate();
if (isUpdate) {
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
}
};
}
/**
* 模拟从服务器解析数据
*/
private void checkForUpdate() {
try {
//模拟耗时
Thread.sleep(1200);
mMainHandler.post(new Runnable() {
@Override
public void run() {
String result = "实时更新中,当前股票行情:%d";
result = String.format(result, (int) (Math.random() * 5000 + 1000));
mTv.setText(Html.fromHtml(result));
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void onResume() {
isUpdate = true;
super.onResume();
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
@Override
protected void onPause() {
super.onPause();
isUpdate = false;
mThreadHandler.removeMessages(MSG_UPDATE_INFO);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit();
mMainHandler.removeCallbacksAndMessages(null);
}
}
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
//持有锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
notifyAll();
}
//设置线程的优先级别
Process.setThreadPriority(mPriority);
//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
start()
方法调用start()
方法之后,才可以将HandlerThread和handler绑定在一起
原因:就是我们是在run()方法才开始初始化我们的looper,而我们调用HandlerThread的start()方法的时候,线程会交给虚拟机调度,由虚拟机自动调用run方法
notifyAll()
quit(boolean safe)
方法void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
//安全退出调用这个方法
if (safe) {
removeAllFutureMessagesLocked();
} else {//不安全退出调用这个方法
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
(1)不安全的退出调用removeAllMessagesLocked()
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
(2)安全的退出会调用removeAllFutureMessagesLocked()
会根据Message.when
这个属性,判断我们当前消息队列是否正在处理消息,没有正在处理消息的话,直接移除所有回调,正在处理的话,等待该消息处理处理完毕再退出该循环
因此说quitSafe()是安全的,而quit()方法是不安全的
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
//判断当前队列中的消息是否正在处理这个消息,没有的话,直接移除所有回调
if (p.when > now) {
removeAllMessagesLocked();
} else {//正在处理的话,等待该消息处理处理完毕再退出该循环
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
IntentService内部采用了HandlerThread实现,作用类似于后台线程
与后台线程相比,IntentService是一种后台服务,优势是:优先级高(不容易被系统杀死),从而保证任务的执行
不建议在Service中编写耗时的逻辑和操作,否则会引起ANR;
从属性 & 作用上来说
类别 | 特性 |
---|---|
Service | 依赖于应用程序的主线程(不是独立的进程 or 线程) |
IntentService | 创建一个工作线程来处理多线程任务 |
类别 | 特性 |
---|---|
Service | 需要主动调用stopSelf() 来结束服务 |
IntentService | 不需要主动关闭,在所有intent被处理完后,系统会自动关闭服务 |
作用:处理异步请求,实现多线程
特征:线程任务需要按顺序、在后台执行的使用场景
不适合场景:由于所有的任务都在同一个Thread looper里面来做,所以不符合多个数据同时请求的场景。
IntentService的onHandleIntent
回调方法中依次执行,执行完自动结束。onHandleIntent()
方法(执行耗时逻辑)package com.example.carson_ho.demoforintentservice;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
/**
* Created by Carson_Ho on 16/9/28.
*/
public class myIntentService extends IntentService {
/*构造函数*/
public myIntentService() {
//调用父类的构造函数
//构造函数参数=工作线程的名字
super("myIntentService");
}
/*复写onHandleIntent()方法*/
//实现耗时任务的操作
@Override
protected void onHandleIntent(Intent intent) {
//根据Intent的不同进行不同的事务处理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
Log.i("myIntentService", "do task1");
break;
case "task2":
Log.i("myIntentService", "do task2");
break;
default:
break;
}
}
@Override
public void onCreate() {
Log.i("myIntentService", "onCreate");
super.onCreate();
}
/*复写onStartCommand()方法*/
//默认实现将请求的Intent添加到工作队列里
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("myIntentService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("myIntentService", "onDestroy");
super.onDestroy();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//同一服务只会开启一个工作线程
//在onHandleIntent函数里依次处理intent请求。
Intent i = new Intent("cn.scu.finch");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
Intent i2 = new Intent("cn.scu.finch");
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
startService(i); //多次启动
}
}
// IntentService源码中的 onCreate() 方法
@Override
public void onCreate() {
super.onCreate();
// HandlerThread继承自Thread,内部封装了 Looper
//通过实例化HandlerThread新建线程并启动
//所以使用IntentService时不需要额外新建线程
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
//获得工作线程的 Looper,并维护自己的工作队列
mServiceLooper = thread.getLooper();
//将上述获得Looper与新建的mServiceHandler进行绑定
//新建的Handler是属于工作线程的。
mServiceHandler = new ServiceHandler(mServiceLooper);
}
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
// IntentService的handleMessage方法把接收的消息交给onHandleIntent()处理
// onHandleIntent()是一个抽象方法,使用时需要重写的方法
@Override
public void handleMessage(Message msg) {
// onHandleIntent 方法在工作线程中执行,执行完调用 stopSelf() 结束服务。
onHandleIntent((Intent) msg.obj);
//onHandleIntent 处理完成后 IntentService会调用 stopSelf() 自动停止。
stopSelf(msg.arg1);
}
}
// onHandleIntent()是一个抽象方法,使用时需要重写的方法
@WorkerThread
protected abstract void onHandleIntent(Intent intent);
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
//把 intent 参数包装到 message 的 obj 中,然后发送消息,即添加到消息队列里
//这里的Intent 就是启动服务时startService(Intent) 里的 Intent。
msg.obj = intent;
//每次startService()的时候,就会最终发送消息给onCreate()中的handleMessage(),然后回调被复写的onHandleIntent处理耗时逻辑
mServiceHandler.sendMessage(msg);
}
//清除消息队列中的消息
@Override
public void onDestroy() {
mServiceLooper.quit();
}
通过HandlerThread单独开启一个名为IntentService的线程
创建一个名叫ServiceHandler的内部Handler
把内部Handler与HandlerThread所对应的子线程进行绑定
通过onStartCommand()传递给服务intent,依次插入到工作队列中,并逐个发送给onHandleIntent()
通过onHandleIntent()来依次处理所有Intent请求对象所对应的任务
调用时传入不同的Intent(比如用Action区分)
复写方法onHandleIntent(),在里面根据Intent的不同进行不同的线程操作
工作任务队列是顺序执行的
原因:
由于onCreate() 方法只会调用一次,所以只会创建一个工作线程
当多次调用 startService(Intent) 时(onStartCommand也会调用多次)其实并不会创建新的工作线程,只是把消息加入消息队列中等待执行,所以多次启动 IntentService 会按顺序执行事件
如果服务停止,会清除消息队列中的消息,后续的事件得不到执行。
quit()
而不是quitSafe()
关键点:使用了Lru(最近最少使用)算法
基本理解
LruCache是个泛型类
主要算法原理是把最近使用的对象用强引用(即我们平常使用的对象引用方式)存储在 LinkedHashMap 中
当缓存满时,把最近最少使用的对象从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。
eg:图片缓存
int maxMemory = (int) (Runtime.getRuntime().totalMemory() / 1024);
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight() / 1024;
}
};
LinkedHashMap 继承自 HashMap
双向链表的结构可以实现访问顺序和插入顺序,使得LinkedHashMap中的对按照一定顺序排列
//其中accessOrder设置为true则为访问顺序,为false,则为插入顺序
public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
public static final void main(String[] args) {
LinkedHashMap map = new LinkedHashMap<>(0, 0.75f, true);
map.put(0, 0);
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
map.put(4, 4);
map.put(5, 5);
map.put(6, 6);
map.get(1);
map.get(2);
for (Map.Entry entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
0:0 //链表尾
3:3
4:4
5:5
6:6
1:1
2:2 //链表头
使用HashMap+双向链表好处:
双向链表用于LRU中数据的存储和对使用时间新旧的维护
HashMap是来配合双向链表,用于减少时间复杂度
不使用单链表原因:
put()
方法
trimToSize()
方法,来判断缓存是否已满,如果满了就要删除近期最少使用的算法public final V put(K key, V value) {
//不可为空,否则抛出异常
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
//插入的缓存对象值加1
putCount++;
//增加已有缓存的大小
size += safeSizeOf(key, value);
//向map中加入缓存对象(put返回value)
previous = map.put(key, value);
//如果已有缓存对象,则缓存大小恢复到之前
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
//entryRemoved()是个空方法,可以自行实现
if (previous != null) {
entryRemoved(false, key, previous, value);
}
//调整缓存大小(关键方法)
trimToSize(maxSize);
return previous;
}
trimToSize()
方法public void trimToSize(int maxSize) {
//死循环
while (true) {
K key;
V value;
synchronized (this) {
//如果map为空并且缓存size不等于0或者缓存size小于0,抛出异常
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
//如果缓存大小size小于最大缓存,或者map为空,不需要再删除缓存对象,跳出循环
if (size <= maxSize || map.isEmpty()) {
break;
}
//迭代器获取第一个对象,即队尾的元素,近期最少访问的元素
Map.Entry toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
//删除该对象,并更新缓存大小
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
get()
方法与recordAccess()
get()
-> LinkedHashMap的get()
-> LinkedHashMap的recordAccess()
实现排序void recordAccess(HashMap m) {
LinkedHashMap lm = (LinkedHashMap)m;
//判断是否是访问排序
if (lm.accessOrder) {
lm.modCount++;
//删除此元素
remove();
//将此元素移动到队列的头部
addBefore(lm.header);
}
}
不负责视图控制,只是控制生命周期和处理事件
一个Activity包含了一个Window
Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow
Window是视图的承载器
Window 通过WindowManager
将DecorView加载其中
Window将DecorView交给ViewRoot,进行视图绘制以及其他交互
是FrameLayout的子类
是Android视图树的根结点视图
内容:包含一个竖直方向的LinearLayout,里面有三个部分
1⃣ViewStub
:延迟加载的视图(应该是设置ActionBar,根据Theme设置)
2⃣标题栏:根据Theme设置,有的布局没有
3⃣内容栏:Activity中setContentView
所设置的布局文件,是内容栏唯一的子View
所有View的绘制及事件分发等交互都是通过它来执行或传递的
View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成
Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的
ViewRoot对应ViewRootImpl
类
ViewRoot并不属于View树的一份子
Activity持有一个PhoneWindow(继承Window抽象类的实现类)
PhoneWindow以内部类的形式持有一个DecorView
Window通过WindowManager将DecorView加载
Window将DecorView交给ViewRoot,进行视图绘制及其他交互
setContentView()
方法中通过Window的方法来装载视图public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
Activity通过attach()
方法生成PhoneWindow实例(获取Window对象)
attach()
方法在ActivityThread的handleLaunchActivity()
方法中先于生命周期开始执行然后Window在setContentView()
中通过installDecor()
方法创建DecorView
在insatallDecor()
中,通过generateDecor()
方法从主题中获取样式,然后根据样式,通过decor.addView()
加载布局到DecorView中
然后从DecorView中通过findViewById
获取mContentParent
mContentParent
即布局中@android:id/content所对应的FrameLayout。目前DecorView还只是创建,但还没有显示
只有在onResume()
的时候才能显示到前台
在ActivityThread中的handleLaunchActivity()
中通过handleResumeActivity()
来回调Activity.onResume()
然后在handleResumeActivity()
中通过windowManager.addView()
方法将DecorView添加进WindowManager中,并创建ViewRootImpl对象
addView()
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
......
synchronized (mLock) {
ViewRootImpl root;
//实例化一个ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
......
try {
//将DecorView交给ViewRootImpl
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
}
}
addView()
方法中将DecorView交给ViewRootImpl,执行一系列的setView()
方法setView()
方法中最终调用到performTraversals()
方法,开始View的三大绘制流程最后在handleResumeActivity()
中通过makeViesable()
的mDecor.setVisibility(View.VISIBLE)
可见来使界面可见
ViewRootImpl是个连接器
通过硬件的感知来通知视图,进行用户之间的交互
负责WindowManagerService与DecorView之间的通信
硬件 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity
用户点击屏幕产生一个触摸行为,这个触摸行为则是通过底层硬件来传递捕获,然后交给ViewRootImpl,接着将事件传递给DecorView,而DecorView再交给PhoneWindow,PhoneWindow再交给Activity,然后接下来就是我们常见的View事件分发了
setMeasuredDimension()
:设定View的宽高信息,完成View的测量操作
View的布局流程
方法名 | 特性 |
---|---|
onMeasure()方法 | 单一View,一般重写此方法,针对wrap_content情况,规定View默认的大小值,避免于match_parent情况一致。ViewGroup,若不重写,就会执行和单子View中相同逻辑,不会测量子View。一般会重写onMeasure()方法,循环测量子View。 |
onLayout()方法 | 单一View,不需要实现该方法。ViewGroup必须实现,该方法是个抽象方法,实现该方法,来对子View进行布局。 |
onDraw()方法 | 无论单一View,或者ViewGroup都需要实现该方法,因其是个空方法 |
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//加载图片
BitmapFactory.decodeResource(res,resId,options);
//计算缩放比
options.inSampleSize = calculateInSampleSize(options,reqHeight,reqWidth);
//重新加载图片
options.inJustDecodeBounds =false;
return BitmapFactory.decodeResource(res,resId,options);
}
private static int calculateInSampleSize(BitmapFactory.Options options, int reqHeight, int reqWidth) {
int height = options.outHeight;
int width = options.outWidth;
int inSampleSize = 1;
if(height>reqHeight||width>reqWidth){
int halfHeight = height/2;
int halfWidth = width/2;
//计算缩放比,是2的指数
while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
}
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.mipmap.ic_launcher,100,100);
描述:Context提供了关于应用环境全局信息的接口
是一个抽象类,它的执行被Android系统所提供
public abstract class Context {
/**
* File creation mode: the default mode, where the created file can only
* be accessed by the calling application (or all applications sharing the
* same user ID).
* @see #MODE_WORLD_READABLE
* @see #MODE_WORLD_WRITEABLE
*/
public static final int MODE_PRIVATE = 0x0000;
public static final int MODE_WORLD_WRITEABLE = 0x0002;
public static final int MODE_APPEND = 0x8000;
public static final int MODE_MULTI_PROCESS = 0x0004;
.
.
.
}
ContextWrapper类
如其名所言,这只是一个包装而已,ContextWrapper构造函数中必须包含一个真正的Context引用
同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象
调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。
ContextThemeWrapper类
如其名所言,其内部包含了与主题(Theme)相关的接口,
只有Activity会需要主题
ContextImpl类
Context在应用中的具体实现
(1)Application
(2)Activity
(3)Service
(1)View.getContext
(2)Activity.getApplicationContext
获取当前Activity所在的(应用)进程的Context对象,通常我们使用Context对象时,要优先考虑这个全局的进程Context。
与getApplication()
区别:结果都是获取到Application的Context,但getApplication()只有在Activity和Service中才能调用的到
(3)ContextWrapper.getBaseContext()
(3)Activity.this
public class Singleton {
private static Singleton instance;
private Context mContext;
private Singleton(Context context) {
this.mContext = context;
}
public static Singleton getInstance(Context context) {
if (instance == null) {
instance = new Singleton(context);
}
return instance;
}
}
public class MainActivity extends Activity {
private static Drawable mDrawable;
@Override
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
ImageView iv = new ImageView(this);
mDrawable = getResources().getDrawable(R.drawable.ic_launcher);
iv.setImageDrawable(mDrawable);
}
}
(1)当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
(2)不要让生命周期长于Activity的对象持有到Activity的引用。
(3)尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。