android-TextureView

为什么要有TextureView

它是在4.0之后出现的用于替代surfaceView。

surfaceView的工作方式是创建一个自己的窗口,跟应用窗口是平级的,也就是说它不像Button之类的可以放在Relativelayout里面,这样surfaceView就有了一个自己的优势,它自己刷新的时候不需要重回应用窗口。

一次技术选型(webView加载h5)

背景

公司有写好的前端,其中包括

  1. 原生不太好实现的动画
  2. 动作跟情绪识别的model(利用的是tensorflow.js)
  3. 音视频

android端也希望可以实现相同的功能,此时想到的可能就是通过webview直接加载一个url就可以了,简单快捷。但是产品希望用户在没有网络的情况下也可以使用,直接加载url的方案肯定是行不通的。此时想到的方案是把前端的代码build到apk中,也就是把前端代码放在assets目录中。

方案确定之后开始爬坑~

一:如何将vue前端代码添加到assets中

将前端build之后生成的dist文件夹copy到assets中,之后使用webview加载assets中的入口html。

加载之后webview一片空白

问题出在了前端build之后的引用资源(css,js等)路径上。前端配置文件中assetsPublic的属性为“/”,对应的是根目录,而引用的资源文件static/css/xx.css目录和index.html是同级的,去根目录下找自然就找不到资源。修改配置文件中的assetsPublic属性为“./”,再重新build解决问题。

二:如何使用chrome调试webview显示的h5页面

加载webview空白的问题,我们是如何定位到的呢。其实chrome提供了调试andorid WebView的功能,inspect之后可以在console中看到报错信息。至于如何调试,过程非常简单,两三步就可以,想自己体验的可以百度下,个人感觉还是非常实用的~(注意在webView中设置debug模式为true)

三:webView如何加载具有音视频功能的h5页面

有两种方案可以选择:

  1. 通过原生跟前端交互实现(需要修改前端代码,android端也有一定的工作量)
  2. webView在5.0之后默认支持WebRtc(支持网页实时语音跟视频对话),需要新建一个WebChromeClient设置给webView,其中WebChromeClient需要重写onPermissionRequest()方法赋予网页音视频权限。另外需要在manifest中声明使用的音视频权限。
四:跨域问题

webView加载assets中的静态html使用的是file协议,如果在index.html中再去加载http协议的页面或者发送ajax请求都会有跨域的问题。

实际项目中加载model使用到了http协议。

方案:可以使用NanoHttpd或者AndroidAsync在app中搭建本地服务器。

webView使用loadUtl(http)的形式加载index.html,这样统一使用http协议。

NanoHttpd不支持ttf以及woff等格式,最终使用了AndroidAsync跑通了demo。

具体可以参考demo中的代码或者google 百度。

五:跨域问题备选方案

要求提供pb格式的model,之后使用tensorflowLite(tensorFlow对android的解决方案)去跟model交互,交互完成之后再去跟前端h5交互。

六:webView跟js交互

dist放在本地之后webview直接load一直担心vue build之后生成的dist中的js文件加了类似android的混淆,可能h5跟android交互会有问题,测试证明我的担心是多余的😓。交互跟直接load服务器中的h5是一样的。可以参考下:

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

七:demo地址

https://github.com/kbjay/AndroidAsyncDemo

okhttp拦截器

​ RetryAndFollowUpInterceptor

  1. 创建streamAllocation
2. 调用proceed 进行网络请求
3. 根据响应结果判断是否重新请求
4. 调用下一个拦截器,对response处理,返回给上一个拦截器

​ BridgeInterceptor

  1. 修饰request,添加请求头

  2. 进行网络请求

  3. 修饰response

​ CacheInterceptor

  1. 目的:更快响应
  2. 使用:.cache(new Cache(new File(“xxx”)))
  3. DiskLruCache
  4. 只会缓存get方法

​ ConnectInterceptor

  1. 连接池
  2. 正式开启网络请求
  3. RealConnection

​ CallServerInterceptor

1.创建拦截器,放入list

2.创建拦截器链RealInterceptorChain,执行proceed

getResponseWithInterceptorChain

proceed

分发器:

leetcode-85. 最大矩形

给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例:

1
2
3
4
5
6
7
8
输入:
[
["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"]
]
输出: 6

解法:

参考84题

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
class Solution {
public int maximalRectangle(char[][] matrix) {
//初始化int数组
int height = matrix.length;
if(height==0){
return 0;
}
int width = matrix[0].length;

int[][] temp = new int[height][width];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
char ch = matrix[i][j];
int t = ch == '0' ? 0 : 1;
if (i == 0) {
temp[i][j] = t;
} else {
if (matrix[i - 1][j] == '0') {
temp[i][j] = t;
} else {
if (t == 0) {
temp[i][j] = 0;
} else {
temp[i][j] = t + temp[i - 1][j];
}
}
}
}
}
//遍历每个数组求最大值
int max = 0;
for (int i = 0; i < height; i++) {
int t = getMaxArea(temp[i]);
max = max > t ? max : t;
}
return max;
}

