implementation和api

implementation 可以在编译时隐藏自己的依赖,但运行时这个依赖对所有模块可见。目的是为了减少编译时间,依赖自己的模块不会因为这个依赖变更重新编译

A- – ->B- – ->C 当C修改后,只要重新编译B和C就好了,A不需要编译

Test

我的车 tradexportprerelease://ads/9d79cd6e-83be-4efa-b8b8-00b63ada76ea/
二手车 tradexportprerelease://ads/ce8976e5-7820-488d-891f-5782caf0bb66/
新车 tradexportprerelease://ads/05bbad0e-e593-4f16-961a-07a5cce39b38/
购买 tradexportprerelease://ads
订单 tradexportprerelease://orders
聊天 tradexportprerelease://messages/83e34e5b-7772-4b6a-ba96-8556486d9eb4/


tradex://tradexport.com/ads
https://tradexport.com/ads

https://u9814280.ct.sendgrid.net/wf/click?upn=yHLBMl79VtwLkvdSNg6rt1wuIcEZxidcfSDB5uJIFdnuOOYw-2FvfTFi6o8R3CtFED-2FxTTphkzGF9-2Fvf0JlSiLvXwr6hz3sDPzwX7A8g8lFA8-3D_rSqg-2FyCuwG-2Bl-2BFw25GPTXspAEz4yJrsYIBrJX-2F9K-2FDLQQScoyA7rjiufIwG3Yz2Xua3FVhCn-2FioTzyqwVi8syHoeJEsK-2BR8RyCPF8bgNYXPFOHaes5aYB7mHrhbBFPGRjYlTIL596PDvYTw6RKiwrrJlMwai76XdmyE4G8qcxS3R4RcypwZSuQUwiu88k-2B413f1FcYGIXejhMLTCu1AMU71a-2FYS736tQcU4YWrP9kZ3iZe0p3Pbr0Fe6U0t9-2F5bELHplcrpFrOjB16kMEgRPMIAEPlma8vN0Yrp8CnoThKc-3D

JVM内存区域的划分,哪些区域会发生 OOM

JVM 的内存区域可以分为两类:线程私有的区域和线程共有的区域。 线程私有的区域:程序计数器、JVM 虚拟机栈、本地方法栈 线程共有的区域:堆、方法区、运行时常量池

  • 程序计数器。 每个线程有有一个私有的程序计数器,任何时间一个线程都只会有一个方法正在执行,也就是所谓的当前方法。程序计数器存放的就是这个当前方法的JVM指令地址
  • JVM虚拟机栈。 创建线程的时候会创建线程内的虚拟机栈,栈中存放着一个个的栈帧,对应着一个个方法的调用。JVM 虚拟机栈有两种操作,分别是压栈和出站。栈帧中存放着局部变量表、方法返回值和方法的正常或异常退出的定义等等。
  • 本地方法栈。 跟 JVM 虚拟机栈比较类似,只不过它支持的是 Native 方法。
  • 堆。 堆是内存管理的核心区域,用来存放对象实例。几乎所有创建的对象实例都会直接分配到堆上。所以堆也是垃圾回收的主要区域,垃圾收集器会对堆有着更细的划分,最常见的就是把堆划分为新生代和老年代
  • 方法区。方法区主要存放类的结构信息,比如静态属性和方法等等
  • 运行时常量池。运行时常量池位于方法区中,主要存放各种常量信息

其实除了程序计数器,其他的部分都会发生 OOM

  • 堆。 通常发生的 OOM 都会发生在堆中,最常见的可能导致 OOM 的原因就是内存泄漏
  • JVM虚拟机栈和本地方法栈。 当我们写一个递归方法,这个递归方法没有循环终止条件,最终会导致 StackOverflow 的错误。当然,如果栈空间扩展失败,也是会发生 OOM 的
  • 方法区。方法区现在基本上不太会发生 OOM,但在早期内存中加载的类信息过多的情况下也是会发生 OOM 的

GC机制

垃圾回收需要完成两件事:找到垃圾,回收垃圾。 找到垃圾一般的话有两种方法:

  • 引用计数法:当一个对象被引用时,它的引用计数器会加一,垃圾回收时会清理掉引用计数为0的对象。但这种方法有一个问题,比方说有两个对象 A 和 B,A 引用了 B,B 又引用了 A,除此之外没有别的对象引用 A 和 B,那么 A 和 B 在我们看来已经是垃圾对象,需要被回收,但它们的引用计数不为 0,没有达到回收的条件。正因为这个循环引用的问题,Java 并没有采用引用计数法。
  • 可达性分析法: 我们把 Java 中对象引用的关系看做一张图,从根级对象不可达的对象会被垃圾收集器清除。根级对象一般包括 Java 虚拟机栈中的对象、本地方法栈中的对象、方法区中的静态对象和常量池中的常量。

