从Android源代码来看WiFi直连

什么是WiFi直连通俗点说,它可以不通过网络,也不通过蓝牙,只要两台设备都支持WiFi直连,打开WiFi,不用连接任何WiFi,就可以进行信息的传输(请忽略下面两张图中的WiFi连接标志,因为其与WiFi的连接与否无关,打开就可以)。在Android的设置->网络与互联网->WLAN-&
阅读更多

Android8.1原生系统网络感叹号消除

原生系统Android8.1上,WiFi上出现感叹号,此时WiFi可正常访问。原因这是Android 5.0引入的网络评估机制:就是当你连上网络后,会给目标产生204响应的服务器发送给一个请求,如果服务器返回的是状态码为204的响应,那么就被认为网络可以访问;否则,如返回的是其他状态码,那么将被视为
阅读更多

libstreaming局域网构建Android相机实时流媒体流程分析

这是一个涉及东西比较多的第三方库,里面的一些代码细节有点让人云里雾里。如果真的是这样,那说明了解的东西还太少,真正的去了解这些详细的东西,起码得知道相应的概念,如RTSP、RTP、H264、H264打包。我觉得首要的事是将大体的逻辑打通,然后再慢慢深入代码的细节,了解相关的技术与知识。奈何,这部分在
阅读更多

OpenCV使用遇到的问题

图像翻转

The example scenarios of using the function are the following:

  • Vertical flipping of the image (flipCode == 0) to switch between top-left and bottom-left image origin. This is a typical operation in video processing on Microsoft Windows* OS.
  • Horizontal flipping of the image with the subsequent horizontal shift and absolute difference calculation to check for a vertical-axis symmetry (flipCode > 0).
  • Simultaneous horizontal and vertical flipping of the image with the subsequent shift and absolute difference calculation to check for a central symmetry (flipCode < 0).
  • Reversing the order of point arrays (flipCode > 0 or flipCode == 0).

简而言之,就是:
flipCode >0: 沿y轴翻转;==0: 沿x轴翻转; <0: x、y轴同时翻转 。图片中的坐标系,原点在左上角,向右为x轴,向下为y轴。
效果图如下:

  • x轴翻转

这里写图片描述

  • y轴翻转

这里写图片描述

  • x,y都翻转

这里写图片描述

图片旋转

transpose():相当于逆时针旋转90°,然后取镜像。效果如下:
这里写图片描述

顺时针旋转90°

1
2
3
transpose(img2, img2);
flip(img2, img3, 1);
imshow("先转置,再y轴翻转", img3);

这里写图片描述

顺时针旋转270°(逆时针旋转90°)

1
2
3
transpose(img2, img2);
flip(img2, img3, 0);
imshow("先转置,再x轴翻转", img3);

这里写图片描述

旋转180°

  • 方法1:

    1
    2
    3
    flip(img2, img3, 0);
    flip(img3, img3, 1);
    imshow("先x轴翻转,再y轴翻转", img3);

    这里写图片描述

  • 方法2:
    先顺时针旋转90°,再顺时针旋转90°。

遇到的错误

描述:
OpenCV Error: Assertion failed (0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows) in cv::Mat::Mat

在使用OpenCV的裁剪功能时,传入了cv::Rect,遇到如上错误。后来经过检查,错误来源自传给cv::Rect的四个参数。在JNI中获得的来自Java层的floag[]在转化过程中出现了错误,让原本的参数,变成了其它的莫名其妙的负数,致使如上错误出现。后面经过对参数的修正,错误就未出现了,裁剪图片成功。

大致理解,此错误来源自所需要裁剪的区域,应该是超过了原本图片大小。

Microsoft Visual Studio 2017配置OpenCV开发环境简要操作

参考博客:https://blog.csdn.net/sinat_36264666/article/details/73135823

有机会接触这个是因为公司的某个项目,有时候需要使用到OpenCV处理图片,在JNI层操作有诸多不便,还不如使用Visual Studio来进行相应操作的验证,得到的结果会更快更有说服力。Visual Studio是传说中的神器,说真的菜单还真看着有点不知道说什么,虽然都是汉字。配置这种东西,博客太多了,但是有一些是2015的或者更低,当然也有2017的。这里就做个简要的记录,多余的都删除。

