Android About ContentProvider
Contents:
- ContentProvider Structural Analysis
- What is URI?
- Query Attribute,SQL UNION
ContentProvider Structural Analysis
一、字段部分
1 public class NotePadProvider extends ContentProvider { 2 3 private static final String TAG = "NotePadProvider"; 4 5 private static final String DATABASE_NAME = "note_pad.db"; 6 private static final int DATABASE_VERSION = 2; 7 private static final String NOTES_TABLE_NAME = "notes"; 8 9 private static HashMap<String, String> sNotesProjectionMap; 10 private static HashMap<String, String> sLiveFolderProjectionMap; 11 12 private static final int NOTES = 1; 13 private static final int NOTE_ID = 2; 14 private static final int LIVE_FOLDER_NOTES = 3; 15 16 private static final UriMatcher sUriMatcher;
5,DATABASE_NAME,表征你要使用的数据库名字,这里是note_pad.db,也就是说会在应用程序的数据文件夹里面生成一个名字为note_pad.db的Sqlite数据库文件。
二、静态初始化语句块部分
1 static { 2 sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 3 sUriMatcher.addURI(NotePad.AUTHORITY, "notes", NOTES); 4 sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID); 5 sUriMatcher.addURI(NotePad.AUTHORITY, "live_folders/notes", LIVE_FOLDER_NOTES); 6 7 sNotesProjectionMap = new HashMap<String, String>(); 8 sNotesProjectionMap.put(Notes._ID, Notes._ID); 9 sNotesProjectionMap.put(Notes.TITLE, Notes.TITLE); 10 sNotesProjectionMap.put(Notes.NOTE, Notes.NOTE); 11 sNotesProjectionMap.put(Notes.CREATED_DATE, Notes.CREATED_DATE); 12 sNotesProjectionMap.put(Notes.MODIFIED_DATE, Notes.MODIFIED_DATE); 13 14 // Support for Live Folders. 15 sLiveFolderProjectionMap = new HashMap<String, String>(); 16 sLiveFolderProjectionMap.put(LiveFolders._ID, Notes._ID + " AS " + 17 LiveFolders._ID); 18 sLiveFolderProjectionMap.put(LiveFolders.NAME, Notes.TITLE + " AS " + 19 LiveFolders.NAME); 20 // Add more columns here for more robust Live Folders. 21 }
三,一个SQLiteOpenHelper
1 /** 2 * This class helps open, create, and upgrade the database file. 3 */ 4 private static class DatabaseHelper extends SQLiteOpenHelper { 5 6 DatabaseHelper(Context context) { 7 super(context, DATABASE_NAME, null, DATABASE_VERSION); 8 } 9 10 @Override 11 public void onCreate(SQLiteDatabase db) { 12 db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME + " (" 13 + Notes._ID + " INTEGER PRIMARY KEY," 14 + Notes.TITLE + " TEXT," 15 + Notes.NOTE + " TEXT," 16 + Notes.CREATED_DATE + " INTEGER," 17 + Notes.MODIFIED_DATE + " INTEGER" 18 + ");"); 19 } 20 21 @Override 22 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 23 Log.w(TAG, "Upgrading database from version " + oldVersion + " to " 24 + newVersion + ", which will destroy all old data"); 25 db.execSQL("DROP TABLE IF EXISTS notes"); 26 onCreate(db); 27 } 28 } 29 30 private DatabaseHelper mOpenHelper;这里实现了一个继承于SQLiteOpenHelper的DatabaseHelper内部类。这个类被封装在这里是因为这个ContentProvider本身是和数据库进行打交道的,所以需要一个SQLiteOpenHelper来处理相关关系。具体该类怎么用自己Google吧。
1 @Override 2 public boolean onCreate() { 3 mOpenHelper = new DatabaseHelper(getContext()); 4 return true; 5 }该方法用于对ContentProvider中的资源进行初始化,可以知道的是,这些处理是不可能在static语句块中实现的,因为会有一些要求,例如这里我们要传入一个Context实例给DataBaseHelper构造器,这在static语句块中是实现不了的。
1 @Override 2 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 3 String sortOrder) { 4 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 5 qb.setTables(NOTES_TABLE_NAME); 6 7 switch (sUriMatcher.match(uri)) { 8 case NOTES: 9 qb.setProjectionMap(sNotesProjectionMap); 10 break; 11 12 case NOTE_ID: 13 qb.setProjectionMap(sNotesProjectionMap); 14 qb.appendWhere(Notes._ID + "=" + uri.getPathSegments().get(1)); 15 break; 16 17 case LIVE_FOLDER_NOTES: 18 qb.setProjectionMap(sLiveFolderProjectionMap); 19 break; 20 21 default: 22 throw new IllegalArgumentException("Unknown URI " + uri); 23 } 24 25 // If no sort order is specified use the default 26 String orderBy; 27 if (TextUtils.isEmpty(sortOrder)) { 28 orderBy = NotePad.Notes.DEFAULT_SORT_ORDER; 29 } else { 30 orderBy = sortOrder; 31 } 32 33 // Get the database and run the query 34 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 35 Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); 36 37 // Tell the cursor what uri to watch, so it knows when its source data changes 38 c.setNotificationUri(getContext().getContentResolver(), uri); 39 return c; 40 }
1 @Override 2 public String getType(Uri uri) { 3 switch (sUriMatcher.match(uri)) { 4 case NOTES: 5 case LIVE_FOLDER_NOTES: 6 return Notes.CONTENT_TYPE; 7 8 case NOTE_ID: 9 return Notes.CONTENT_ITEM_TYPE; 10 11 default: 12 throw new IllegalArgumentException("Unknown URI " + uri); 13 } 14 }
关于getType使用提示:
<intent-filter android:label="@string/resolve_edit">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<action android:name="com.android.notepad.action.EDIT_NOTE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
我們很容易看出action和category是很容易匹配的,而我們傳的Uri的數據怎麼匹配呢,這時系統就會去調用你定義的ContentProvider中的getType,取得相關的返回值來和上面的data串進行匹配,當然getType的返回結果你是需要自己去定義的。
但在程序中你也可以自己知道data的類型,就直接匹配了:intent.setType(type);
1 @Override 2 public Uri insert(Uri uri, ContentValues initialValues) { 3 // Validate the requested uri 4 if (sUriMatcher.match(uri) != NOTES) { 5 throw new IllegalArgumentException("Unknown URI " + uri); 6 } 7 8 ContentValues values; 9 if (initialValues != null) { 10 values = new ContentValues(initialValues); 11 } else { 12 values = new ContentValues(); 13 } 14 15 Long now = Long.valueOf(System.currentTimeMillis()); 16 17 // Make sure that the fields are all set 18 if (values.containsKey(NotePad.Notes.CREATED_DATE) == false) { 19 values.put(NotePad.Notes.CREATED_DATE, now); 20 } 21 22 if (values.containsKey(NotePad.Notes.MODIFIED_DATE) == false) { 23 values.put(NotePad.Notes.MODIFIED_DATE, now); 24 } 25 26 if (values.containsKey(NotePad.Notes.TITLE) == false) { 27 Resources r = Resources.getSystem(); 28 values.put(NotePad.Notes.TITLE, r.getString(android.R.string.untitled)); 29 } 30 31 if (values.containsKey(NotePad.Notes.NOTE) == false) { 32 values.put(NotePad.Notes.NOTE, ""); 33 } 34 35 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 36 long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values); 37 if (rowId > 0) { 38 Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI, rowId); 39 getContext().getContentResolver().notifyChange(noteUri, null); 40 return noteUri; 41 } 42 43 throw new SQLException("Failed to insert row into " + uri); 44 }
1 @Override 2 public int delete(Uri uri, String where, String[] whereArgs) { 3 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 4 int count; 5 switch (sUriMatcher.match(uri)) { 6 case NOTES: 7 count = db.delete(NOTES_TABLE_NAME, where, whereArgs); 8 break; 9 10 case NOTE_ID: 11 String noteId = uri.getPathSegments().get(1); 12 count = db.delete(NOTES_TABLE_NAME, Notes._ID + "=" + noteId 13 + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); 14 break; 15 16 default: 17 throw new IllegalArgumentException("Unknown URI " + uri); 18 } 19 20 getContext().getContentResolver().notifyChange(uri, null); 21 return count; 22 } 23 24 @Override 25 public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { 26 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 27 int count; 28 switch (sUriMatcher.match(uri)) { 29 case NOTES: 30 count = db.update(NOTES_TABLE_NAME, values, where, whereArgs); 31 break; 32 33 case NOTE_ID: 34 String noteId = uri.getPathSegments().get(1); 35 count = db.update(NOTES_TABLE_NAME, values, Notes._ID + "=" + noteId 36 + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); 37 break; 38 39 default: 40 throw new IllegalArgumentException("Unknown URI " + uri); 41 } 42 43 getContext().getContentResolver().notifyChange(uri, null); 44 return count; 45 } 46 }
四,小结
被管理的Cursor 会在 Activity进入暂停状态的时候调用自己的 deactivate 方法自行卸载,而在Activity回到运行状态时会调用自己的requery 方法重新查询生成的Cursor对象。如果一个未被管理的Cursor对象想被Activity管理,可以调用Activity的 startManagingCursor方法来实现。
What is URI?
分为A,B,C,D 4个部分:
A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://"
B:URI的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它 必须是一个完整的、小写的 类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包. 类的名称 ;"content://com.android.calendar" (系统日历的URI)
C:路径,URI下的某一个Item,就像网站一样,主网页下包含很多小网页。这里通俗的讲就是你要操作的数据库中表的名 字,或者你也可以自己定义,记得在使用的时候保持一致就ok了;"content://com.android.calendar/calendars"
D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; "content://com.android.calendar/calendars/#" #表示数据id(#代表任意数字)
"content://com.android.calendar/calendars/*" *来匹配任意文本
ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分
parseId(uri)方法用于从路径中获取ID部分
Query Attribute,SQL UNION
一是:按名字查询;二是:多表查询。
我的理解:其实ContentProvider的机制很随意,它就类似于一个服务器一样,你把uri传来,只要按照特定的方式,它就能给你特定的功能,我觉得这个机制自由又方便。
其实这两个功能主要通过修改query就可以,完整的ContentProvider代码如下:
package com.ianc.lilyprovider; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.util.Log; public class LilyProvider extends ContentProvider {final static String TABLE_NAME = "customer";final static String PRODUCT_TABLE = "shoplist";private static final String DATABASE_NAME = "lily.db";private static String AUTHORITY = "com.ianc.lilyprovider";private static final int DATABASE_VERSION = 1;DatabaseHelper mDbHelper;static UriMatcher sUriMatcher;private static final int USER = 1;private static final int USER_NAME = 2;private static final int MULITABLE = 3;private static final int SHOPLIST = 4;static{sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);sUriMatcher.addURI(AUTHORITY, TABLE_NAME, USER);sUriMatcher.addURI(AUTHORITY, PRODUCT_TABLE, SHOPLIST);sUriMatcher.addURI(AUTHORITY, TABLE_NAME+"/"+LilyUser.UserColumns.NAME+"/*", USER_NAME);//search special user by namesUriMatcher.addURI(AUTHORITY, TABLE_NAME+"/*", MULITABLE);}class DatabaseHelper extends SQLiteOpenHelper {public DatabaseHelper(Context context) {super(context, DATABASE_NAME, null, DATABASE_VERSION);}@Overridepublic void onCreate(SQLiteDatabase db) {Log.i("lily","LilyProvider-->DatabaseHelper-->onCreate");db.execSQL("Create table " + TABLE_NAME + "( " +LilyUser.UserColumns._ID+ " INTEGER PRIMARY KEY AUTOINCREMENT, " +LilyUser.UserColumns._COUNT + " INTEGER,"+LilyUser.UserColumns.NAME + " TEXT," +LilyUser.UserColumns.PASSWORD +" TEXT" +");");db.execSQL("Create table " + PRODUCT_TABLE + "( " +LilyUser.ShopListColumns._ID+ " INTEGER PRIMARY KEY AUTOINCREMENT, " +LilyUser.ShopListColumns._COUNT + " INTEGER,"+LilyUser.ShopListColumns.USER_ID+" INTEGER,"+LilyUser.ShopListColumns.PRODUCT + " TEXT," +LilyUser.ShopListColumns.DATE +" TEXT" +");");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {Log.i("lily","LilyProvider-->DatabaseHelper-->onUpgrade");db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME+";");db.execSQL("DROP TABLE IF EXISTS "+PRODUCT_TABLE+";");onCreate(db);}}@Overridepublic int delete(Uri arg0, String arg1, String[] arg2) {Log.i("lily","LilyProvider-->delete");SQLiteDatabase db = mDbHelper.getWritableDatabase();int rownum = db.delete(TABLE_NAME, arg1, arg2);return rownum;}@Overridepublic String getType(Uri uri) {Log.i("lily","LilyProvider-->getType");return null;}@Overridepublic Uri insert(Uri uri, ContentValues values) {Log.i("lily","LilyProvider-->insert");if (sUriMatcher.match(uri) == USER){SQLiteDatabase db = mDbHelper.getWritableDatabase();db.insert(TABLE_NAME, null, values);}else if (sUriMatcher.match(uri) == SHOPLIST){SQLiteDatabase db = mDbHelper.getWritableDatabase();db.insert(PRODUCT_TABLE, null, values);}return null;}@Overridepublic boolean onCreate() {Log.i("lily","LilyProvider-->onCreate");mDbHelper = new DatabaseHelper(this.getContext());return false;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {Log.i("lily","LilyProvider-->query");Log.i("lily","uri = "+uri);Log.i("lily","sUriMatcher.match(uri)="+sUriMatcher.match(uri));Cursor c = null;if (sUriMatcher.match(uri) == USER){SQLiteDatabase db = mDbHelper.getReadableDatabase();c = db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);}else if (sUriMatcher.match(uri) == USER_NAME){// format:authority/table/column/value-->0:table 1:column 2:valueString name = uri.getPathSegments().get(2);// get name valueLog.i("lily","query table:"+uri.getPathSegments().get(0)+" column:"+uri.getPathSegments().get(1)+" value:"+uri.getPathSegments().get(2));SQLiteDatabase db = mDbHelper.getReadableDatabase();if (selection != null){if (selection.length()>0){selection += "AND "+LilyUser.UserColumns.NAME+" like "+name;}}c = db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);}else if (sUriMatcher.match(uri) == MULITABLE){// format:authority/table1/table2String table1 = uri.getPathSegments().get(0);String table2 = uri.getPathSegments().get(1);Log.i("lily","table1 = "+table1);Log.i("lily","table2 = "+table2);if (table1.equalsIgnoreCase(TABLE_NAME)){if (table2.equals(PRODUCT_TABLE)){SQLiteDatabase db = mDbHelper.getReadableDatabase();if (selection != null){if (selection.length()>0){selection += "AND "+LilyUser.UserColumns._ID+" = "+LilyUser.ShopListColumns.USER_ID;}else{selection = LilyUser.UserColumns._ID+" = "+LilyUser.ShopListColumns.USER_ID;} }else{selection = TABLE_NAME + "." +LilyUser.UserColumns._ID+" = "+PRODUCT_TABLE + "." + LilyUser.ShopListColumns.USER_ID;}c = db.query(table1+" cross join "+table2, projection, null, selectionArgs, null, null, sortOrder);}}}return c;}@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {Log.i("lily","LilyProvider-->update");SQLiteDatabase db = mDbHelper.getWritableDatabase();int rownum = db.update(TABLE_NAME, values, selection, selectionArgs);return rownum;} }
这个里面定义Uri格式的时候一定要小心,不然出错了死都查不出来。
下面是使用查询的方法:
private void queryNameUri() {Uri uri = Uri.withAppendedPath(LilyUser.User.CONTENT_URI, "name/lily");//content://com.ianc.lilyprovider/customer/name/lilyString[] projection = {LilyUser.UserColumns._ID, LilyUser.UserColumns.NAME, LilyUser.UserColumns.PASSWORD};String selection = null;String [] selectionArgs = null;String sortOrder = null;Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);if (cursor == null){Log.i("lily","cursor == null");return;}if (cursor.moveToFirst()){Log.i("lily","*********************************************");String id;String name;String password;do{id = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns._ID));Log.i("lily","id = "+id);name = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns.NAME));Log.i("lily","name = "+name);password = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns.PASSWORD));Log.i("lily","password = "+password);Log.i("lily","*********************************************");}while(cursor.moveToNext());}else{Log.i("lily","no result");}cursor.close();}
多表查询:
private void queryMultiTable() {String[] projection = {"customer."+LilyUser.UserColumns._ID, "customer."+LilyUser.UserColumns.NAME, "customer."+LilyUser.UserColumns.PASSWORD, "shoplist."+LilyUser.ShopListColumns.USER_ID, "shoplist."+LilyUser.ShopListColumns.PRODUCT, "shoplist."+LilyUser.ShopListColumns.DATE};String selection = null;String [] selectionArgs = null;String sortOrder = null;Uri uri = Uri.withAppendedPath(LilyUser.User.CONTENT_URI, "shoplist");Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);if (cursor == null){Log.i("lily","queryMultiTable()-->cursor == null");return;}if (cursor.moveToFirst()){Log.i("lily","*********************************************");String id;String name;String password;String user_id;String product;String date;do{id = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns._ID));Log.i("lily","id = "+id);name = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns.NAME));Log.i("lily","name = "+name);password = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns.PASSWORD));Log.i("lily","password = "+password);user_id = cursor.getString(cursor.getColumnIndex(LilyUser.ShopListColumns.USER_ID));Log.i("lily","user_id = "+user_id);product = cursor.getString(cursor.getColumnIndex(LilyUser.ShopListColumns.PRODUCT));Log.i("lily","product = " + product);date = cursor.getString(cursor.getColumnIndex(LilyUser.ShopListColumns.DATE));Log.i("lily","date = " + date);Log.i("lily","*********************************************");}while(cursor.moveToNext());}else{Log.i("lily","no result");}cursor.close();}
接下来是LilyUser这个说明类的代码:
package com.ianc.lilyprovider; import android.net.Uri; import android.provider.BaseColumns; public class LilyUser {public static class User{public static final Uri CONTENT_URI = Uri.parse("content://com.ianc.lilyprovider/" + "customer");}public static class UserColumns implements BaseColumns{ public static String NAME = "name";public static String PASSWORD = "password";}public static class ShopList{public static final Uri CONTENT_URI = Uri.parse("content://com.ianc.lilyprovider/" + "shoplist");}public static class ShopListColumns implements BaseColumns{ public static String USER_ID = "user_id";public static String PRODUCT = "product";public static String DATE = "date";} }
转载于:https://www.cnblogs.com/qiengo/archive/2012/05/25/2517657.html
Android About ContentProvider相关推荐
- 009 Android之ContentProvider
文章目录 Android文件权限简述 ContentProvider 内容提供者 ContentResolver URI 什么是URI URI示例 URI和URL ContentProvider实例 ...
- provider android简书,Android 使用ContentProvider在应用间共享数据
Android 使用ContentProvider在应用间共享数据 题图 www.gratisography.com 在开发的过程中,有时会有需求要实现应用程序之间实现数据共享,在Android系统中 ...
- Android之ContentProvider数据存储
一.ContentProvider保存数据介绍 一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProvider是以类似数据库中表的方式将数 ...
- Android之ContentProvider
文章目录 简介 ContentResolver.ContentProvider和URI Uri简介 Uri处理 自定义ContentProvider 1. 新建类继承ContentProvider 2 ...
- android使用ContentProvider初始化sdk,初始化时机
文章目录 系列目录 前言 什么是ContentProvider sdk初始化 常规初始化方案 使用ContentProvider初始化sdk 为什么ContentProvider可以作为sdk初始化 ...
- Android【ContentProvider案例】
ContentProvider实现跨程序数据共享,实例如下: 数据库基于数据存储中的使用的数据库,点击Here 1]ContentProvider类 public class ContentProvi ...
- android中contentProvider及ContentResolver
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程 这个技术是解决应用之间的一个调用,如常见的应用间数据库查询,内容提供者暴露接口,内容解析器Conten ...
- android 53 ContentProvider内容提供者
ContentProvider内容提供者:像是一个中间件一样,一个媒介一样,可以以标准的增删改差操作对手机的文件.数据库进行增删改差.通过ContentProvider查找sd卡的音频文件,可以提供标 ...
- Android 中ContentProvider和Uri详解
一.使用ContentProvider(内容提供者)共享数据 ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给 ...
最新文章
- 【C/C++】计时函数比较
- 【iOS 开发】基本 UI 控件详解 (UIButton | UITextField | UITextView | UISwitch)
- 修改Linux root用户名 后提示network manager启动失败修复
- Windows内存管理机密+揭穿内存优化工具的骗局
- php什么是阵列,php – 功能阵列的替代方法?
- uclinux多线程应用-网络通信[转]
- 运用事理图谱搞事情:新闻预警、事件监测、文本可视化、出行规划与历时事件流生成
- 参数返回Oracle 常用函数:nvl/nullif/case when/wm_concat/replace
- CentOS笔记-系统概述
- C++_数据结构_堆用法详解
- html设置ie9兼容性视图,ie9兼容性视图设置方法
- 锆石FPGA---verlog语法篇
- 六年软件测试感悟 从博彦到VMware
- android模拟器游戏大全,安卓模拟器游戏大全_小鸡模拟器
- 明明白白lua的协程(coroutine)
- 在OpenCV里学习常见问题汇编27
- (二)安全计算-Threat Modelling威胁建模
- google/uuid
- 变电站可视化搭建推陈出新?无人值守却更胜一筹
- iOS开发——使用Charles进行https网络抓包详解
热门文章
- Hadoop HIVE 复合数据类型
- Java Spring AspectJ
- 1.12 改善你的模型的表现
- Numpy Mathematical functions 数学函数
- R语言聚类算法之密度聚类(Density-based Methods)
- C语言中调用可执行程序的方法。
- 微机计算机硬件技术实用教程知识点,微机接口技术实用教程1.ppt
- mysql 设置默认值_为什么 Flink 无法实时写入 MySQL?
- LIST函数JAVA特点_Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和性能分析)...
- Mysql学习总结(85)——开发人员最应该明白的数据库设计原则