WarriorYu

山中莫道无供给,明月清风不用钱


  • 首页

  • 标签

  • 分类

  • 归档

实现在Recycleview中一键返回指定Position

发表于 2019-11-19 | 分类于 Android

问题:

怎么实现Recyclerview 中某个Item的子View(如图片中的TextView3),无论当前列表滑动到什么位置,让这个子View可以平滑滚动到屏幕顶部?

答:

如果想平滑滚动到RecyclerView的某个Item,可以通过RecyclerView.smoothScrollToPosition(position);我们一般是当列表往上滑动一段距离后,显示一个「返回顶部」的按钮,点击返回到顶部,这个方法很完美。但是有一个特殊的场景:当点击按钮后,不是滑动到顶部,而是滑动到还没有显示出来的某个Item(比如要求滑动到position=10,当前在position=1),这时候滑动出来的position=10的Item的底部贴在在屏幕的底部,而不是Item的顶部贴在屏幕的顶部。如果RecyclerView是垂直的,想解决这个问题,需要自定义一个LinearLayoutManager,通过RecyclerView.setLayoutManager(new LinearLayoutManagerWithSmoothScroller(context));替换掉原来的LinearLayoutManager。看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class LinearLayoutManagerWithSmoothScroller extends LinearLayoutManager {

public LinearLayoutManagerWithSmoothScroller(Context context) {
super(context, RecyclerView.VERTICAL, false);
}

public LinearLayoutManagerWithSmoothScroller(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}

@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}

private class TopSnappedSmoothScroller extends LinearSmoothScroller {
public TopSnappedSmoothScroller(Context context) {
super(context);

}

@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return LinearLayoutManagerWithSmoothScroller.this
.computeScrollVectorForPosition(targetPosition);
}

@Override
protected int getVerticalSnapPreference() {
return SNAP_TO_START;
}
}
}

再来看题目的要求,如果想把Textview3置顶的话,可以计算Textview3距离屏幕顶部的距离,通过RecyclerView.smoothScrollBy(x, y)平滑滚到到指定的距离。这个距离的计算可以通过下面我贴出来的「代码一」获取Textview3在屏幕中的坐标,但是在小米手机上有时候获取的坐标偶尔有正确的值,偶尔是[0,0],我也没有找到是什么原因导致的这个问题,如果你有好的想法,欢迎来告诉我。但是我们可以通过下面的「代码二」获取Textview3距离屏幕顶部的精确距离。

「代码一」

1
2
3
4
5
6
7
8
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
int[] location = new int[2];
//获取在当前窗口内的绝对坐标
TextView3.getLocationInWindow(location);
int y = location[1];
}

「代码二」

1
2
3
4
5
6
TextView3.post(new Runnable() {
@Override
public void run() {
int top = TextView3.getTop()
}
});

因为没有RecyclerView.smoothScrollTo(x,y)这个方法,只获取到坐标还实现不了功能,我们可以通过一个迂回的方法,通过下面的「代码三」监听RecyclerView的滑动距离,通过「代码三」中计算的总的滑动距离和「代码二」中获取的top可以计算出来一个差值,通过这个差值,就可以使用RecyclerView.smoothScrollBy(0, top - scrollHeight)平滑滚到到指定的距离了。

「代码三」

1
2
3
4
5
6
7
recyView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int scrollHeight += dy;
}
});

注意:如果通过「代码三」这种方式监听滑动距离,千万不能使用View.scrollTo()这种方法让View跳到某个位置,这会导致「代码三」中的监听滑动距离失败,可以理解为scrollTo()这种方法是一下跳过一段距离,所以无法获取到滑动的距离。

如果你有更好的实现方式,或者对于在复杂列表中返回到指定的Position有什么分享的,欢迎来讨论。

学习树

发表于 2019-11-19 | 分类于 学习记录
一、已学习:
  1. HashMap:

    • 美团技术团队:https://tech.meituan.com/2016/06/24/java-hashmap.html
    • 简书:Carson_Ho的博客:https://blog.csdn.net/carson_ho/article/details/79373134
  2. 多线程:

    • 简书:Carson_Ho的博客:https://www.jianshu.com/nb/6977014
  3. Android 消息机制:
    • 基础篇:简书:Carson_Ho的博客

      Android Handler:手把手带你深入分析 Handler机制源码

    • 深入篇:

      Android 消息机制——你真的了解Handler?

    • Gityuan

      Android消息机制1-Handler(Java层)

  4. final关键字,为什么匿名内部类使用局部引用要用final?

    现在我们知道了,是由于一个拷贝的动作,使得内外两个变量无法实时同步,其中一方修改,另外一方都无法同步修改,因此要加上final限制变量不能修改。

    那么为什么要拷贝呢,不拷贝不就没那么多事了吗?

    这时候就得考虑一下Java虚拟机的运行时数据区域了,dog变量是位于方法内部的,因此dog是在虚拟机栈上,也就意味着这个变量无法进行共享,匿名内部类 也就无法直接访问,因此只能通过值传递的方式,传递到匿名内部类中

    从JDK1.8开始,编译器不要求自由变量一定要声明为final,如果这个变量在后面的使用中没有发生变化,就可以通过编译,Java称这种情况为“effectively final”

  5. RxJava:

    • 图文详解 背压策略
    • compose操作符:Don’t break the chain: use RxJava’s compose() operator
    • RxJava 沉思录:RxJava沉思录
  6. JDK8新特性(在笔记文件夹里有文档):

    • Lambda 函数式接口:Function/Predicate/Consumer/Supplier
    • Stream :forEach/filter/map/count/limit/skip/concat/两种获取Stream流的方式
    • 方法引用 : 通过对象名引用成员变量/类名引用静态成员/super引用父类的成员/this引用本类的成员/类的 构造器引用/数组的构造器引用
  7. Android主流三方库源码分析

    • 深入理解GreenDao源码
  8. 数据库框架设计(网易移动架构 -> 数据库架构设计):

    • 这个课程讲述的是通过反射的方式,但是反射耗费性能,GreeDao通过freeMarker使用模板的方式,通过使用自身的插件配套相应的freemarker模板生成所需的静态代码,避免了反射等消耗性能的操作。