回收垃圾的话有这么四种方法:

  • 标记清除算法: 顾名思义分为两步,标记和清除。首先标记到需要回收的垃圾对象,然后回收掉这些垃圾对象。标记清除算法的缺点是清除垃圾对象后会造成内存的[碎片化]。
  • 复制算法: 复制算法是将存活的对象复制到另一块内存区域中,并做相应的内存整理工作。复制算法的优点是可以避免内存碎片化,缺点也显而易见,它需要[两倍的内存]。
  • 标记整理算法: 标记整理算法也是分两步,先标记后整理。它会标记需要回收的垃圾对象,清除掉垃圾对象后会[将存活的对象压缩],避免了内存的碎片化。
  • 分代算法:将对象分为新生代和老年代对象。那么为什么做这样的区分呢?主要是在Java运行中会产生大量对象,这些对象的生命周期会有很大的不同,有的生命周期很长,有的甚至使用一次之后就不再使用。所以针对不同生命周期的对象采用不同的回收策略,这样可以提高GC的效率

新生代对象分为三个区域:Eden 区和两个 Survivor 区。

新创建的对象都放在 Eden区,当 Eden 区的内存达到阈值之后会触发 Minor GC,这时会将存活的对象复制到一个 Survivor 区中,这些存活对象的生命存活计数会加一。这时 Eden 区会闲置,当再一次达到阈值触发 Minor GC 时,会将Eden区和之前一个 Survivor 区中存活的对象复制到另一个 Survivor 区中,采用的是我之前提到的复制算法,同时它们的生命存活计数也会加一。

这个过程会持续很多遍,直到对象的存活计数达到一定的阈值后会触发一个叫做晋升的现象:新生代的这个对象会被放置到老年代中。 老年代中的对象都是经过多次 GC 依然存活的生命周期很长的 Java 对象。当老年代的内存达到阈值后会触发 Major GC,采用的是标记整理算法

介绍下Android应用程序启动过程

一. :Launcher通过Binder进程间通信机制通知ActivityManagerService,它要启动一个Activity;

二.:ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态;

三.:Launcher通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态,于是ActivityManagerService就创建一个新的进程,用来启动一个ActivityThread实例,即将要启动的Activity就是在这个ActivityThread实例中运行;

四. :ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService,以便以后ActivityManagerService能够通过这个Binder对象和它进行通信;

五 :ActivityManagerService通过Binder进程间通信机制通知ActivityThread,现在一切准备就绪,它可以真正执行Activity的启动操作了。

Android PorterDuffXfermode

PorterDuffXfermod模式的混合效果如图:

@Override
protected void onDraw(Canvas canvas) {
    //背景色
    canvas.drawARGB(255, 255, 156, 161);
    //先绘制的是dst,后绘制的是src
    drawDst(canvas, mPaint);
    drawSrc(canvas, mPaint);
}

private void drawDst(Canvas canvas, Paint p) {
    //画黄色圆形
    p.setColor(0xFFFFCC44);
    canvas.drawOval(new RectF(0, 0, W * 3 / 4, H * 3 / 4), p);
}

private void drawSrc(Canvas canvas, Paint p) {
    //画蓝色矩形
    p.setColor(0xFF66AAFF);
    canvas.drawRect(W / 3, H / 3, W * 19 / 20, H * 19 / 20, p);
}

代码很简单,就是在onDraw()中先画了一个黄色圆形dst,然后再画了一个蓝色矩形src。

结果显示后绘制的蓝色矩形叠加在黄色圆形上了,这是正常的混合模式。

使用PorterDuffXfermod的CLEAR模式。修改onDraw方法如下:

@Override
protected void onDraw(Canvas canvas) {
    //背景色
    canvas.drawARGB(255, 255, 156, 161);
    //先绘制的是dst,后绘制的是src
    drawDst(canvas, mPaint);
    //设置xfermode
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    drawSrc(canvas, mPaint);
    //还原
    mPaint.setXfermode(null);
}

在绘制蓝色矩形src时给paint设置了Xfermode为CLEAR模式;

发现绘制的蓝色矩形src结果成了白色矩形