下载并安装Visual Studio 2017

下载安装器后,选择安装。如果是第一次安装,可以先勾上这两个选项(从网上看到的操作,并且我之前没勾全了,配置好了也跑不起来);
这里写图片描述
如果是已经安装了,并且上图中的两个勾没选上的话,那么可以按照下述步骤去勾上并进行组件的安装。
控制面板->程序与应用->Visual Studio 2017->右键->修改

下载并安装OpenCV3.4.1

说是安装,其实是解压到某个地方,先记着这个地方

配置OpenCV环境变量

添加到环境变量后如下
这里写图片描述

新建项目并配置项目

我选了这“Windows控制台应用程序”
这里写图片描述
建立好了之后,配置项目中OpenCV相关信息。

  1. 打开属性管理窗口。操作路径为视图->其他窗口->属性管理窗口
  2. 在此配置Debug X64,操作右键->属性
    这里写图片描述
  3. 配置VC++目录
  • 包含目录
    包含目录

  • 库目录
    库目录

4.配置链接器->输入
这个名字中的数字表示OpenCV的版本号,此处我的版本号为3.4.1,后面的d表示为Debug。
附加依赖项

测试运行

1
2
3
4
5
6
7
8
9
10
11
// ConsoleApplication3.cpp: 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <opencv2/opencv.hpp>

int main()
{
cv::Mat img = cv::imread("C:\\Users\\xxxxxx\\Desktop\\1.png");
cv::imshow("测试", img);
cv::waitKey(0);
return 0;
}

这里写图片描述
前面配置的是Debug X64的,所以现在选择这个,运行后如下:
这里写图片描述

利用自定义线程池解决OOM问题

从Camera获取到了byte[]类型的图像数据之后,需要送到so库中,让so库进行相应的处理,并对其处理的结果进行相应的反馈;1s大概有30帧的数据,但是除去一些装载数据的事件,时间就变大了,实际可拿到的帧率就变小了;在此每秒取5帧,每张图片大致1~3M。这大致就是解决这个问题的背景框架。起因处理
阅读更多

SmartRefreshLayout使用记录

之前在简书上面的一篇文章,挪到CSDN上来。

前言

此开源控件高度省力、美观,理应不会有太多的问题。奈何该项目的主页上面的说明并不能满足我的需求,因此大致翻看了其中的源码,得到了满足自己需求的用法。因此特意地记录下来自己对其的理解,算是对此控件使用的一个小小笔记吧。

需求

所涉及的需求很简单,也是非常常见的一种用法。下拉刷新,刷新完毕之后关闭刷新动画

开始分析

为了达到这个小小的需求,首先翻看了其的说明文件。并没有与我的需求对应的说明,其上的说明也没有详细说明每个函数的更多用法,多的只是一笔带过的感觉。也许是作者相信我们的能力吧!
这一番得到的结果是:

  • autoRefresh()应该可以让刷新的动画显示;finishRefresh()应该可以让正在显示的刷新动画停止并消失;下拉刷新类似。
  • 可以通过SmartRefreshLayout.setDefaultRefreshFooterCreater()设置默认的刷新时的动画;下拉加载类似。除此之外,还可以通过其的setRefreshHeader()方法,来设置;下拉加载类似。
  • 可以通过setOnRefreshListener设置监听事件。这是说明中给的一段代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    RefreshLayout refreshLayout = (RefreshLayout)findViewById(R.id.refreshLayout);
    refreshLayout.setOnRefreshListener(new OnRefreshListener() {
    @Override
    public void onRefresh(RefreshLayout refreshlayout) {
    refreshlayout.finishRefresh(2000);
    }
    });
    refreshLayout.setOnLoadmoreListener(new OnLoadmoreListener() {
    @Override
    public void onLoadmore(RefreshLayout refreshlayout) {
    refreshlayout.finishLoadmore(2000);
    }
    });
    但是当时并没有仔细看这个,才导致了我后来使用时有点摸不着方向啊!其实在使用过程中对此也是有疑问的,这个监听事件到底是什么时候触发的?所以接下来的一段曲折的经历都是基于此上,谁让我没仔细看这个代码~ 其实我一直以为在onRefresh中调用的是finishRefresh()onLoadMore()中调用的是finishLoadmore(), 所以这根本就说不通啊,啥时候触发这监听事件的都不知道。