二、待学习:
  1. 内存泄漏:

    • 内存分析:http://www.huahuaxie.com/android-performance-analysis/
  2. Gradle:

    • Gradle 篇 – 从使用到深入理解
  3. Github 有关学习:

    • Android 修炼手册

    • Tech Interview Guide 技术面试必备基础知识、Leetcode 题解、Java、C++、Python、后端面试、操作系统、计算机网络、系统设计

    • JsonChao/Awesome-Android-Interview

    • 一线互联网公司内部题库

    • nanchen:说说过去一周的面试和想法

  1. 剑指Offer(第二版)所有面试题总结

  2. JavaGuide

  3. 《Java面试进阶指南》

  4. RxJava:

    • RxJava2-Android-Samples

    • kaushikgopal/RxJava-Android-Samples

    • lzyzsd/Awesome-RxJava

    • RxJava沉思录

  5. Android复习资料整理

  6. jsonchao :https://juejin.im/post/5caf0f89f265da038145c66e https://github.com/JsonChao/Awesome-Android-Interview

    • GC机制、基本的热修复原理、简单的算法题和智力题,比如冒泡、查找
    • JAVA并发、JVM等知识,还有数据库、SQL语句、性能优化、海量Top k
三、第三方库:
  1. 大图加载方案:
    • implementation ‘me.panpf:sketch:2.7.1-rc01’(已使用)
    • implementation ‘com.koushikdutta.ion:ion:2.+’
    • implementation ‘com.shizhefei:LargeImageView:1.1.0’
    • implementation ‘com.github.SherlockGougou:BigImageViewPager:v4_4.1.0’
    • implementation ‘com.davemorrissey.labs:subsampling-scale-image-view:3.10.0’
    • implementation ‘com.github.piasy:BigImageViewer:1.5.7’
四、Android开发艺术探索
第二章 IPC机制
  1. 序列化和反序列化

    Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输,一般当我们使用缓存cache(内存空间不够有可能会本地存储到硬盘)或远程调用rpc(网络传输)的时候,经常需要让我们的实体类实现Serializable接口,目的就是为了让其可序列化。

    当然,序列化后的最终目的是为了反序列化,恢复成原先的Java对象,要不然序列化后干嘛呢,所以序列化后的字节序列都是可以恢复成Java对象的,这个过程就是反序列化。

  2. 静态成员变量属于类,不属于对象,所以不会参与序列化过程。对象的跨进程传输本质上都是反序列化的过程,这就是为什么AIDL中的自定义对象都要实现Parcelable的原因。

  3. Binder是Android中的一个类,它继承了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有;从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WorkManager等)和ManagerService的桥梁;从Android应用层来说,Binder是客服端和服务端通信的媒介,当bindService的时候,服务端会返回一个包含服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。

  4. Android 中的IPC方式

    • 使用Bundle:通过Intent中附加extras来传递信息
    • 使用文件共享:通过共享文件的方式来共享数据
    • 使用Messenger
    • 使用AIDL:通过Binder方式来跨进程通信
    • 使用ContentProvider:ContentProvider支持跨进程访问
    • 使用Socket:Socket通过网络通信实现数据传递
    • BroadcastReceiver:广播是一种被动跨进程通信方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。
第三章 View的事件体系
  1. 弹性滑动:
    • 使用Scroller
    • 通过动画
    • 使用延时策略