这是为什么呢?

先看如下代码:

@Override
protected void onDraw(Canvas canvas) {
    //背景色
    canvas.drawARGB(255, 255, 156, 161);
    int sc = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);
    //先绘制的是dst,后绘制的是src
    drawDst(canvas, mPaint);
    //设置xfermode
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    drawSrc(canvas, mPaint);
    //还原
    mPaint.setXfermode(null);
    canvas.restoreToCount(sc);
}

这次我们发现绘制的这个蓝色矩形src好像“消失了”。并且发现dst与src两个图形的相交处显示的是背景色。

比较这两处代码不同之处在于这次绘制图形是放在canvas.saveLayer与canvas.restoreToCount中实现的。

关于savelayout的作用查一下可知:

  1. canvas是支持图层layer渲染这种技术的,canvas默认就有一个layer,当我们平时调用canvas的各种drawXXX()方法时,其实是把所有的东西都绘制到canvas这个默认的layer上面。
  2. 我们还可以通过canvas.saveLayer()新建一个layer,新建的layer放置在canvas默认layer的上部,当我们执行了canvas.saveLayer()之后,我们所有的绘制操作都绘制到了我们新建的layer上,而不是canvas默认的layer。
  3. 用canvas.saveLayer()方法产生的layer所有像素的ARGB值都是(0,0,0,0),即canvas.saveLayer()方法产生的layer初始时时完全透明的。
  4. canvas.saveLayer()方法会返回一个int值,用于表示layer的ID,在我们对这个新layer绘制完成后可以通过调用canvas.restoreToCount(layer)或者canvas.restore()把这个layer绘制到canvas默认的layer上去,这样就完成了一个layer的绘制工作。

分析一下原因:

  1. 我们先将画布设置背景色,此时画布的ARGB值就是背景色值。
  2. 我们通过saveLayer新建一个layer,接下来的绘制都是在这个新建的layer上进行绘制的了。
  3. 我们先绘制了一个黄色的圆形dst。
  4. 设置画笔Xfermode为CLEAR模式画蓝色矩形src,由于CLEAR模式蓝色矩形区域(包括与圆形相交部分)的ARGB值被设置为(0,0,0,0)也就是透明。
  5. 这样新建的layer上就只有一个3/4黄色圆形dst是不透明的(1/4与src叠加部分被置为透明),最后通过restoreToCount将layer绘制到canvas上,绘制的结果就是:透明部分显示canvas背景颜色,不透明部分显示黄色圆形dst部分。也就是上图这种现象。

为什么第一次会出现白色矩形呢?”这个问题也就很好回答了,因为我们一开始是在canvas的默认layer上绘制的,当我们的矩形区域src被CLEAR模式绘制后,该区域变为透明。canvas该部分就成为透明了,但由于Activity背景本身是白色的所以最终显示就为白色了。

通过上面的实验和分析可以得出,CLEAR模式可以使两种图片相交处设置为透明。但我们把API Demo那张图拿出来后一看:“不对啊,不应该是全部透明么?”这里就是那张图给我们挖的坑了。我们仔细看下官方示例的代码是如何实现的:

// create a bitmap with a circle, used for the "dst" image
static Bitmap makeDst(int w, int h) {
    Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(bm);
    Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

    p.setColor(0xFFFFCC44);
    c.drawOval(new RectF(0, 0, w*3/4, h*3/4), p);
    return bm;
}

// create a bitmap with a rect, used for the "src" image
static Bitmap makeSrc(int w, int h) {
    Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(bm);
    Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

    p.setColor(0xFF66AAFF);
    c.drawRect(w/3, h/3, w*19/20, h*19/20, p);
    return bm;
}

这是其实现代码,我们可以看出这两个方法的目的就是创建两个图形用来演示PorterDuffXfermod的不同效果。但我们仔细一看就能发现有点问题了:

  1. 创建的bitmap的宽高是w,h;但绘制的图形大小只是其中一部分(注意drawXX中的代码),这样bitmap其余部分是透明的。
  2. 这就可以知道最终得到的dst与src是两张大小一致(都为w,h)的bitmap,只是可见区域(绘制区域)导致我们误解是两个不同尺寸的bitmap。
  3. 这样的dst与src叠加就不再是一个黄色圆形与蓝色矩形叠加了,而是两张宽高一致的bitmap叠加了。
  4. 这样的src(蓝色矩形)设置为CLEAR后,其实是绘制的整个bitmap设置为CLEAR。dst与src相交区域也就是整个bitmap区域都会被设置为透明,这也就是为什么我们的实验与官方demo不一致的地方了。

