skylarliuu
skylarliuu
以上代码的意思应该是让Person的年龄值参与了hashCode值计算,因此改变年龄值会改变P3的hashCode值,同时因为hashCode值改变,定位不到原来的P3对象,也就删除不掉P3。
RecyclerView是Android 5.0推出的一个可以展示大量数据的、灵活的控件。 ### 优势 - 支持线性,网格和瀑布流三种布局 - 强制实现ViewHolder - 提供友好的ItemAnimator动画 - 解耦的架构设计 - 比ListView有更好的性能 ### 重要组件 - LayoutManager:定位和布局Item - ItemAnimator:Item动画 - Adapter:创建View并绑定数据 ### 性能优化 - 使用LinearLayoutManager.setInitialPrefetchItemCount(count)设置嵌套在内部的横向RecyclerView的初次显示Item个数 - Item高度固定时(数据变化也不会导致Item高度变化),使用RecyclerView.setHasFixedSize(true) - 多个RecyclerView共用一个RecyclerViewPool...
首先分析一下Down事件。事件传递顺序是从底层一层一层往上传的,从A到B,到C,每一层View都有消费事件的机会,如果大家都不消费,又会一层一层回传回去,从C到B到A,最终到最底层的Activity消费掉事件。 从源码角度上来细看Down事件: 1. 首先会调用ViewGroup A的dispatchTouchEvent 2. dispatchTouchEvent中会通过onInterceptTouchEvent判断是否拦截此事件 3. 如果拦截直接调用ViewGroup的onTouchEvent 3. 如果不拦截就通过child.dispatchTouchEvent遍历调用每个在触摸范围内的子View,让子View自己处理是否消费事件。 4. 如果子View都不消费,就调用super.dispatchTouchEvent,实际上就是调用ViewGroup的onTouchEvent. dispatchTouchEvent主要分发事件,真正做事件处理是在onTouchEvent中,如果onTouchEvent返回true就代表view消费了这个事件,并且也将消费这组事件序列中剩余的事件。 Down事件走完流程后,会找到最终消费这个事件序列的View。后面的事件就会直接被该View接管。所以接下来的Move、Up事件,默认都是从底层一层一层传到该View。 正常的事件流程就是这样了。但是还有一种情况,就是底层的ViewGroup可能最开始不会拦截事件,将Down以及一些Move事件给子view消费,但是当ViewGroup的onInterceptTouchEvent中检测到手指滑动达到一定的条件时,就可以在onInterceptTouchEvent返回true,以拦截这个事件以及这组事件序列中剩余事件。一旦onInterceptTouchEvent返回true,该ViewGroup就默认拦截事件,不会再调用onInterceptTouchEvent,它的子 view也不能再消费事件了。此时View Group抢夺了子View的事件,子View会收到一个Cancel事件。
AsyncTask主要用来执行耗时操作,同时它把执行进度和结果传递给UI线程,因此很适合一些需要在执行完耗时操作后更新UI或者执行耗时操作过程在UI上显示进度的场景。 AsyncTask内部封装了Handler和线程池,只暴露出五个主要方法给使用者,使用者可以方便地在对应的方法内去做耗时操作或者更新UI。 主要有五个方法: - onPreExecute:主线程中执行,可以用来做一些执行耗时操作之前的准备工作。 - doInBackground :在子线程中执行,必须实现的方法,用来做耗时操作。可以通过publishProgress方法调用onProgressUpdate显示下载进度。下载完成后会调用onPostExecute方法。 - onProgressUpdate:主线程中执行,调用publishProgress才会触发此方法。 - onPostExecute:主线程中执行,任务执行完成后会调用此方法。 - onCancelled:主线程中执行,任务取消时被调用。 AysncTask对象可以在主线程中创建,但是execute方法按照规范必须在主线程中执行。因为onPreExecute方法是在execute的线程中执行的。而构造函数内部的Handler则默认使用了主线程了Looper,所以AysncTask对象创建的线程不做要求。 AysncTask内部主要有两个线程池,SerialExecutor负责将任务同步加入队列中,ThreadPoolExecutor负责实际执行任务。
Android的Handler可以指定在某个运行中的线程中执行代码,一般我们会在子线程中做一些耗时操作,然后再借助Handler切换到主线程更新UI。Handler具备这种特性跟Looper、MessageQueue有密不可分的关系。 Handler的工作流程: 1. Handler发送消息 `handler.sendMessage(msg)`,这条message会记录发送的handler为target 2. sendMessage会调用到与Handler关联的Looper中的`messageQueue.enqueueMessage()` 3. enqueueMessage()就是向将message插入到MessageQueue中 4. 线程开始执行时,就会启动绑定的Looper,`Looper.loop()`让Looper一直循环地从内部的`MessageQueue.next()`方法中取Message。 5. `MessageQueue.next()`如果取到了Message就返回给Looper,Looper再把message交由message.target(即发送这条message的handler)去处理,如果当前没有消息,Looper就会阻塞,当前线程也会释放掉CPU进入休眠状态,直到有新消息来才被唤醒。 6. 最终调用到`handler.dispatchMessage()`中处理消息,dispatchMessage()方法是在创建Handler时所使用的Looper中执行的,这样就成功地将代码逻辑切换到指定的线程中去执行了。 Looper在哪个线程创建的,loop()就运行在哪个线程中,同样dispatchMessage()也是在这个线程执行。mainLooper是在 Android的主线程ActivityThread的main方法中调用`Looper.prepareMainLooper()`中初始化的,所以mainLooper是运行在主线程中的。而Handler的默认Looper就是mainLooper,所以能很方便地用Handler切换到主线程。
1. 内存溢出:当JVM无法给程序分配够程序需要的内存时,就会抛出OutOfMemoryError异常。 2. 内存泄漏:当生命周期长的对象持有生命周期短的对象的引用时,生命周期短的对象即使无用了,也不会被GC回收,此时发生了内存泄漏。 3. 常见发生内存泄漏的场景: - 匿名内部类和非静态内部类持有外部类的引用(非静态内部类的Handler) - 单例持有Activity的引用 - 当集合里面的对象属性被修改后,hashcode值已发生改变,再调用remove()方法时删除不掉这个对象 - 各种连接,比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。 4. 当内存泄漏现象越来越严重时,大量无用的对象还是存储在Java堆中得不到释放,就可能导致JVM的可用内存越来越少,当JVM没有足够的内存分配给程序时,就会产生内存溢出。