第四章 View的工作原理
  1. View的工作流程主要是指measure、layout、draw这三大流程,即测量、布局、绘制。其中measure确定View的测量宽/高,layout确定View的最终宽/高和四个顶点的位置,而draw则将View绘制到屏幕上。

  2. 直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content就相当于使用match_parent。

  3. 在某些极端情况下,系统可能需要多次measure才能确定最终的测量宽/高,在这种情形下,在onMeasure方法中拿到的测量宽/高可能是不准确的。一个比较好的习惯是在onLayout方法中去获取View的测量宽/高或者最终宽/高。

    在onCreate、onStart、onResume中均无法正确得到某个View的宽/高信息,这是因为View的measure过程和Activity的生命周期方法不是同步执行的,因此无法保证Activity执行了onCreate、onStart、onResume时某个View已经测量完毕。

    四种方法解决这个问题:

    • Activity/View#onWindowFocusChanged.

      1
      2
      3
      4
      5
      6
      7
      8
      @Override
      public void onWindowFocusChanged(boolean hasFocus) {
      super.onWindowFocusChanged(hasFocus);
      if (hasFocus) {
      int width = view.getMeasuredWidth();
      int height = view.getMeasuredHeight();
      }
      }
    • View.post(runnable)

      1
      2
      3
      4
      5
      6
      7
      view.post(new Runnable() {
      @Override
      public void run() {
      int width = view.getMeasuredWidth();
      int height = view.getMeasuredHeight();
      }
      });
    • ViewTreeObserver

      1
      2
      3
      4
      5
      6
      7
      8
      view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
      view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
      int width = view.getMeasuredWidth();
      int height = view.getMeasuredHeight();
      }
      });
    • View.measure(int widthMeasureSpec, int heightMeasureSpec)

      通过手动对View进行measure来得到View的宽/高。要分情况处理,根据View的LayoutParams来分:

      1. matche_parent无法measure出具体的宽/高,直接放弃。

      2. 具体的数值(dp/px),比如宽高都是100px,如下measure:

        1
        2
        3
        4
        int wideMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
        int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
        view.measure(wideMeasureSpec,heightMeasureSpec);
        Log.d(TAG, "measureView, width= " + view.getMeasuredWidth() + " height= " + view.getMeasuredHeight());
      3. wrap_content

        1
        2
        3
        4
        int wideMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
        int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(1<<30)-1, View.MeasureSpec.AT_MOST);
        view.measure(wideMeasureSpec,heightMeasureSpec);
        Log.d(TAG, "measureView, width= " + view.getMeasuredWidth() + " height= " + view.getMeasuredHeight());
  4. 自定义View避免内存泄露:

    • 只要针对View中含有线程或者动画的情况:当View退出或者不可见时,记得及时停止该View包含的线程和动画,否则会造成内存泄露的问题。
    • 启动或者停止线程/动画的方式:
      • a.启动线程/动画:使用view.onAttachedToWindow(),因为该方法的调用时机是当包含View的Activity启动的时刻。
      • b.停止线程/动画:使用view.onDetachedFromWindow(),因为该方法调用的时机是当包含View的Activity退出或当前View被remove的时刻。
第五章 理解RemoteViews
  1. PendingIntent支持三种待定意图:
    • 启动Activity
    • 启动Service
    • 发送广播BroadcastReciever
五、算法与数据结构
一. 玩转数据结构
  1. 线性结构:
    • 数组
    • 栈
    • 队列
    • 链表
    • 哈希表
    • 等等
  2. 树结构:
    • 二叉树
    • 二分搜索树
    • AVL
    • 红黑树
    • Treap
    • Splay
    • 堆
    • Trie
    • 线段树(字典树、前缀树)
    • K-D树
    • 并查集
    • 哈夫曼树
    • 等等
  3. 图结构:
    • 邻接矩阵
    • 邻接表
    • 等等
  4. 大量的算法,以数据结构为基石
  5. 数组基础:
    • 数组最大的优点:快速查询。scores[2]。
    • 数组最好应用于“索引有语义”的情况 。
    • 但并非所有有语义的索引都适用于数组,比如:身份证号 110126198845345683,需要开辟很大的内存空间。
  6. 泛型:不可以使用基本数据类型,但是可以使用基本数据类型的包装类。自动拆箱,自动装箱。
  7. 动态数组、栈、队列:底层依托静态数组,靠resize解决容量问题。
  8. 链表:真正的动态数据结构。
    • 优点:真正的动态,不需要处理固定容量问题。
    • 缺点:丧失了随机访问的能力。
  9. 极客时间有关链表的题目推荐:

    • 单链表反转 (Leetcode-206)
    • 链表中环的检测(Leetcode-141)
    • 两个有序的链表合并 (Leetcode-21)
    • 删除链表倒数第 n 个结点(Leetcode-19)
    • 求链表的中间结点(Leetcode-876)
  10. 堆:

  • 将n个元素逐个插入到一个空堆中,时间复杂度是O(nlogn)。因为每次添加一个元素,往上堆化的过程是O(logn),n个元素就是O(nlogn)。

  • heapify的方式,时间复杂度是O(n)。

二. 玩转算法
  1. 二分查找法:

    • 对于有序序列,才能使用二分查找法
  2. 数组:

    • 双指针

    • 双索引技术(滑动窗口)

六、我对问题的理解
  1. GC机制:

    首先

七、深入理解Java虚拟机
第一章:走进Java
  1. 从Android5.0开始,Dalvik虚拟机全面替代ART虚拟机。
八、动脑:移动架构
  1. TreadLocal的工作原理:
    • 定义:ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
九、资源索引:
  1. Android系统源码8.0:
    • 网盘下载

      链接:https://pan.baidu.com/s/1Vc0rWucfsbse_5TdgtROxw

      提取码:wy00

      复制这段内容后打开百度网盘手机App,操作更方便哦

    • 在线地址:

      http://androidxref.com/

    • 阅读源码辅助文档:

      https://www.cnblogs.com/CVstyle/p/6395662.html

  2. 优秀技术博客:

    • jsonchao
    • Gityuan
    • Carson_Ho
  3. Android 学习笔记:

    • GcsSloop/AndroidNote
  4. Git可视化练习工具:

    • 练习使用Git的一个开源项目,Github 项目地址:https://github.com/pcottle/learnGitBranching

    • 教程网站地址:https://learngitbranching.js.org

  5. SQL:

    • SQLZOO 是一款很好用的 SQL 练习平台:https://sqlzoo.net/