原来如此!所以在开发中万不可看着API Demo图无脑来选择一种自己想要的模式,这样会使你得不到自己想要的结果时摸不着头脑。接下来我们按照图形实际大小修改后可以得出另一张图:

最后

1、关于硬件加速 

在sdkversion>=11时,需要关闭硬件加速,否则 Mode.CLEAR 、 Mode.DARKEN 、 Mode.LIGHTEN 三种模式下绘制效果不正常。

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
//View从API Level 11才加入setLayerType方法
//关闭硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

2、关于saveLayer 

savelayer不是必须的,我们已经知道使用他的目的了;其实我们可以新建一个bitmap并在这上面进行绘制,最后将bitmap绘制到canvas上,其实原理是一致的。

Https相关-HostnameVerifier

[强制]在实现的HostnameVerifier子类中,需要使用verify函数效验服务器主机名的合法性,否则会导致恶意程序利用中间人攻击绕过主机名效验。

说明:
在握手期间,如果URL的主机名和服务器的标识主机名不匹配,则验证机制可以回调此接口实现程序来确定是否应该允许此连接,如果回调内实现不恰当,默认接受所有域名,则有安全风险。

反例:
HostnameVerifier hnv=new HosernameVerifier(){
  @Override
  public boolean verify(String hostname,SSLSession session){
      return true;
  }
}
正例:
HostnameVerifier hnv=new HosernameVerifier(){
@Override
public boolean verify(String hostname,SSLSession session){
    if("youhostname".equals(hostname)){
        return true;
    }else{
          HostnameVerifier        hv=HttpsURLConnection.getDefaultHostnameVerifier();
         return hv.verify(hostname,session);
          }
  }
}

经验

./gradlew clean -i assembleDebug

发现 连接dl.google.com超时

则使用http://ping.chinaz.com/网站ping下,找到dl.google.com对应的ip

在本地hosts中添加一条记录

203.208.43.66 dl.google.com

再编译就好了

bei

recyclerView.setNestedScrollingEnabled(true);

https://www.jianshu.com/p/635970ac603a

https://hfutfei.iteye.com/blog/988374

https://www.cnblogs.com/frankliiu-java/articles/1759460.html

https://www.cnblogs.com/zhaoyan001/p/6365064.html

https://blog.csdn.net/android_gogogo/article/details/53376178

https://my.oschina.net/djone/blog/145057

https://blog.csdn.net/lv_fq/article/details/77836700

https://github.com/lvfaqiang/AndroidTestCode

https://github.com/osmandapp/Osmand

http://www.justlive.vip/blog/article/details/4199

https://github.com/jiaowenzheng/CustomTextView

http://www.cnblogs.com/TerryBlog/archive/2013/04/02/2994815.html

https://www.cnblogs.com/zhujiabin/p/5808232.html

https://github.com/Luction/AndroidRichText

https://blog.csdn.net/u014620028/article/details/54092723

https://www.cnblogs.com/zhujiabin/p/5808232.html

http://www.cnblogs.com/luction/p/3645210.html

https://www.jianshu.com/p/b87dddf02e04

https://juejin.im/post/5b13a5b8f265da6e3128d501

https://tonnyl.io/Spantastic-Text-Styling-With-Spans/

http://melonteam.com/posts/gei_ni_de_spannablestring_she_zhi_dian_ji_tai/

https://blog.51cto.com/kinbos/1348407

https://blog.csdn.net/c16882599/article/details/52913799

https://segmentfault.com/a/1190000006163046

https://blog.csdn.net/natloc/article/details/50849700

http://blog.hacktons.cn/2015/02/03/porterduff/

https://www.jianshu.com/p/d54e24efbd7b

https://github.com/MrAllRight/BezierView/

https://www.jianshu.com/p/a5c2f0359d31

https://www.jianshu.com/p/016534448bfe

https://www.jianshu.com/p/92627f72c707

https://blog.csdn.net/ccpat/article/details/46717573

Androidbug-java.util.zip.ZipException: duplicate entry(重复依赖多版本的类库)

以上大概意思:重复依赖,重复了条目。

1、清除一下缓存:File->Invalidate Caches/Restart..

注意:
除开Gradle依赖类库之外,还可以在项目中的libs下静态方式添加类库。
若是使用静态方式添加jar类库的,请删除重复的jar

