- 浏览: 285846 次
- 性别:
- 来自: 深圳
-
文章分类
- 全部博客 (142)
- android (64)
- android team 应用开发流程 (0)
- android 个人 开发流程 (1)
- android UI 切换皮肤 (1)
- java (9)
- 敏捷开发 (1)
- git (1)
- 学习 (2)
- hibernate (0)
- jQuery (1)
- windows (2)
- tomcat (1)
- Spring (3)
- struts2 (5)
- mysql (4)
- linux (15)
- JBPM (2)
- maven (4)
- 企业管理 (1)
- Iphone (1)
- 工作计划 (0)
- news (1)
- MOVE (1)
- exception-android (1)
- RFID (1)
- 测试 (7)
- android基础 (1)
- Gson (1)
- Android中的单元测试 (1)
最新评论
-
jlees:
Nice post.i hope this will help ...
Business mobile application development. The developer’s insight. -
weisi2375:
确实很详细的开发流程。
Android应用开发全流程 -
mikefather:
不错不错
Android,谁动了我的内存 -
ylzyd12345:
mark一下,谢谢分享
android的一些开源项目 -
limingcai:
确实不行,2.2就不行了,虽说2.3了 只有1.6可以
Android完全关闭应用程序
转载: http://www.enterra-inc.com/techzone/handling_sql_issues/
Today OS Android is gaining more popularity enhancing the range of tasks that users would want to be resolved here and now. Alongside with the most pertaining tasks that we can encounter there are those that require handling large volumes of data for reasonable time like for example full text search thru the database, that is SQLite that is used on OS Android with the accessory package android.database.sqlite. However this package contains only a set of tools to work with the database which is not in fact a framework that regulates approaches to database access implementations.
Currently Google does not provide any detailed recommendations to work with the database. In official documentation there are only 2 simple examples using SQLite (“NotePad” and “SearchableDictionary”) therefore programmers find their own ways to implementing database tasks and as the result there are many methods to resolve though often incorrect.
It is rather hard to elaborate the right approach to implementation. The primary issues here are weak documentation and non-evident features in the classes of the android.database.sqlite package.
The following errors are the first evidence that it is high time to think of the architecture:
- database is locked – shows up at multithreaded record to the database.
- database is closed – may appear when working with the database from various parts of the program e.g. Activity and Service.
- corrupted database – arises if the database file is corrupted either by the user or at unexpected interruption of recording to the database (phone switched off, OS error, lack of space, bad sectors on the SD card etc.)
- low efficiency at working with the DB – may appear due to internal deadlocks, concurrent transactions, high volume of logging, absence of batch processing.
Let’s review in details the reasons of why the issues arise and possible “non-evident” demonstration of such issues as well as methods to their resolution.
The “Database Is Locked” Issue (Aka Concurrency)
Programmers often face the question like “how to better work with SQLiteOpenHelper”. Indeed since any part of the program can send a query to the data access layer (service, presenter, widget, etc.) then SQLiteOpenHelpershould be available everywhere alongside with Context. Also there is a question if it’s worth creating a personal connection to the database for each part of the program, will then the speed of queries be increased? There are questions on concurrent access to the database and of course locking on writing.
First of all it should be noted that locking in SQLite is done on the file level which guarantees locking of changes from different threads and connections. Thus multiple threads can read the database however one can only write to it. More on locking in SQLite can be read at SQLite documentation but we are most interested in the API provided by OS Android.
Writing with two concurrent threads can be made both from a single and from multiple database connections. Since only one thread can write to the database then there are two variants:
- If you write from two threads of one connection then one thread will await on the other to finish writing.
- If you write from two threads of different connections then an error will be – all of your data will not be written to the database and the application will be interrupted with SQLiteDatabaseLockedException.
It becomes evident that the application should always have only one copy of SQLiteOpenHelper(just an open connection) otherwise SQLiteDatabaseLockedException can occur at any moment.
Different Connections At a Single SQLiteOpenHelper
Everyone is aware that SQLiteOpenHelper has 2 methods providing access to the database getReadableDatabase()and getWritableDatabase(), to read and write data respectively. However in most cases there is one real connection. Moreover it is one and the same object:
SQLiteOpenHelper.getReadableDatabase()==SQLiteOpenHelper.getWritableDatabase()
It means that there is no difference in use of the methods the data is read from. However there is another undocumented issue which is more important – inside of the class SQLiteDatabase there are own locks – the variable mLock. Locks for writing at the level of the object SQLiteDatabase and since there is only one copy ofSQLiteDatabase for read and write then data read is also blocked. It is more prominently visible when writing a large volume of data in a transaction.
Besides the internal locks the SQLiteDatabase Class has another interesting feature: this class (up to API 11) allows creating transactions only in the exclusive transaction mode. Because of this holdups may occur when actively working with the database. Let’s consider an example of such an application that should download a large volume of data (approx. 7000 lines containing BLOB) in the background on first launch and save it to the database. If the data is saved inside the transaction then saving takes approx. 45 seconds but the user can not use the application since any of the reading queries are blocked. If the data is saved in small portions then the update process is dragging out for a rather lengthy period of time (10-15 minutes) but the user can use the application without any restrictions and inconvenience. “The double edge sword” – either fast or convenient. The reasons for such an issue and summary are covered in the article from Kevin Galligan “Android Sqlite Locking”.
Then how shall we resist the “standard” behavior? In the new versions of Android starting from API 11 Google has already fixed a part of issues related to SQLiteDatabase functionality as the following methods have been added:
beginTransactionNonExclusive() – creates a transaction in the “IMMEDIATE mode”.
yieldIfContendedSafely() – temporary seizes the transaction in order to allow completion of tasks by other threads.
isDatabaseIntegrityOk() – checks for database integrity
Please read in more details in the documentation.
However for the older versions of Android this functionality is required as well.
The Solution
First locking should be turned off and allow reading the data in any situation.
SQLiteDatabase.setLockingEnabled(false); cancels using internal query locking – on the logic level of the java class (not related to locking in terms of SQLite)
SQLiteDatabase.execSQL(“PRAGMA read_uncommitted = true;”); Allows reading data from cache. In fact, changes the level of isolation. This parameter should be set for each connection anew. If there are a number of connections then it influences only the connection that calls for this command.
SQLiteDatabase.execSQL(“PRAGMA synchronous=OFF”); Change the writing method to the database – without “synchronization”. When activating this option the database can be damaged if the system unexpectedly fails or power supply is off. However according to the SQLite documentation some operations are executed 50 times faster if the option is not activated.
Unfortunately not all of PRAGMA is supported in Android e.g. “PRAGMA locking_mode = NORMAL” and “PRAGMA journal_mode = OFF” and some others are not supported. At the attempt to call PRAGMA data the application fails.
In the documentation for the method setLockingEnabled it is said that this method is recommended for using only in the case if you are sure that all the work with the database is done from a single thread. We should guarantee than at a time only one transaction is held. Also instead of the default transactions (exclusive transaction) the immediate transaction should be used. In the older versions of Android (below API 11) there is no option to create the immediate transaction thru the java wrapper however SQLite supports this functionality. To initialize a transaction in the immediate mode the following SQLite query should be executed directly to the database, – for example thru the method execSQL:
SQLiteDatabase.execSQL(“begin immediate transaction”);
Since the transaction is initialized by the direct query then it should be finished the same way:SQLiteDatabase.execSQL(“commit transaction”);
Then TransactionManager is the only thing left to be implemented which will initiate and finish transactions of the required type. The purpose of TransactionManager – is to guarantee that all of the queries for changes (insert, update, delete, DDL queries) originate from the same thread.
The “Database Is Closed” Issue
When working with the database from a single Activity thru SQLiteOpenHelper it is obvious that the database should be opened together with Activity and closed when closing Activity. However if a number of Activities are concurrently working with the database as well as Services and part of data is shared by ContentProvider then there is a question: when the connection with the database should be opened and closed? If the connection is opened and closed after every query then the database query speed will fall down in times and if it is opened when the application starts and close on shutdown it is not clear when we leave the application (and if the service is still working or the provider is not used – there is only one method left – Application.onTerminate()). However none of the methods is correct. The database connection can be automatically closed upon the following conditions:
If a number of Activities independently from each other are opening new connections then the error may occur that is described in the case “database is locked” above.
If we open a connection with the database upon the application start and close at Application.onTerminate(), the database connection can be closed on its own at another call for Cursor.getCount() or Cursor.onMove(). If we carefully take a look at the source code of the respective classes then we can see that at some combination of conditions the method SQLiteDatabase.onAllReferencesReleased() will be finally called that will call the native method dbclose(). Here is a more detailed description of the issue, the succession of calls and required conditions are described here.
Probably this is one of the reasons why “ManagedCursor” has been claimed “Deprecated”.
This issue is widely known and multiple ways have been proposed to its resolution.
Variant 1
At every call to the database one should check if the database is opened or closed and if opened then it should be reopened.
SQLiteDatabase db;
try {
db = super.getReadableDatabase();
}
catch (SQLiteException e) {
Log.d(Constants.DEBUG_TAG, e.getMessage());
db = reopenDatabase(dbFile.getAbsolutePath(), null);
}
return db;
}
This method has an evident drawback – if we query the database and then save the link to already opened copy and use the received copy not calling SQLiteDatabase.getReadableDatabase() then this method won’t work.
Variant 2
Mandatory adding a fake reference to the database and keep it while the database is in useSQLiteClosable.acquireReference();
However here the database should be closed with preliminary cleanup of all the references manually created. Nevertheless the number of references may become zero therefore it is required to constantly track the number of references and populate them when necessary. Though this method is not quite successful.
Variant 3
Close and open the database after every query. This method is unreliable since two independent connections for writing are feasible to be created and an error will occur. If using this approach only for reading then an error won’t happen however will significantly slows down the application efficiency (especially when there is a large number of queries to the database).
Variant 4
Using ContentProvider to access the database. And it is desirable to use exactly one provider – it is simple to implement since it can be added with an unlimited number of Uri’s. The point is that ContentProvider tracks the database status. And the decision on when it is time to close the database is assigned to the OS – it will delete the old providers from the memory and return them when first required.
Please review the detailed description on how to work with ContentProvider on the official web site.
The Issue of the “Corrupted Database”
There is little space on Android phones left for applications and this space should be taken care of otherwise the user will sacrifice your application for another game. Almost any application uses a database to store data and if the database is too large then it is desirable to store it on the SD card. The older versions of Android (2.2 and earlier) do not allow creating the database on the SD card via standard tools of SQLiteOpenHelper however this can be bypassed if using AndroidConnectionSource from ORMLite.
One should bear in mind that anything that is visible to the user can be deleted. The user can delete or otherwise corrupt a database file, can take out the SD card from the device and many other things. However the user is not only one who can corrupt the database. A telephone is the device with unreliable power supply – part of data can be left unrecorded (the most vital if logging is not activated), the database can be corrupted when on download or when using a preinstalled database etc. please read more on items that can corrupt the database in the article
“How To Corrupt An SQLite Database File”.
If the developer has not managed to implement the database restore mechanism then Android will create the database anew. However it happens that the database can be restored. The simplest way – query data from any available tables and insert them to the new database. However often it would be just enough to execute the“VACUUM” command – this method recreates the database and restores maximum of data.
Frequently it is required to create an application with preinstalled data. For this an available database can be taken and placed to the raw folder and when installing the application the database will be copied to the device. The file with the database would be best to be saved to the raw folder. The folder assets seems to be more proper since it can be compressed however it is impossible to get data over 1 Mb (please see [here]), and therefore the database should be split into files of 1 Mb which is rather inconvenient. It is important that the database should always be built on the emulator of the lowest of the supported versions since if the preinstalled database is built on Android 2.3 then on Android 2.2 it will give the “corrupted database” error though on devices 2.3 and above the database will work correctly.
Optimizing Queries
Speed of completing queries is made of multiple factors however optimizing the query and the database structure are the most important ones. In order to optimize queries there are many standard methods which can be easily found in the Internet so let’s just specify SQLite optimization features. For brevity let’s just arrange them as theses.
There is no need in writing queries that return over 1000 lines or data of over 1 Mb – always use the operator limit. If a query returns more than 1000 lines then a warning will be given to the log or the application will fail depending on available memory and the device. In case a long list is required to be displayed there are two solutions:
a) Query the list in parts and then merge using android.database.CursorJoiner.
b) Implement the auto updated list on the interface (the list with resumable download).
Single query is much faster than 2 separate. Join is more preferable to be used however execute 1 query. An order for restrictions to join is very important so that there is no Cartesian product when strings are selected by the operator “where”.
If anything is required to be changed in the database – do it in the transaction. This will not only guarantee the data integrity but significantly accelerates execution of the task. The thing is that at any change to the database the changes file is created next to the file. If you do 100 inserts then 100 times the changes file will be created and deleted and if the 100 inserts are in the transaction then the changes file is created only once.
If you need to create a table from already existing data then use INSERT AS SELECT (do not execute separate INSERT) which significantly facilitates the execution speed.
If you have received too much data from the file at once and such “large” query is not often repeated then clean the unnecessary memory SQLiteDatabase.releaseMemory().
First more simple conditions should be written in the operator where
SELECT * FROM tablename WHERE col1 LIKE ‘%string%’ AND col2 = 123456
Works 3-4 times slower than
SELECT * FROM tablename WHERE col2 = 123456 AND col1 LIKE ‘%string%’
Correct indexing of tables facilitates execution of queries 5-7 times. Indexing should be made for those fields that are with join and further to where search is made. And it is better to indicate the direction for the indexer for example:
CREATE INDEX index_name ON table_name (column_name ASC).
Use FTS3 for large tables with search which significantly speeds up text search by the table. Instead of LIKE use MATCH however be aware that MATCH on default works as search for a word as a whole and not for the sub-string. Please see the description of FTS3.
The Conclusion
In this article the main issues have been outlined when working with SQLite in Android. Unfortunately the API has a lot of gaps, to resolve some issues there is no documentation available as well as from time to time at work the system errors are detected. However the fact that with every new version the Android API is getting more flexible and comprehensive, errors are being fixed and documentation enhanced.
Want to benefit by our experience in mobile application development for Android? Start with your project estimation right now!Share on email
发表评论
-
Resource-type-->Color State List Resource
2013-04-22 10:50 1689Color State List Resource Col ... -
Business mobile application development. The developer’s insight.
2012-11-07 17:49 1657from: http://www.enterra-inc.co ... -
git 获取android source
2012-08-15 12:52 3695在做android开发的时,在遇到某一问题,想看andro ... -
Android 手机上获取物理唯一标识码
2012-07-27 10:27 11785唯一标识码这东西在网络应用中非常有用,例如检测是否 ... -
android listview adapter
2012-06-23 14:41 1027listview 在什么情况下会刷新: 1. 当ada ... -
Android多线程下载详解
2012-06-20 18:31 953http://www.pin5i.com/showtopic- ... -
Unable to open sync connection!
2012-06-18 17:04 976把设置里的USB调试重新开了开,问题解决! -
android checkbox 定制(修改checkbox 的图片)
2012-06-18 14:30 3664转载:http://www.bangchui.org/read ... -
Android ProgressBar自定义图片进度,自定义渐变色进度条
2012-06-15 16:53 7600 -
Android应用开发全流程
2012-06-15 09:21 3789转载:http://blog.csd ... -
intent.setDataAndType
2012-06-13 18:24 75201. Intent open a picture ... -
Android操作HTTP实现与服务器通信
2012-06-03 14:47 1756本示例以Servlet为例,演示Android与Serv ... -
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thre
2012-06-03 12:00 9046当应用程序启动,创建了一个叫“main”的线程,用于管理 ... -
这篇文章是android开发人员的必备知识,是我特别为大家整理和总结的,不求完美,但是有用。 1.签名的意义 为了保证每个应用程序开发商合法ID,防止部分开
2012-05-25 13:58 1536这篇文章是android开发人员的必备知识,是我特别为大 ... -
android Collections.sort(List<T> list) 与JAVA Collections.sort(List<T> list)
2012-05-04 10:33 1870Info.java : public class In ... -
android string xliff:g
2012-03-22 10:47 1029这个主要用于程序中,动态的插入内容时候使用,例如, ... -
android的一些开源项目
2011-12-07 17:13 2178转自: http://www.uuroid.com ... -
Understanding the Android Build Process
2011-11-25 12:38 987http://www.alittlemadness.com/2 ... -
Android 命令行手动编译打包详解
2011-11-24 10:07 1257Android 命令行手动编译打包过程图 【详细步骤】: 1 ... -
Android ListView 自定义背景后 滚动时的背景变黑问题
2011-11-21 14:30 1566ListView是常用的显示控件, ...
相关推荐
should be disabled, as enabling it may result in issues when generating XML ; documents, however this remains supported for backward compatibility reasons. ; Note that this directive does not control...
Fixed a compatibility issue with some third party syntax definitions that include JavaScript.sublime-syntax Reduced the default number of worker processes used for indexing. This can be manually ...
基于万能逼近原理的自适应模糊控制算法在多自由度AUV运动控制中的应用与抗干扰补偿Simulink仿真研究,自适应模糊控制算法的万能逼近原理与多自由度AUV运动控制的抗干扰补偿技术——基于Simulink的仿真研究,万能逼近原理自适应模糊控制算法的多自由度AUV运动控制抗干扰补偿simulink仿真 ,核心关键词:万能逼近原理; 自适应模糊控制算法; 多自由度AUV运动控制; 抗干扰补偿; Simulink仿真。,基于万能逼近的模糊控制算法多自由度AUV抗干扰补偿Simulink仿真
deepseek最新资讯、配置方法、使用技巧,持续更新中
deepseek最新资讯、配置方法、使用技巧,持续更新中
结合扩展卡尔曼滤波与滑模观测器的策略:优化电角度估计,反电势波形逼近完美正弦波,结合扩展卡尔曼滤波与滑模观测器的反电势波形优化:正弦波形展现近乎完美精度,电角度估算与实际应用差异微小,扩展卡尔曼滤波与滑模观测器的结合,反电势波形近乎完美的正弦波形,观测器估算转子电角度与实际电角度相差0.3弧度左右,转速跟随效果较好。 ,核心关键词:扩展卡尔曼滤波; 滑模观测器; 反电势波形; 转子电角度估算; 转速跟随效果。,卡尔曼滑模观测器:优化正弦波转子角度与转速估算
毕业设计_基于springboot+vue的**学生公寓管理系统**【源码+sql+可运行】【**50217**】.zip 全部代码均可运行,亲测可用,尽我所能,为你服务; 1.代码压缩包内容 代码:springboo后端代码+vue前端页面代码; 脚本:数据库SQL脚本 效果图:运行结果请看资源详情效果图 2.环境准备: - JDK1.8+ - maven3.6+ - nodejs14+ - mysql5.6+ - redis 3.技术栈 - 后台:springboot+mybatisPlus+Shiro - 前台:vue+iview+Vuex+Axios - 开发工具: idea、navicate 4.功能列表 - 系统设置:用户管理、角色管理、资源管理、系统日志 - **业务管理:业务管理:公寓信息、房间信息、入住记录、学生信息** 3.运行步骤: 步骤一:修改数据库连接信息(ip、port修改) 步骤二:找到启动类xxxApplication启动 4.若不会,可私信博主!!!
1、文件内容:xorg-x11-server-source-1.20.4-29.el7_9.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/xorg-x11-server-source-1.20.4-29.el7_9.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊
1、文件内容:yum-plugin-ps-1.1.31-54.el7_8.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/yum-plugin-ps-1.1.31-54.el7_8.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊
基于模型预测控制(MPC)的无人船与无人车编队一致性协同控制研究(附原文献),基于模型预测控制(MPC)的无人船与无人车编队一致性协同控制研究(附原文献),无人船编队 无人车编队 MPC 模型预测控制 多智能体协同控制 一致性 MATLAB 无人车 USV 带原文献 ,无人船编队; 无人车编队; MPC 模型预测控制; 多智能体协同控制; 一致性; MATLAB; USV; 原文献,无人系统协同控制:MPC模型预测控制下的多智能体编队与一致性研究(原文献支撑)
4套中级通信工程师综合真题及答案(2019,2020,2021,2023),适用于需要考中级通信工程师的人群
deepseek最新资讯,配置方法,使用技巧,持续更新中
基于matlab的锁相环PLL相位噪声拟合仿真代码集合:多个版本建模与仿真,高质量的锁相环PLL仿真代码集合:Matlab与Simulink建模研究,[1]锁相环 PLL 几个版本的matlab相位噪声拟合仿真代码,质量杠杠的,都是好东西 [2]锁相环matlab建模稳定性仿真,好几个版本 [3]锁相环2.4G小数分频 simulink建模仿真 ,PLL; Matlab相位噪声拟合仿真; Matlab建模稳定性仿真; 锁相环2.4G小数分频Simulink建模仿真,MATLAB仿真系列:锁相环PLL及分频器建模仿真
exceptionLogs.zip
基于光伏微网的经济性与并网负荷波动率双目标优化调度策略:蓄电池与V2G协同管理策略仿真研究,MATLAB下光储充微网结合电动汽车V2G的多目标协同调度策略研究:经济性与并网负荷波动性的对比分析,MATLAB代码:考虑V2G的光储充一体化微网多目标优化调度策略 关键词:光储充微网 电电汽车V2G 多目标优化 蓄电池优化 调度 参考文档:《光伏微网下考虑V2G补偿蓄电池容量的双目标优化调度策略》,已经投稿EI会议,中文说明文档可联系我咨询 仿真平台:MATLAB 平台 优势:代码注释详实,适合参考学习,相关成果已经采用,程序非常精品,请仔细辨识 主要内容:过建立光伏微网中以经济性和并网负荷波动率为双目标的蓄电池和V2G的协同调度模型。 采用粒子群算法,对电网、微网调度中心和电动汽车用户三方在无、无序、转移和调度V2G电动汽车负荷四种运行模式下的经济和安全影响进行对比。 最后,根据算例分析,求解四种模式下两级负荷曲线及经济收益表。 对比分析得出,引入V2G可以替代部分容量的蓄电池,使光伏微网在负荷峰谷平抑、三方经济和安全等方面进一步优化。 求解采用的是PSO算法(粒子群算法),求解效果极
javascript 动态网页设计期末大作业(自己手写的,高分期末作业),含有代码注释,新手也可看懂,个人手打98分项目,导师非常认可的高分项目,毕业设计、期末大作业和课程设计高分必看,下载下来,简单部署,就可以使用。该项目可以直接作为毕设、期末大作业使用,代码都在里面,系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值,项目都经过严格调试,确保可以运行! javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期
混合智能体系统编队控制:分布式优化与15异构混合阶的挑战,异构混合阶智能体系统编队控制的分布式优化策略研究,15异构混合阶多智能体系统编队控制的分布式优化(无参考文献) ,核心关键词:15异构混合阶; 多智能体系统; 编队控制; 分布式优化; 无参考文献。,15混合阶多智能体系统编队分布式优化控制
javascript 动态网页设计期末大作业(自己手写的,很适合期末作业),含有代码注释,新手也可看懂,个人手打98分项目,导师非常认可的高分项目,毕业设计、期末大作业和课程设计高分必看,下载下来,简单部署,就可以使用。该项目可以直接作为毕设、期末大作业使用,代码都在里面,系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值,项目都经过严格调试,确保可以运行! javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascrip
X光安检OPIXray数据集已经转换为VOC格式,可直接转换为为YOLO
DataX--Web:图形化界面简化大数据任务管理_datax-web