十. 书籍
  1. Darren:
    • 《正念的奇迹》

我要学Android-【算法篇(1)】

发表于 2019-03-26 | 分类于 算法和数据结构
  1. 如何理解”栈“
    • 后进者先出,先进者后出,这就是典型的“栈”结构。
    • 栈是一种“操作受限”的线性表,只允许在一端插入和删除数据。
    • 当某个数据集合只涉及在一端插入和删除数据,并且满足后进先出、先进后出的特性,我们就应该首选“栈”这种数据结构
  2. 栈在括号匹配中的应用

    我们用栈来保存未匹配的左括号,从左到右依次扫描字符串。当扫描到左括号时,则将其压入栈中;当扫描到右括号时,从栈顶取出一个左括号。如果能够匹配,比如“(”跟“)”匹配,“[”跟“]”匹配,“{”跟“}”匹配,则继续扫描剩下的字符串。如果扫描的过程中,遇到不能配对的右括号,或者栈中没有数据,则说明为非法格式。

    当所有的括号都扫描完成之后,如果栈为空,则说明字符串为合法格式;否则,说明有未匹配的左括号,为非法格式。

  3. 如何实现浏览器的前进和后退功能?

    我们使用两个栈,X 和 Y,我们把首次浏览的页面依次压入栈 X,当点击后退按钮时,再依次从栈 X 中出栈,并将出栈的数据依次放入栈 Y。当我们点击前进按钮时,我们依次从栈 Y 中取出数据,放入栈 X 中。当栈 X 中没有数据时,那就说明没有页面可以继续后退浏览了。当栈 Y 中没有数据,那就说明没有页面可以点击前进按钮浏览了。

  4. 为什么函数调用要用“栈”来保存临时变量呢?用其他数据结构不行吗?

    其实,我们不一定非要用栈来保存临时变量,只不过如果这个函数调用符合后进先出的特性,用栈这种数据结构来实现,是最顺理成章的选择。

    从调用函数进入被调用函数,对于数据来说,变化的是什么呢?是作用域。所以根本上,只要能保证每进入一个新的函数,都是一个新的作用域就可以。而要实现这个,用栈就非常方便。在进入被调用函数的时候,分配一段栈空间给这个函数的变量,在函数结束的时候,将栈顶复位,正好回到调用函数的作用域内。

我要学Android-【算法篇(0)】

发表于 2019-03-25 | 分类于 算法和数据结构
  1. 概念:数据结构指的是“一组数据的存储结构”,算法指的是“操作数据的一组算法”。

  2. 大O时间复杂度表示法(T(n)=O(f(n)):

    所有代码的执行时间 T(n) 与每行代码的执行次数 n 成正比

    大O时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以,也叫作渐进时间复杂度(asymptotic time complexity),简称时间复杂度。

  3. 复杂度量级(按数量级递增)

    • 常量阶 O(1)
    • 对数阶 O(logn)
    • 线性阶 O(n)
    • 线性对数阶 O(nlogn)
    • 平方阶 O(n²)
    • 立方阶 O(n³)
    • K次方阶 O(n^k^)
    • 指数阶 O(2^n^)
    • 阶乘阶 O(n!)
  4. 空间复杂度分析

    时间复杂度的全称是渐进时间复杂度,表示算法的执行时间与数据规模之间的增长关系。类比一下,空间复杂度全称就是渐进空间复杂度(asymptotic space complexity),表示算法的存储空间与数据规模之间的增长关系。

    常见的复杂度并不多,从低阶到高阶有:O(1)、O(logn)、O(n)、O(nlogn)、O(n2 )

  5. 最好情况时间复杂度(best case time complexity)、最坏情况时间复杂度(worst case time complexity)、平均情况时间复杂度(average case time complexity)、均摊时间复杂度(amortized time complexity)。

  6. 数组
    数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

    如何实现随机访问?

    • 第一是线性表。(线性表:数组、链表、队列、栈 非线性表:树 图)
    • 第二个是连续的内存空间和相同类型的数据
    • 正是因为这两个限制,它才有了一个堪称“杀手锏”的特性:“随机访问”。但有利就有弊,这两个限制也让数组的很多操作变得非常低效,比如要想在数组中删除、插入一个数据,为了保证连续性,就需要做大量的数据搬移工作。
  7. 数组是如何实现根据下标随机访问数组元素的

1
2
3
4
5
 拿一个长度为 10 的 int 类型的数组 int[] a = new int[10] 来举例:

计算机会给每个内存单元分配一个地址,计算机通过地址来访问内存中的数据。当计算机需要随机访问数组中的某个元素时,它会首先通过下面的寻址公式,计算出该元素存储的内存地址:

a[i]_address = base_address + i * data_type_size
内存块的首地址为 **base_address** = 1000。
**data_type_size** 表示数组中每个元素的大小
  1. *数组和链表的区别**

    “链表适合插入、删除,时间复杂度 O(1);数组适合查找,查找时间复杂度为 O(1)”。

    实际上,这种表述是不准确的。数组是适合查找操作,但是查找的时间复杂度并不为 O(1)。即便是排好序的数组,你用二分查找,时间复杂度也是 O(logn)。所以,正确的表述应该是,数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)。

  2. 数组为了保持内存数据的连续性,会导致插入、删除这两个操作比较低

    低效的“插入”和“删除”的优化:

    插入

  • 长度是n的数组,在第K个位置插入一个数据,需要将第k~n这部分的元素都顺序往后挪一位。
  • 如果在末尾插入元素,不需要移动数据,这时的时间复杂度是O(1)。
  • 如果在数组的开头插入数据,那所有数据都需要往后移动一位,所以最坏时间复杂度是O(n)。
  • 由于在每个位置插入元素的概率是一样的,所有平均情况时间复杂度为 (1+2+…n)/n=O(n)。
  • 如果数组存储的数据没有任何规律,直接将第K位的数据搬移到数组元素的最后,把新的元素直接放入第K个位置。这种处理技巧下,在第 k 个位置插入一个元素的时间复杂度就会降为 O(1)。