由一个小小的不仔细引发的旅程

虽然缘由是无厘头的,但是我喜欢接下来的这个过程。

首先,从autoRefresh()看起

这个函数的用法到底是怎么样的?它到底是用来干什么的?
为了回答自己心中的这个问题,只能习惯性地按着Ctrl点开这个函数。

开源库中的源码为:

1
2
3
4
5
6
7
/**
* 自动刷新
*/
@Override
public boolean autoRefresh() {
return autoRefresh(400);
}

这个400是什么,看了这个兄弟函数之后,发现是delay,也就是延迟。为啥要这个?很是疑惑,但是心中想到的也就是,在过完了400/1000秒之后,可能会显示刷新的动画吧。接着看,

1
2
3
4
5
6
7
/**
* 自动刷新
*/
@Override
public boolean autoRefresh(int delayed) {
return autoRefresh(delayed, 1f * (mHeaderHeight + mHeaderExtendHeight / 2) / mHeaderHeight);
}

大致的意思也就是,说句实话不太想知道这个到底是干啥的,感觉与我想知道的无关。接着看,

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
 * 自动刷新
*/
@Override
public boolean autoRefresh(int delayed, final float dragrate) {
if (mState == RefreshState.None && mEnableRefresh) {
if (reboundAnimator != null) {
reboundAnimator.cancel();
}
Runnable runnable = new Runnable() {
@Override
public void run() {
reboundAnimator = ValueAnimator.ofInt(mSpinner, (int) (mHeaderHeight * dragrate));
reboundAnimator.setDuration(mReboundDuration);
reboundAnimator.setInterpolator(new DecelerateInterpolator());
reboundAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
moveSpinner((int) animation.getAnimatedValue(), false);
}
});
reboundAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mLastTouchX = getMeasuredWidth() / 2;
setStatePullDownToRefresh();
}

@Override
public void onAnimationEnd(Animator animation) {
reboundAnimator = null;
if (mState != RefreshState.ReleaseToRefresh) {
setStateReleaseToRefresh();
}
overSpinner();
}
});
reboundAnimator.start();
}
};
if (delayed > 0) {
reboundAnimator = new ValueAnimator();
postDelayed(runnable, delayed);
} else {
runnable.run();
}
return true;
} else {
return false;
}
}

中间一大堆是初始化了一个Runnable对象,关键的代码在下面:

1
2
3
4
5
6
if (delayed > 0) {
reboundAnimator = new ValueAnimator();
postDelayed(runnable, delayed);
} else {
runnable.run();
}

如果delayed是小于等于0的数,就直接运行刚才初始化的Runnable对象的run()方法。否则又看到了delayed,很明显,小于等于0就会直接执行Runnable对象的run(),也就是会立刻显示刷新动画。因此只需要调用autoRefresh(0)方法,便可立刻开启刷新动画。抱着试试看的心态,好奇地点开了当delayed大于0时会执行的postDelayed()。源码如下:

1
2
3
4
5
6
7
8
9
@Override
public boolean postDelayed(Runnable action, long delayMillis) {
if (handler == null) {
mDelayedRunables = mDelayedRunables == null ? new ArrayList<DelayedRunable>() : mDelayedRunables;
mDelayedRunables.add(new DelayedRunable(action, delayMillis));
return false;
}
return handler.postDelayed(new DelayedRunable(action), delayMillis);
}

果然将Runnable对象交给了handler,然后是handler进行的一些延时执行的操作。
至此,autoRefresh()的用法便了然于心。

再者,finishRefresh()是到底干啥的?

它竟然和上者基本上是同样的套路!
了然!

那么,setOnRefreshListener到底是个什么??