public int getMaxArea(int[] array) {
int len = array.length;
if (len == 0) {
return 0;
}
int max = 0;
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < len; i++) {
if (stack.isEmpty()) {
stack.push(i);
} else {
int current = array[i];
if (current > array[stack.peek()]) {
stack.push(i);
} else {
while (!stack.isEmpty() && current <= array[stack.peek()]) {
int height = array[stack.pop()];
int start = -1;
if (!stack.isEmpty()) {
start = stack.peek();
}
int area = height * (i - start - 1);
max = max > area ? max : area;
}
stack.push(i);
}
}
}
if (!stack.isEmpty()) {
int end = stack.peek();
while (!stack.isEmpty()) {
int height = array[stack.pop()];
int start = -1;
if (!stack.isEmpty()) {
start = stack.peek();
}
int area = height * (end - start);
max = max > area ? max : area;
}
}
return max;
}
}

leetcode-84. 柱状图中最大的矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

img

以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]

img

图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。

示例:

1
2
输入: [2,1,5,6,2,3]
输出: 10

解法一:遍历每一种可能的高度(n*n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int largestRectangleArea(int[] heights) {
int max=0;
int len=heights.length;
for(int i =0;i<len;i++){
int num=heights[i];
int start = i;
int end = i;
while(start>=0 && heights[start]>=num){
start--;
}
while(end<len && heights[end]>=num){
end++;
}
int total=num*(end-start-1);
max=max>total?max:total;
}
return max;
}
}

解法二:使用递增栈(2*n)

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
class Solution {
public static int largestRectangleArea(int[] heights) {
if(heights.length==0){
return 0;
}
int max = 0;
Stack<Integer> stack = new Stack();
stack.push(0);
int len = heights.length;
for (int i = 1; i < len; i++) {
int current = heights[i];
int stackTop = heights[stack.peek()];
if (current > stackTop) {
stack.push(i);
} else {
while (!stack.isEmpty() && heights[stack.peek()] >= current) {
Integer topIndex = stack.pop();
int start = -1;
if (!stack.isEmpty()) {
start = stack.peek();
}
int area = heights[topIndex] * (i - start - 1);
max = max > area ? max : area;
}
stack.push(i);
}
}
if (!stack.isEmpty()) {
Integer top = stack.peek();
while (!stack.isEmpty()) {
int height = heights[stack.pop()];
int start = -1;
if (!stack.isEmpty()) {
start = stack.peek();
}
int area = height * (top - start);
max = max > area ? max : area;
}
}
return max;
}
}

SVG图像处理

语法

https://developer.mozilla.org/zh-CN/docs/Web/SVG/Tutorial

https://brucewar.gitbooks.io/svg-tutorial/33.SVG%E5%8F%98%E6%8D%A2.html

解析xml(dom4j)

https://dom4j.github.io/

接口:

单一元素:

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
/**
* 单一元素双行旋转平铺(元素随机)
*
* @param source
* @param itemWidth
* @param width
* @param height
* @param fromColor
* @param toColor
* @return
*/
public static Document getDestDocumentSingle(Document[] source, int itemWidth, int width, int height, String[] fromColor, String[] toColor) {
}

/**
* 单一元素双行旋转平铺(元素不随机)
*
* @param source
* @param itemWidth
* @param fromColor
* @param toColor
* @return
*/
public static Document getDestDocumentSingle(Document source, int itemWidth, int width, int height, String[] fromColor, String[] toColor) {
}

组合元素:

1
2
3
4
5
6
7
8
9
10
/**
* 多种元素平铺(每一个单元的元素都由用户指定(指定图像源及旋转角度即可),用户可以随意客制化)
*
* @param sources 按顺序排列的原图数组(从左到右,从上到下)
* @param itemWidth 原图集合宽度
* @param fromColor 图片原始色值
* @param toColor 图片最终色值
*/
public static Document getDestDocument(FloorSvg[] sources, int itemWidth, int width, int height, String[] fromColor, String[] toColor) {
}

Android-图片实现类似素描的效果

核心思路
  1. 使用openCV获取灰度图像:0(黑)~255(白)

  2. 将该图像按照灰度值分层,每一层是一个0,1二维数组:1表示绘制,0表示不绘制,之后绘制每一层

  3. 先绘制颜色最深的那一层。

    在二维数组中找一个随机点,从该点出发依次向上下左右四个方向探测,如果找到需要绘制的点,那么再去找该点所在的区域中所有的跟该点绘制次数相同的点,之后按照随机的mask去绘制找到的点(注意点的颜色由该位置的已经绘制的次数决定)。遍历完该点的4个方向之后,再遍历该点左下位置的点,直到超出边界,之后再去遍历该点右上位置的点,直到超出边界,这样就遍历完了一层。

    之后再遍历下一层,直到遍历完所有层。

  4. 上面提到的mask是预先生成的蒙板。具体可以去看源代码,非常简单。

效果gif:

sumiao

最终效果图

sketch

代码地址

https://github.com/kbjay/KJ_Sketch