删除

  • 和插入类似,如果删除数组末尾的数据,则最好情况时间复杂度为 O(1);如果删除 开头的数据,则最坏情况时间复杂度为 O(n);平均情况时间复杂度也为 O(n)。
  • 数组 a[10] 中存储了 8 个元素:a,b,c,d,e,f,g,h。现在,我们要依次删除 a,b,c 三个元素。
  • 为了避免 d,e,f,g,h 这几个数据会被搬移三次,我们可以先记录下已经删除的数据。每次的删除操作并不是真正地搬移数据,只是记录数据已经被删除。当数组没有更多空间存储数据时,我们再触发执行一次真正的删除操作,这样就大大减少了删除操作导致的数据搬移。
  1. 容器能否完全替代数组?
  • ArrayList 最大的优势就是可以将很多数组操作的细节封装起来。比如前面提到的数组插入、删除数据时需要搬移其他数据等。另外,它还有一个优势,就是支持动态扩容。
  • 扩容操作涉及内存申请和数据搬移,是比较耗时的。所以,如果事先能确定需要存储的数据大小,最好在创建 ArrayList 的时候事先指定数据大小。
  1. 有些时候,用数组会更合适些
  • Java ArrayList 无法存储基本类型,比如 int、long,需要封装为 Integer、Long 类,而 Autoboxing、Unboxing 则有一定的性能消耗,所以如果特别关注性能,或者希望使用基本类型,就可以选用数组
  • 如果数据大小事先已知,并且对数据的操作非常简单,用不到 ArrayList 提供的大部分方法,也可以直接使用数组。
  • 当要表示多维数组时,用数组往往会更加直观。比如 Object[][] array;而用容器的话则需要这样定义:ArrayList array.

总结,对于业务开发,直接使用容器就足够了,省时省力。毕竟损耗一丢丢性能,完全不会影响到系统整体的性能。但如果你是做一些非常底层的开发,比如开发网络框架,性能的优化需要做到极致,这个时候数组就会优于容器,成为首选。

  1. 为什么大多数编程语言中,数组要从 0 开始编号,而不是从 1 开始呢?

从数组存储的内存模型上来看,“下标”最确切的定义应该是“偏移(offset)”。如果用 a 来表示数组的首地址,a[0] 就是偏移为 0 的位置,也就是首地址,a[k] 就表示偏移 k 个 type_size 的位置,所以计算 a[k] 的内存地址只需要用这个公式:

1
a[k]_address = base_address + k * type_size

但是,如果数组从 1 开始计数,那我们计算数组元素 a[k] 的内存地址就会变为:

1
a[k]_address = base_address + (k-1)*type_size

对比两个公式,我们不难发现,从 1 开始编号,每次随机访问数组元素都多了一次减法运算,对于 CPU 来说,就是多了一次减法指令。

数组作为非常基础的数据结构,通过下标随机访问数组元素又是其非常基础的编程操作,效率的优化就要尽可能做到极致。所以为了减少一次减法操作,数组选择了从 0 开始编号,而不是从 1 开始。

常用指令和快捷键

发表于 2019-01-07 | 分类于 指令/快捷键
一. App的内存使用情况概览

超过单个程序限制最大内存则OOM,如果设置了开启largeHeap,则可提高到dvm最大内存才OOM。

1
2
//dvm最大可用内存:
adb shell getprop | grep dalvik.vm.heapsize
1
2
//单个程序限制最大可用内存:
adb shell getprop|grep heapgrowthlimit

输出我们App的内存使用情况概览:

1
adb shell dumpsys meminfo 包名
二. 局域网提供文件加载

在命令行定位到需要供下载的文件夹,键入命令 python -m SimpleHTTPServer 就可以,用户通过浏览器网址中输入本机的ip地址下载

三. MarkDown相关

​ 换行:空格+空格+回车

​ 显示非列表的序号:可以使用:数字. 来取消显示为列表 例如:05.

MarkDown快捷键:

1、源代码模式: command+/

2、标题的使用格式

# 一阶标题 或者快捷键command++1

##二阶标题 或者快捷键command++2

###三阶标题 或者快捷键command++3

####四阶标题 或者快捷键command++4

#####五阶标题 或者快捷键command++5

######六阶标题 或者快捷键command++6

3、文本居中使用格式

这是要居中的文本内容

4、下划线使用格式

快捷键command+U 或者 下划线的内容

5、删除线使用格式

删除线的内容

删除线在Typora显示形式是:这是删除线的内容

6、字体加粗使用格式

快捷键command+B 或者 加粗字体