在阅读该项目的WiKi时,看见过默认3秒的说法。可是,我了解到的是,持续3秒,没办法实现啊!我了解到的是,延迟3秒之后再进行某个操作啊!

突然,翻到了源码中的一段代码,一语惊醒梦中人:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (mRefreshListener == null) {
mRefreshListener = new OnRefreshListener() {
@Override
public void onRefresh(RefreshLayout refreshlayout) {
refreshlayout.finishRefresh(3000);
}
};
}
if (mLoadmoreListener == null) {
mLoadmoreListener = new OnLoadmoreListener() {
@Override
public void onLoadmore(RefreshLayout refreshlayout) {
refreshlayout.finishLoadmore(2000);
}
};
}

如果监听事件为空,也就是我们没有设置默认的监听事件,那么它就会为我们创建一个监听事件,而且执行的是3000,这不就是3秒吗!所以啊,原来这个监听事件就是下拉的时候就会触发的啊。等等,

尴尬
这不是finishRefresh()吗?就是延迟3秒钟之后,关闭刷新动画的显示。那么,也就是说,autoRefresh()就只会(延迟)开启动画咯。我还特意去验证了,真是可爱?。
突然间看到了说明中的那段设置监听事件的代码,瞬间笑得哭了起来。

结论

所以,如果设置监听事件的话,并且它为空的话,那么就会一直显示刷新动画。因此,我只需要在开启刷新动画之后,在其中的监听事件的某个时间点关闭刷新动画即可!
坑了我的人就只有自己啊!为自己的一时sb买单。
附上成功地完成了需求之后的动图:
结果终究还算是好的

System.loadLibrary()进程不断重启

解决这个问题花费了最近很长的一段时间,所以觉得非常有写出来分享的必要。其实这个问题,主要还是需要考虑细心!因为说到底是权限的问题,但是又与平常所说的权限问题有很大的差别。网上遇到的一些关于加载so库的问题都是什么连接问题、什么又找不到了的之类,都是可以正常吐出Exception的情况,这个连个Exc
阅读更多

EventBus3的基本使用指南

使用EventBus可以省略繁杂的Handler,效果也是类似的,使用方法也很简单。这里从官网上面做一个小小的总结,可以看得更全面一些。入门贴官方教程定义事件事件就是一个普通的Java类,POJO就好。public class MessageEvent { public final Strin
阅读更多

关于Bitmap相关的一些总结

如何从当前View获取到Bitmap

1
2
3
v.setDrawingCacheEnabled(true);
v.buildDrawingCache();
Bitmap bitmap = v.getDrawingCache();

如何从TextureView中获取Bitmap

1
mTextureView.getBitmap();

如何压缩Bitmap

压缩成适配目标宽、高的Bitmap

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
/**
* get a scaled bitmap from a file located in path, adjust to destWidth and destHeight
* @param path the bitmap's file location
* @param destWidth destination width
* @param destHeight destination height
* @return a scaled bitmap
*/
public static Bitmap getScaledBitmap(String path, int destWidth, int destHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);

float srcWidth = options.outWidth;
float srcHeight = options.outHeight;

int inSampleSize = 1;
if (srcHeight > destHeight || srcWidth > destWidth){
float heightScale = srcHeight / destHeight;
float widthScale = srcWidth / destWidth;

inSampleSize = Math.round(heightScale > widthScale ? heightScale : widthScale);
}

options = new BitmapFactory.Options();
options.inSampleSize = inSampleSize;

return BitmapFactory.decodeFile(path, options);
}

如何将Bitmap与byte[]相互转换

从Bitmap到byte[]

1
2
3
4
int bytes = bmp.getByteCount();
ByteBuffer buf = ByteBuffer.allocate(bytes);
bmp.copyPixelsToBuffer(buf);
byte[] byteArray = buf.array();

从byte[]到Bitmap

1
2
3
Bitmap stitchBmp = Bitmap.createBitmap(width, height, type);
stitchBmp.copyPixelsFromBuffer(ByteBuffer.wrap(byteArray));
imageView.setImageBitmap(stitchBmp);

缩略图相关

理解ThumbnaiUtils