2、编译前clean下

https://blog.csdn.net/hexingen/article/details/74065796

Androidbug-android-sdk-macosx/cmake/3.6.4111459/bin/cmake

Build command failed.
Error while executing process /Users/xx/Documents/software/android-sdk-macosx/cmake/3.6.4111459/bin/cmake with arguments {-H/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app -B/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app/.externalNativeBuild/cmake/debug/armeabi -GAndroid Gradle - Ninja -DANDROID_ABI=armeabi -DANDROID_NDK=/Users/xx/Documents/software/android-sdk-macosx/ndk-bundle -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app/build/intermediates/cmake/debug/obj/armeabi -DCMAKE_BUILD_TYPE=Debug -DCMAKE_MAKE_PROGRAM=/Users/xx/Documents/software/android-sdk-macosx/cmake/3.6.4111459/bin/ninja -DCMAKE_TOOLCHAIN_FILE=/Users/xx/Documents/software/android-sdk-macosx/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=android-16 -DCMAKE_CXX_FLAGS=}
(include) CMakeLists.txt
Open File
CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage
CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage
-- Configuring incomplete, errors occurred!
Build command failed.
Error while executing process /Users/xx/Documents/software/android-sdk-macosx/cmake/3.6.4111459/bin/cmake with arguments {-H/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app -B/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app/.externalNativeBuild/cmake/release/armeabi -GAndroid Gradle - Ninja -DANDROID_ABI=armeabi -DANDROID_NDK=/Users/xx/Documents/software/android-sdk-macosx/ndk-bundle -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app/build/intermediates/cmake/release/obj/armeabi -DCMAKE_BUILD_TYPE=Release -DCMAKE_MAKE_PROGRAM=/Users/xx/Documents/software/android-sdk-macosx/cmake/3.6.4111459/bin/ninja -DCMAKE_TOOLCHAIN_FILE=/Users/xx/Documents/software/android-sdk-macosx/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=android-16 -DCMAKE_CXX_FLAGS=}
(include) CMakeLists.txt
Open File
CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage
CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage
-- Configuring incomplete, errors occurred!

却换NDK版本,我用了android-ndk-r15b

Glide

Transformation<Bitmap> circleCrop = new CircleCrop();
if (data.imgUrl.endsWith("gif")) {
Glide.with(context)
.asGif()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.load(data.imgUrl)
.into(imageView);
} else if (data.imgUrl.endsWith("webp")) {
Glide.with(context)
.load(data.imgUrl)
.optionalTransform(circleCrop)
.optionalTransform(WebpDrawable.class, new WebpDrawableTransformation(circleCrop))
.into(imageView);
} else {
Glide.with(context)
.load(data.imgUrl)
.into(imageView);
}

Android播放器-顶点坐标和材质坐标

顶点坐标信息

                     ^
|
4 | 3
(1, -1, 0) <----------------- (1, 1, 0)
| | |
| | |
---------------------|------------------------->
| | |
| | |
V | V
(-1, -1, 0) <-----——----------- (1, -1, 0)
2 | 1
|

上三角形:3->4->2->3
下三角形:1->2->3->1
共向量:2->3

float *vertexData = new float[12]{
1f, -1f, 0f,
-1f, -1f, 0f,
1f, 1f, 0f,
-1f, 1f, 0f
}

材质坐标信息

         ^
|
4 | 3
(0, 1) ----------- (1, 1)
| |
| |
| |
-------(0, 0) ----------- (1, 0)------>
2 | 1
|

glTexParameteri

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

GL_TEXTURE_2D: 操作2D纹理
GL_TEXTURE_MIN_FILTER: 缩小过滤
GL_TEXTURE_MAG_FILTER: 放大过滤

GL_LINEAR: 线性过滤,使用距离当前渲染像素中心最近的4个纹理加权平均值

Android播放器-着色器语言GLSL

着色器语言又叫Shader,有两个模块一个是顶点着色器,一个是片元着色器

  • 顶点着色器是针对每个顶点执行一次,用于确定顶点的位置;
  • 片元着色器是针对每个片元(可以理解为每个像素)执行一次,用于确定每个片元(像素)的颜色;
  • GLSL的基本语法与C基本相同
  • 它完美的支持向量和矩阵操作
  • GLSL提供了大量的内置函数来提供丰富的扩展功能
  • 它是通过限定符操作来管理输入输出类型的

ffmpeg -i 720.mp4 -pix_fmt yuv420p -s 424×240 out.yuv