字体加粗在Typora显示形式是:加粗字体 This is a bold font

7、字体倾斜使用格式

快捷键command+I 或者 字体倾斜了

字体倾斜在Typora的显示形式:字体倾斜了 This is a alanted font

8、超链接使用格式

第一种:http://www.simba.com

第二种:快捷键Ctrl+K

按住command键+点击上面链接就可以直接访问该链接

9、代码区域的使用格式

快捷键:shift+command+` 或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14

10、表格的二种使用格式

快捷键command+T,会自动跳出设置行和列的设置框

11、任务列表使用格式

\- [ ] 文字 (注:注意用空格隔开)

- java
- C
- Android

12、列表

alt+command+O: 有序列表
`

​ alt+command+U:无序列表

普通类、抽象类实现接口的区别

发表于 2018-10-10 | 分类于 Java基础

普通类、抽象类实现接口的区别

1、普通类实现接口,需要实现接口里所有的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*接口*/
public interface BaseView {
void showLoadingDialog(String msg);

void dismissLoadingDialog();
}

/*普通类*/
public class MyView implements BaseView{
@Override
public void showLoadingDialog(String msg) {

}

@Override
public void dismissLoadingDialog() {

}
}

2、抽象类实现接口,可以选择实现或者部分实现(包括完全不实现)接口里的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class MyView implements BaseView{
@Override
public void showLoadingDialog(String msg) {

}
}

当我们需要实现接口里的部分方法时,可以先写一个抽象类来实现该接口,并且实现除了我们想要的方法之外的方法,也就是我们在抽象类里实现不需要的方法,当我们的类(RootView)去继承这个抽象类时,RootView中就只需要实现我们需要的方法了。
public class RootView extends MyView {

@Override
public void dismissLoadingDialog() {

}
}
总结:
抽象类有个好处就是,它可以有方法的具体实现,也可以不写方法的具体实现,而接口里是没有具体实现的。
一个类如果实现一个接口,那它必然要实现接口里的所有方法,而如果一个抽象类实现一个接口的话,抽象类里可以只写部分方法或者完全不重写任何方法。

Android ABI、CPU、so文件浅析

发表于 2018-09-28 | 分类于 CPU

Android ABI的概念

ABI全称:Application binary interface(应用程序二进制接口),定义了一套规则,允许编译好的二进制目标代码能在所有兼容该ABI的操作系统中无需改动就能运行。

不同的Android手机使用不同的CPU,因此需要提供对应的二进制接口交互规则(即对应的ABI文件)才能进行交互。

部分CPU是能支持多种交互规则,但这是在牺牲性能的前提下所做的兼容。

主流的ABI架构

  1. armeabiv-v7a: 第7代及以上的 ARM 处理器。2011年以后的生产的大部分Android设备都使用它。
  2. arm64-v8a: 第8代、64位ARM处理器,很少设备,三星 Galaxy S6是其中之一。
  3. armeabi: 第5代、第6代的ARM处理器,早期的手机用的比较多。
  4. x86: 平板、模拟器用得比较多。
  5. x86_64: 64位的平板。

ABI和CPU的关系

当一个应用被安装在设备上时,只有该设备支持的CPU架构对应的.so文件会被安装,如果支持多个ABI架构,会按照优先级进行按照具体的支持类型如下

ARMv5(CPU):armeabi(ABI)
ARMv7:armeabi,armeabi-v7a
ARMv8:armeabi,armeabi-v7a,arm64-v8a
MIPS:mips
MIPS64:mips,mips64
x86:x86(1),armeabi(2),armeabi-v7a(3)
x86_64:armeabi,x86,x86_64

可以看出CPU大都是向前兼容的,但是选择ABI时会有个优先级。

比如X86型的CPU,优先选择x86目录下的.so包,如果存在,就不会再安装其他支持的ABI架构;如果没有x86目录,才会选择armeabi-v7a目录下的.so,最后才会选择armeabi目录下的.so文件。

ps:X86设备虽然能够运行armeabi下的so库,但可能会损失性能,而且不能保证一定不会发生crash,尤其是有小公司出产的so库

使用过程中的问题

.so文件,放入了优先级低的ABI目录

设备CPU架构是ARMv7,ABI文件是armeabi-v7a,但是放进了armeabi目录中

1.如果项目中有armeabi-v7a目录,目录下没有so库,ARMv7优先加载armeabi-v7a目录,发现没有对应的so库,会报错。

1
Caused by: java.lang.UnsatisfiedLinkError

2.项目中只有armeabi的目录,ARMv7会加载armeabi目录,同时加载目录下的so库,相当于加载了一个armeabi规则的so库,因为向前兼容的特性,不会报错,也能运行,但性能会损失。

.so文件放入优先级高的ABI目录

设备CPU架构是ARMv7,ABI文件是armeabi,但是放进了armeabi-v7a目录中下。

可以被加载,但是能不能被使用?我也不确定。因为armeabi的so库不一定能支持armeabi-v7a定下的接口交互规则。

多个第三方的SDK中的ABI文件优先级不一样。

两个第三方的SDK中ABI文件优先级不一样,手机加载运行时,会导致优先级低的库,无法被加载

我的手机cpu架构是ARMv7,项目中使用两个第三方SDK:企业A和企业B

