开发工具:Android Studio
注意:
在使用Android Studio进行开发前,要保证用户目录的名字为英文
例C:\Users\Yourname,否则开发过程中会频繁遇到
sdk下载失败、依赖下载失败、访问超时等问题,而且很难解决
本人最终选择了备份数据,将电脑重置才得以解决(下载速度很快,没遇到上述问题)
正文都会单独形成一篇文章
正文1:封装设置对话框尺寸及属性的方法
//dialog创建
View myView = LayoutInflater.from(context).
inflate(R.layout.my_dialog, null, false);
final AlertDialog dialog = new AlertDialog.
Builder(context).setView(myView).create();
//封装的方法
public void setWindow(AlertDialog dialog){
//设置点击对话框之外的区域弹窗不消失
dialog.setCancelable(false);
//显示对话框
dialog.show();
//设置对话框尺寸 window变量初始化时声明即可(Window window)
window = Objects.requireNonNull(dialog.getWindow());
window.setLayout(1000, 600);
//设置背景图全部显示
window.setBackgroundDrawableResource(android.R.color.transparent);
}
//对应布局文件(my_dialog.xml),图片自取
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="200dp"
android:orientation="vertical"
android:background="@drawable/new_dialog"
tools:ignore="ContentDescription" >
</RelativeLayout>
正文2:使用线程池处理多个任务
//创建线程池
private final ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(15, 40, 100,
TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
// 定义线程
Runnable test1Runnable;
Runnable test2Runnable;
Runnable test3Runnable;
//定义处理任务的线程方法
private Runnable createTest1Runnable(){
//lambda表达式
return () -> {
//业务逻辑1
/**
设置标志位testFlag(初始值true)
退出页面在onBackPressed()回调函数或
onDestroy()回调函数中设置其为false,进而结束线程
*/
while (testFlag) {
//业务逻辑2
}
};
}
//线程赋值
test1Runnable = createTest1Runnable();
//放入线程池执行
threadPoolExecutor.execute(test1Runnable);
//在回调函数onDestroy()中关闭线程
threadPoolExecutor.shutdownNow();
正文3:OkHttp和JSON的简单使用
//使用OkHttp建立连接并发送请求,使用JSON处理返回的json字符串并获取某个字段的值(配合线程使用)
private Runnable testOkHttpAndJSONRunnable(){
//创建实体类封装JSON对象
TestRequesEntity testRequestEntity = new TestRequesEntity();
//转换json字符串
final String testRequestJson = JSON.toJSONString(testRequestEntity);
return () -> {
//建立客户端testClient
OkHttpClient testClient = new OkHttpClient();
//设置访问地址testURL
String testURL = "";
//创建HTTP请求testRequest,传入json字符串testRequestJson
Request testRequest = new Request.Builder()
.url(testURL)
.post(RequestBody.create(MediaType.parse("application/json"),
testRequestJson))
.build();
//设置标志位testFlag,用于停止线程
while (testFlag) {
try {
//执行发送的指令,封装返回值testResponse
Response testResponse = testClient.newCall(testRequest ).execute();
//获取返回数据testResponseData
String testResponseData = Objects.requireNonNull(testResponse .body()).string();
//打印日志
Log.d(TAG, "testResponseData: " + testResponseData);
//转换成JSON对象
JSONObject jsonObject = JSON.parseObject(testResponseData);
// testResults字段为一个json串(json中还包含着json)
JSONObject jsonObjectTestResults = jsonObject .getJSONObject("testResults");
// test字段为一个字符串
String testJSONString = jsonObject.getString("test");
// 拓展:刷新UI(实时刷新数据)
runOnUiThread(() -> {
textView.setText(testJSONString );
});
}
catch (Exception e){
e.printStackTrace();
}
}
};
}
正文4:OpenCV SDK下载及Android Java环境搭建
参考链接:OpenCV SDK下载及Android Java环境搭建
1、官方网站下载sdk:https://opencv.org
1)悬停Library后点击Releases
2)点击Android,打开网页后会自动下载
打开后得到以下内容
2、打开Android新建项目进行测试,依次点击File -> New -> New Project
- 选择
Empty Activity
,点击Next
Name
栏输入OpenCVTest
(命名自定义),点击Finish
3、将OpenCV SDK导入项目
- 将视图切换到
Project
,右键项目总目录,选择New -> Module
然后选择Android Library
,将模块命名为opencv-test
(自定义命名)
最后点击Finish
- 删除目录下所有内容
- 复制
sdk
目录下的内容到模块opencv-test
下
- 打开
opencv-test
目录下的build.gradle
文件,删除图中kotlin
那一行,然后点击Sync Now
- 显示
BUILD SUCCESSFUL
即可
4、将opencv-test模块依赖到app模块中
- 打开
app
模块下的build.gradle
,在dependencies
中加入以下内容,点击Sync Now
implementation 'com.rabbitmq:amqp-client:5.12.0'
implementation project(path: ':opencv-test')
- 在
app
模块下的src/main/java
的包目录下新建Activity
:OpencvTestActivity
public class OpencvTestActivity extends Activity {
ConnectionFactory factory;
//定义线程池处理上帝视角引起的程序卡死问题
private final ThreadPoolExecutor threadPoolExecutor = new
ThreadPoolExecutor(3, 10, 1, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
private boolean mQuit = false;//当为true的时候退出画图线程
private boolean closeCompleteFlag = false;//当为true的时候表示通道和连接都关闭
//播放视频页面(前、后、左、右)
private SurfaceHolder surfaceHolder;
private static final String TAG = "opencv启动日志";
//消费rabbitmq数据的线程
Runnable basicConsumeRunnable;
volatile String picture;
//画图的runnable
Runnable drawRunnable;
//全局定义各视角连接和通道,便于退出Activity后关闭
Connection connection;
Channel channel;
//关闭通道和连接的runnable
Runnable closeRunnable;
private final String TAG_THREAD = "线程池";
private static final String TAG_RABBITMQ = "RabbitMQ";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_opencv);
//初始化opencv相关变量
initOpencv();
playVideo();
}
private void initOpencv() {
SurfaceView mySurfaceView = findViewById(R.id.surfaceViewTest);
surfaceHolder = mySurfaceView.getHolder();
setupConnectionFactory();
}
//上帝视角相关函数以及线程的开启和关闭 Start
@SuppressLint("HandlerLeak")
private void playVideo() {
//用于从线程中获取数据,更新ui
Handler incomingMessageHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
picture = msg.getData().getString("msg");
}
};
//视频号
basicConsumeRunnable = getBasicConsumeRunnable(incomingMessageHandler);
threadPoolExecutor.execute(basicConsumeRunnable);
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
drawRunnable = getDrawRunnable();
threadPoolExecutor.execute(drawRunnable);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
});
}
/**
* 关闭channel和connection的runnable,网络请求不能在主线程挂起
*
* @return 运行的runnable
*/
private Runnable getCloseRunnable(Channel channel, Connection connection){
return () -> closeChannelAndConnection(channel, connection);
}
//画图线程
private Runnable getDrawRunnable() {
return () -> {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
//矩形区域:左上角、右下角(坐标)(老铁稳)
Rect mSrcRect = new Rect(0, 0, 6500, 1080);;
Rect mDestRect = new Rect(0, 0, 7200, 1080);
while (!mQuit) {
// Lock the canvas for drawing
Canvas canvas = surfaceHolder.lockCanvas();
if (canvas == null) {
Log.i("WindowSurface", "Failure locking canvas");
continue;
}
if (picture != null) {
Bitmap bmp = returnBitMap(picture);
// Bitmap scaled = scaleBitmap(bmp, 0.95f);
canvas.drawBitmap(Objects.requireNonNull(bmp), mSrcRect, mDestRect, paint);
}
// Update graphics
// All done
surfaceHolder.unlockCanvasAndPost(canvas);
}
Log.d(TAG_THREAD, "画图线程已关闭");
};
}
//消费者线程
private Runnable getBasicConsumeRunnable(Handler incomingMessageHandler) {
return () -> basicConsume(incomingMessageHandler);
}
//收消息(从发布者那边订阅消息)
private void basicConsume(final Handler handler) {
try {
Log.d(TAG, "basicConsume: 已连接");
connection = factory.newConnection();
Log.d(TAG, "basicConsume: 通道已建立");
channel = connection.createChannel();
//实现Consumer的最简单方法是将便捷类DefaultConsumer子类化。可以在basicConsume 调用上传递此子类的对象以设置订阅:
channel.basicConsume("video_6", false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body);
long deliveryTag = envelope.getDeliveryTag();
channel.basicAck(deliveryTag, false);
//从message池中获取msg对象更高效
Message uiMsg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("msg", msg);
uiMsg.setData(bundle);
Log.d(TAG, "handleDelivery: msg" + msg);
handler.sendMessage(uiMsg);
}
});
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
//关闭通道和连接
private void closeChannelAndConnection(Channel channel, Connection connection) {
//关闭通道
if (channel != null){
if (channel.isOpen()) {
try {
channel.close();
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
Log.d(TAG_RABBITMQ, "channel关闭状态:" + !channel.isOpen());
}
//关闭连接
if (connection != null) {
if (connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Log.d(TAG_RABBITMQ, "connection关闭状态:" + !connection.isOpen());
}
Log.d(TAG_THREAD, "通道线程已关闭");
closeCompleteFlag = true;//设置为true表示关闭完成
}
//关闭正在运行的所有线程
private void closeMyActivity() {
//设置为true可以关闭画图线程
mQuit = true;
//关闭channel和connection通道的线程
closeRunnable = getCloseRunnable(channel, connection);
threadPoolExecutor.execute(closeRunnable);
//closeCompleteFlag为true表示通道关闭完成
while (!closeCompleteFlag) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
threadPoolExecutor.shutdownNow();//杀死所有运行结束的线程
Log.d(TAG_THREAD, "线程池排队数量:" + threadPoolExecutor.getQueue().size());
Log.d(TAG_THREAD, "正在运行的线程数量:" + threadPoolExecutor.getActiveCount());
Log.d(TAG_THREAD, "执行完成的线程数量" + threadPoolExecutor.getCompletedTaskCount());
}
@Override
protected void onResume() {
super.onResume();
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
//openCV4Android 需要加载用到
private final BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
if (status == LoaderCallbackInterface.SUCCESS) {
Log.i(TAG, "OpenCV loaded successfully");
} else {
super.onManagerConnected(status);
}
}
};
private Bitmap returnBitMap(String message) {
byte[] frame = Base64.decode(message.getBytes(), Base64.DEFAULT);
try{
Mat mat = Imgcodecs.imdecode(new MatOfByte(frame),Imgcodecs.IMREAD_UNCHANGED);
Bitmap bmp = Bitmap.createBitmap(mat.width(), mat.height(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mat,bmp);
return bmp;
}catch (Exception e){
return null;
}
}
/**
* 连接设置
*/
private void setupConnectionFactory() {
factory = new ConnectionFactory();
factory.setHost("47.242.239.206");
factory.setPort(5672);
factory.setUsername("zdx19981006");
factory.setPassword("19981006");
}
// 缩放函数
private Bitmap scaleBitmap(Bitmap origin, float ratio) {
if (origin == null) {
return null;
}
int width = origin.getWidth();
int height = origin.getHeight();
Matrix matrix = new Matrix();
matrix.preScale(ratio, ratio);
Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
if (newBM.equals(origin)) {
return newBM;
}
origin.recycle();
return newBM;
}
@Override
public void onBackPressed() {
//选择确定会杀死所有线程
closeMyActivity();
super.onBackPressed();
}
//销毁Activity回调方法
@Override
protected void onDestroy() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPoolExecutor.shutdownNow();
Log.d(TAG_THREAD, "线程池排队数量:" + threadPoolExecutor.getQueue().size());
Log.d(TAG_THREAD, "正在运行的线程数量:" + threadPoolExecutor.getActiveCount());
Log.d(TAG_THREAD, "执行完成的线程数量" + threadPoolExecutor.getCompletedTaskCount());
super.onDestroy();
}
}
- 对应的
xml
文件:activity_main_opencv.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<SurfaceView
android:id="@+id/surfaceViewTest"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
- 在
AndroidManifest.xml
中修改主启动类进行测试
<!--测试opencv-->
<activity android:name=".OpencvTestActivity"
tools:ignore="IntentFilterExportedReceiver">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
5、启动程序,查看日志
- 连接手机,打开开发者选项进行调试
- 点击
Logcat
,输入opencv启动日志
检索日志 - 出现以下日志即成功集成opencv
持续更新中...