企业A:ABI文件是armeabi-v7a,放进armeabi-v7a目录中。
企业B:ABI文件是armeabi-v5te,放进armeabi目录中。

在运行时,会发现运行后crash,出现如下log信息。

1
Caused by: java.lang.UnsatisfiedLinkError

这是因为低优先级的也就是企业B的so包没有被加载进来,也就无法正常使用导致报错。

解决办法:

1、使用同一优先级的ABI文件,ABI文件放入优先级相同的ABI目录

企业A:ABI文件是armeabi-v5te,放进armeabi目录中。
企业B:ABI文件是armeabi-v5te,放进armeabi目录中。
或

企业A:ABI文件是armeabi-v7a,放进armeabi-v7a目录中。
企业B:ABI文件是armeabi-v7a,放进armeabi-v7a目录中。

2、使用不同优先级的ABI文件,ABI文件放入优先级相同的ABI目录。一般情况不建议这么做。

企业A:ABI文件是armeabi-v7a,但是放进armeabi目录中。
企业B:ABI文件是armeabi-v5te,放进armeabi目录中。
或

企业A:ABI文件是armeabi-v7a,放进armeabi-v7a目录中。
企业B:ABI文件是armeabi-v5te,但是放进armeabi-v7a目录中。

so文件的重要法则

处理.so文件时有一条简单却并不知名的重要法则。
你应该尽可能的提供专为每个ABI优化过的.so文件

NDK的兼容性

使用NDK时,选择app的minsdkVersion对应的编译平台,因为NDK是向后兼容的

注意事项

所有ABI文件夹提供的so要保持一致

如果我们的应用选择了支持多个ABI,要十分注意:对于每个ABI下的so,但要么全部支持,要么都不支持。不应该混合着使用,而应该为每个ABI目录提供对应的.so文件。

支持过多的so会导致apk体积增大

第一种:支持全部的7种ABI架构,APK大小为70M

1
2
3
4
5
defaultConfig {
ndk {
abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "mips", "mips64", "x86", "x86_64"
}
}

第二种:支持一种ABI架构,APK大小为30M

1
2
3
4
5
defaultConfig {
ndk {
abiFilters "armeabi-v7a"
}
}

参考

Android ABI的浅析

搭建个人博客和Git的基本指令

发表于 2018-09-17 | 分类于 博客搭建

Create a new post

1
$ hexo new "My New Post"

清除缓存

$ hexo clean

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

Git 的使用

Git 文件状态介绍

  • 已修改(modified) ———— 表示修改了文件,但还没保存到数据库中
  • 已暂存(staged) ———— 表示对一个已修改文件的当前版本做了追踪,使之包含在下次提交的快照中
  • 已提交(committed)———— 表示数据已经安全的保存在本地数据库中

Git 常用命令速查表

创建版本库

1
2
$ git clone <url>                  #克隆远程版本库
$ git init #初始化本地版本库

修改和提交

1
2
3
4
5
6
7
8
9
$ git status                       #查看状态
$ git diff #查看变更内容
$ git add . #跟踪所有改动过的文件
$ git add <file> #跟踪指定的文件
$ git mv <old><new> #文件改名
$ git rm<file> #删除文件
$ git rm --cached<file> #停止跟踪文件但不删除
$ git commit -m "commit messages" #提交所有更新过的文件
$ git commit --amend #修改最后一次改动

查看提交历史

1
2
3
$ git log                    #查看提交历史
$ git log -p <file> #查看指定文件的提交历史
$ git blame <file> #以列表方式查看指定文件的提交历史

撤销

1
2
3
4
$ git reset --hard HEAD      #撤销工作目录中所有未提交文件的修改内容
$ git checkout HEAD <file> #撤销指定的未提交文件的修改内容
$ git revert <commit> #撤销指定的提交
$ git log --before="1 days" #退回到之前1天的版本

分支与标签

1
2
3
4
5
6
7
$ git branch                   #显示所有本地分支
$ git checkout <branch/tag> #切换到指定分支和标签
$ git branch <new-branch> #创建新分支
$ git branch -d <branch> #删除本地分支
$ git tag #列出所有本地标签
$ git tag <tagname> #基于最新提交创建标签
$ git tag -d <tagname> #删除标签

合并与衍合

1
2
$ git merge <branch>        #合并指定分支到当前分支
$ git rebase <branch> #衍合指定分支到当前分支

远程操作

1
2
3
4
5
6
7
8
$ git remote -v                   #查看远程版本库信息
$ git remote show <remote> #查看指定远程版本库信息
$ git remote add <remote> <url> #添加远程版本库
$ git fetch <remote> #从远程库获取代码
$ git pull <remote> <branch> #下载代码及快速合并
$ git push <remote> <branch> #上传代码及快速合并
$ git push <remote> :<branch/tag-name> #删除远程分支或标签
$ git push --tags #上传所有标签

推送代码到远程仓库

在终端运行命令

1
git push

,将文件推送到远程仓库:

1
2
3
4
5
6
7
8
$ git push origin master
Counting objects: 8, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (8/8), 626 bytes | 626.00 KiB/s, done.
Total 8 (delta 0), reused 0 (delta 0)
To https://git.coding.net/Yangconghou/learn_git.git
* [new branch] master -> master

git push是推送命令,实际上是把本地的master分支推送到了远程仓库,相当于在远程有了一个代码仓库的备份。

使用 Git 管理文件时,每次结束工作前请依次执行git add、git commit和git push命令将文件推送到 远程仓库。

1、查看本地分支

1
$ git branch
1
2
3
4
结果:
➜ WarriorYu.github.io git:(code) ✗ git branch
* code
master

2、 list both remote-tracking and local branches (查看所有分支)

1
$ git branch -a
1
2
3
4
5
6
结果:
➜ WarriorYu.github.io git:(code) ✗ git branch -a
* code
master
remotes/origin/code
remotes/origin/master

3、我想看一下暂存区(staging area)里的内容

1
$ git ls-files

结果:

1
2
3
➜  _posts git:(code) ✗ git ls-files 
The Start.md
test.md
1
$ git ls-files --stage/-s   注:show staged contents' object name in the output

结果:

1
2
3
➜  _posts git:(code) ✗ git ls-files --stage
100644 a06e35bb0bbcc29593483ce13b37f9b4d28cd585 0 The Start.md
100644 a06e35bb0bbcc29593483ce13b37f9b4d28cd585 0 test.md

4、在我的post文件夹里新建一个test.md文件,并推到远程仓库

1
$ git add "test.md"

先拉取最新的代码

1
git pull origin code

结果;

1
2
3
4
➜  _posts git:(code) ✗ git pull origin code 
From https://github.com/WarriorYu/WarriorYu.github.io
* branch code -> FETCH_HEAD
Already up-to-date.

提交:-m后面添加提交的备注

1
git commit -m "add test.md"
1
2
3
4
➜  _posts git:(code) ✗ git commit -m "add test.md"
[code 9145a31] add test.md
1 file changed, 10 insertions(+)
create mode 100644 source/_posts/test.md

推送到远程仓库:

1
$ git push origin code

结果:

1
2
➜  _posts git:(code) ✗ git push origin code
Everything up-to-date

参考文章

Android ABI的浅析

layout中巧妙使用Space和weight

发表于 2018-09-13 | 分类于 Android

经常遇到这种布局,见下图,1、2、3、4、5、6的间隙都是等宽的,这种布局很简单,我们一般都使用LinearLayout,再利用weight属性,就可以实现。

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:padding="6dp">

<TextView
android:id="@+id/tv_detail_bottom_like"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_weight="1"
android:drawablePadding="3dp"
android:drawableTop="@mipmap/ic_daily_like"
android:text="@string/detail_bottom_like"
android:textColor="@color/bottom_text" />

<TextView
android:id="@+id/tv_detail_bottom_comment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:drawablePadding="3dp"
android:drawableTop="@mipmap/ic_daily_comment"
android:text="@string/detail_bottom_commit"
android:textColor="@color/bottom_text" />

<TextView
android:id="@+id/tv_detail_bottom_share"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_weight="1"
android:drawablePadding="3dp"
android:drawableTop="@mipmap/ic_daily_share"
android:text="@string/detail_bottom_share"
android:textColor="@color/bottom_text" />
</LinearLayout>

如果想按下面图示的比例呢,先看图

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal">

<android.support.v4.widget.Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="2" />

<TextView
android:id="@+id/tv_detail_bottom_like"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="3dp"
android:drawableTop="@mipmap/ic_daily_like"
android:text="@string/detail_bottom_like"
android:textColor="@color/bottom_text" />

<android.support.v4.widget.Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="3" />

<TextView
android:id="@+id/tv_detail_bottom_comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="3dp"
android:drawableTop="@mipmap/ic_daily_comment"
android:text="@string/detail_bottom_commit"
android:textColor="@color/bottom_text" />

<android.support.v4.widget.Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="3" />

<TextView
android:id="@+id/tv_detail_bottom_share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="3dp"
android:drawableTop="@mipmap/ic_daily_share"
android:text="@string/detail_bottom_share"
android:textColor="@color/bottom_text" />

<android.support.v4.widget.Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="2" />
</LinearLayout>

在日常开发中,可以使用weight属性来实现等比例布局,也可以通过Space实现非等比例布局。当然,这两种方式也有交叉点,也就是有些情况下,可以互相取代。

Space官方解释:

Space is a lightweight View subclass that may be used to create gaps between components in general purpose layouts.

翻译过来:

Space是一个轻量级的View子类,可用于在通用布局中创建组件之间的间隙。

The Start

发表于 2018-07-30 | 分类于 杂谈

The Start

今天刚刚搭建了自己的博客,在过去的几天里,我极度焦虑,更多的是对自己的不满意,这体现在没有一个途径来驱动自己做工作上的深入研究,自我驱动的效果不佳,没人管控我的技术发展方向,也没人督促我作为「个体」的进步。之所以说「个体」,我的理解是个人体系,这个体系包括文化、艺术、修养、技能、情操、素质等各方面,而我焦虑的正是我明白我要去发展某些方面,却总是有障碍,但归根结底是自我执行力不足。

所以今天搭建起了博客,把这里当做一个输出的空间,任何生活、工作,或者感悟、杂谈,都会输出在这里,希望在这里能见证自己的成长,也希望在没人引导我的同时,自己去探索更多的可能性,让自己主宰自己的人生!不妥协!就是干!

123
WarriorYu

WarriorYu

Stay Hungry. Stay Foolish.

30 日志
13 分类
19 标签
GitHub E-Mail
© 2020 WarriorYu
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4