android原生的資料庫實現(ContentProvider+SQLiteOpenHelper)
其實網上有很多比較不錯的orm移動開發框架,相對還是比較優秀,可以節省大把的開發時間,但我個人認為,現在這個還不是很穩定,而且,在效率上,我持保守態度,移動開發上,應相對的少用反射和抽象介面,而網路上的一些第三方的框架,卻抓住這個大用特用,個人覺得,不大好,另外,採用android原生的資料庫實現方案,還有個極大的好處是:很容易實現產品生態系統,這個是其他的orm所不易達到的。也即,可以輕而易舉地實現我們的幾個應用間的資料共享和互動,URI訪問即可,從而形成一個生態圈的目的,具體的,就不多說了。
這個demo是一個完整的db操作,但只給出了一個實體的db實現,不過,足以:
程式結構圖:
後面的所有db操作,都會被封裝到DBManager中,也即,以後應用的db操作,只需通過DBManager進行互動介面,具體的看TestDBAct中實現:
1、抽象實體基類 BasicEntity.java:
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
|
package
blockcheng.android.model; import
java.io.Serializable; import
org.json.JSONObject; import
android.content.ContentValues; import
android.database.Cursor; import
android.os.Parcelable; import
android.provider.BaseColumns; public
abstract
class
BasicEntity implements
BaseColumns,Serializable, Parcelable{ private
static
final
long
serialVersionUID = 7169909425485915669L; protected
String entityName; protected
String entityId; protected
long
lastUpdateTime; public
static
final
String sDEFAULTSORT = "
lastUpdateTime desc" ; public
static
final
String sKEY_LASTUPDATETIMEADID = "lastUpdateTime" ; /** *
demo methods *
@param obj *
@return */ public
static
BasicEntity translateJson2Object(JSONObject obj){ //TODO:this
method must be override by subclass. return
null ; } public
String getEntityName() { return
entityName; } public
void
setEntityName(String entityName) { this .entityName
= entityName; } public
String getEntityId() { return
entityId; } public
void
setEntityId(String entityId) { this .entityId
= entityId; } public
long
getLastUpdateTime() { return
lastUpdateTime; } public
void
setLastUpdateTime( long
lastUpdateTime) { this .lastUpdateTime
= lastUpdateTime; } public
abstract
ContentValues getContentValues(); /** *
demo method :should be copy by subclass *
@param c *
@return */ public
static
BasicEntity fromCursor(Cursor c) { return
null ; } @Override public
String toString() { return
"id="
+ entityId + ",
name=" +
entityName + ",
lastUpdateTime="
+ lastUpdateTime + "" ; } } |
2、廣告實體類AdvertisementEntity.java:
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
package
blockcheng.android.model; import
java.io.Serializable; import
org.json.JSONObject; import
android.content.ContentValues; import
android.database.Cursor; import
android.os.Parcel; public
class
AdvertisementEntity extends
BasicEntity implements
Serializable{ private
static
final
long
serialVersionUID = 3045767248752772598L; /** *
廣告圖片路徑 **/ private
String adPic; /** *
廣告型別 1主題 2 單個商品(後可擴充,客戶端可根據廣告型別設定廣告的點選狀態) **/ private
int
adType; /** *
廣告物件地址(主題ID、單個商品ID或者其他) **/ private
String adTarget; //'廣告位置:1.banner(後擴充位置) private
int
adPosition; private
String data; /** *
demo methods *
@param obj *
@return */ public
static
AdvertisementEntity translateJson2Object(JSONObject obj){ //TODO:implement
it later. AdvertisementEntity
aEntity = new
AdvertisementEntity(); return
aEntity; } public
String getAdPic() { return
adPic; } public
void
setAdPic(String adPic) { this .adPic
= adPic; } public
int
getAdType() { return
adType; } public
void
setAdType( int
adType) { this .adType
= adType; } public
String getAdTarget() { return
adTarget; } public
void
setAdTarget(String adTarget) { this .adTarget
= adTarget; } public
int
getAdPosition() { return
adPosition; } public
void
setAdPosition( int
adPosition) { this .adPosition
= adPosition; } public
String getData() { return
data; } public
void
setData(String data) { this .data
= data; } @Override public
int
describeContents() { //
TODO Auto-generated method stub return
0 ; } /******************************************************************/ /*********************Database
config information********************/ /******************************************************************/ public
static
final
String sTABLE_NAME = "AdvertisementEntity" ; public
static
final
String sKEY_ADID = "adId" ; public
static
final
String sKEY_ADNAME = "adName" ; public
static
final
String sKEY_ADTYPE = "adType" ; public
static
final
String sKEY_ADPIC = "adPic" ; public
static
final
String sKEY_ADTARGET = "adTarget" ; /* *
要查詢的全部欄位 */ public
static
String[] ADS_PROJECTION_ALL = new
String[] { "adId" , "adName" , "lastUpdateTime" , "adType" , "adPic" , "adTarget" , }; @Override public
void
writeToParcel(Parcel dest, int
flags) { //
TODO Auto-generated method stub } @Override public
ContentValues getContentValues() { //
TODO Auto-generated method stub final
ContentValues values = new
ContentValues(); values.put(sKEY_ADID,
getEntityId()); values.put(sKEY_ADNAME,
getEntityName()); values.put(sKEY_ADTYPE,
adType); values.put(sKEY_ADPIC,
adPic); values.put(sKEY_ADTARGET,
adTarget); return
values; } public
static
AdvertisementEntity fromCursor(Cursor c) { AdvertisementEntity
aEntity = new
AdvertisementEntity(); aEntity.entityId
= c.getString(c.getColumnIndexOrThrow(sKEY_ADID)); aEntity.entityName
= c.getString(c.getColumnIndexOrThrow(sKEY_ADNAME)); aEntity.adPic
= c.getString(c.getColumnIndexOrThrow(sKEY_ADTARGET)); aEntity.adType
= c.getInt(c.getColumnIndexOrThrow(sKEY_ADTYPE)); aEntity.lastUpdateTime
= c.getLong(c.getColumnIndexOrThrow(BasicEntity.sKEY_LASTUPDATETIMEADID)); return
aEntity; } @Override public
String toString() { return
"AdvertisementEntity ["
+ super .toString()+
"adPic=
" +
adPic + ",
adType="
+ adType +
",
adTarget="
+ adTarget + ",
adPosition="
+ adPosition +
",
data="
+ data + "]"
+ super .toString(); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package
blockcheng.android.service.database; import
android.net.Uri; public
interface
DBConfig { public
static
final
String DATABASE_NAME = "blockchengDB" ; public
static
final
int
DATABASE_VERSION = 1 ; public
static
final
String DB_TAG = "blockcheng" ; public
static
final
String AUTHORITY = "blockcheng.android" ; //
content url public
static
final
Uri CONTENT_URI_AdvertisementEntity = Uri .parse( "content://"
+AUTHORITY+ "/AdvertisementEntity" ); //search
value public
static
final
int
sSEARCH = 10 ; public
static
final
int
sADVERTISEMENT = 100 ; public
static
final
int
sADVERTISEMENT_ID = 101 ; } |
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
|
package
blockcheng.android.service.database; import
blockcheng.android.model.AdvertisementEntity; import
android.content.Context; import
android.database.sqlite.SQLiteDatabase; import
android.database.sqlite.SQLiteOpenHelper; import
android.util.Log; public
class
DBHelper extends
SQLiteOpenHelper implements
DBConfig{ public
DBHelper(Context context) { super (context,
DBConfig.DATABASE_NAME, null ,
DBConfig.DATABASE_VERSION); } @Override public
void
onCreate(SQLiteDatabase db) { //
TODO Auto-generated method stub StringBuffer
createAdSql = new
StringBuffer( "CREATE
TABLE'" ); createAdSql.append(AdvertisementEntity.sTABLE_NAME); createAdSql.append(
"'('adId'
TEXT NOT NULL,"
); createAdSql.append(
"'adName'
TEXT," ); createAdSql.append( "'lastUpdateTime'
real DEFAULT 0," ); createAdSql.append( "'adType'
INTEGER," ); createAdSql.append( "'adPic'
TEXT," ); createAdSql.append(
"'adTarget'
TEXT," ); createAdSql.append(
"PRIMARY
KEY ('adId')"
); createAdSql.append(
");" );; Log.i(DBConfig.DB_TAG,
"createAdSql::" +createAdSql.toString()); db.execSQL(createAdSql.toString()); String
createIndexSql_0 = "CREATE
INDEX 'id' ON '"
+ AdvertisementEntity.sTABLE_NAME
+ "'
('adId' ASC);" ; db.execSQL(createIndexSql_0); } @Override public
void
onUpgrade(SQLiteDatabase db, int
oldVersion, int
newVersion) { //
TODO Auto-generated method stub Log.w(DBConfig.DB_TAG,
"Upgrading
database from version "
+ oldVersion +
"
to "
+ newVersion + ",
which will destroy all old data" ); db.execSQL( "DROP
TABLE IF EXISTS "
+ AdvertisementEntity.sTABLE_NAME); onCreate(db); } } |
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
package
blockcheng.android.service.database; import
java.util.HashMap; import
blockcheng.android.model.AdvertisementEntity; import
blockcheng.android.model.BasicEntity; import
android.app.SearchManager; import
android.content.ContentProvider; import
android.content.ContentUris; import
android.content.ContentValues; import
android.content.UriMatcher; import
android.database.Cursor; import
android.database.SQLException; import
android.database.sqlite.SQLiteDatabase; import
android.database.sqlite.SQLiteOpenHelper; import
android.database.sqlite.SQLiteQueryBuilder; import
android.net.Uri; import
android.text.TextUtils; import
android.util.Log; public
class
DemoContentProvider extends
ContentProvider { private
SQLiteOpenHelper dbHelper; private
static
final
UriMatcher URI_MATCHER; static
{ //TODO
需要整合下面的地址: URI_MATCHER
= new
UriMatcher(UriMatcher.NO_MATCH); URI_MATCHER.addURI(DBConfig.AUTHORITY,
SearchManager.SUGGEST_URI_PATH_QUERY, DBConfig.sSEARCH); URI_MATCHER.addURI(DBConfig.AUTHORITY,
SearchManager.SUGGEST_URI_PATH_QUERY +
"/*" ,
DBConfig.sSEARCH); //advertisement
uri config URI_MATCHER.addURI(DBConfig.AUTHORITY,
AdvertisementEntity.sTABLE_NAME, DBConfig.sADVERTISEMENT); URI_MATCHER.addURI(DBConfig.AUTHORITY,
AdvertisementEntity.sTABLE_NAME+ "/*" ,
DBConfig.sADVERTISEMENT_ID); } //
TODO: need more investigate. private
static
final
HashMap<String, String> SUGGESTION_PROJECTION_MAP; static
{ SUGGESTION_PROJECTION_MAP
= new
HashMap<String, String>(); SUGGESTION_PROJECTION_MAP.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "topicName
"
+ "
AS "
+ SearchManager.SUGGEST_COLUMN_TEXT_1); SUGGESTION_PROJECTION_MAP.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "goodName
"
+ "
AS "
+ SearchManager.SUGGEST_COLUMN_TEXT_2); SUGGESTION_PROJECTION_MAP.put( SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID,
AdvertisementEntity._ID + "
AS " +
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); SUGGESTION_PROJECTION_MAP.put(AdvertisementEntity._ID,
AdvertisementEntity._ID); } @Override public
boolean
onCreate() { //
TODO Auto-generated method stub dbHelper
= new
DBHelper(getContext()); return
true ; } //
TODO: update the manifest accordingly. public
String getType(Uri uri) { switch
(URI_MATCHER.match(uri)) { case
DBConfig.sADVERTISEMENT: case
DBConfig.sADVERTISEMENT_ID: return
"vnd.android.cursor.dir/"
+ DBConfig.AUTHORITY + "." +
AdvertisementEntity.sTABLE_NAME; default : throw
new
IllegalArgumentException( "Unknown
URI "
+ uri); } } @Override public
Uri insert(Uri uri, ContentValues initialValues) { //
TODO Auto-generated method stub Log.i(DBConfig.DB_TAG,
"insert::" +uri.toString()); ContentValues
values; SQLiteDatabase
db = dbHelper.getWritableDatabase(); switch (URI_MATCHER.match(uri)){ case
DBConfig.sADVERTISEMENT: if
(initialValues != null )
{ values
= new
ContentValues(initialValues); values.put(BasicEntity.sKEY_LASTUPDATETIMEADID,
System.currentTimeMillis()); }
else
{ values
= new
ContentValues(); } final
long
rowId = db.insert(AdvertisementEntity.sTABLE_NAME, AdvertisementEntity.sKEY_ADPIC, values); if
(rowId > 0 )
{ Uri
insertUri = ContentUris.withAppendedId(DBConfig.CONTENT_URI_AdvertisementEntity, rowId); getContext().getContentResolver().notifyChange(uri,
null ); return
insertUri; } throw
new
SQLException( "Failed
to insert row into "
+ uri); default : throw
new
IllegalArgumentException( "Unknown
URI "
+ uri); } } @Override public
int
delete(Uri uri, String selection, String[] selectionArgs) { //
TODO Auto-generated method stub Log.i(DBConfig.DB_TAG,
"delete::" +uri.toString()); SQLiteDatabase
db = dbHelper.getWritableDatabase(); int
count; switch
(URI_MATCHER.match(uri)) { case
DBConfig.sADVERTISEMENT: count
= db.delete(AdvertisementEntity.sTABLE_NAME, selection, selectionArgs); break ; case
DBConfig.sADVERTISEMENT_ID: String
adId = uri.getPathSegments().get( 1 ); count
= db.delete(AdvertisementEntity.sTABLE_NAME, AdvertisementEntity.sKEY_ADID +
"=" +
adId +
(!TextUtils.isEmpty(selection) ? "
AND (" +
selection + ')'
: "" ),
selectionArgs); break ; default : throw
new
IllegalArgumentException( "
Unknown URI "
+ uri); } getContext().getContentResolver().notifyChange(uri,
null ); return
count; } @Override public
int
update(Uri uri, ContentValues values, String where, String[]
whereArgs) { Log.i(DBConfig.DB_TAG,
"update::" +uri.toString()
+ ":
value=" +values.toString()); SQLiteDatabase
db = dbHelper.getWritableDatabase(); int
count = - 1 ; switch
(URI_MATCHER.match(uri)) { case
DBConfig.sADVERTISEMENT_ID: Log.i(DBConfig.DB_TAG,
"update::sADVERTISEMENT_ID" ); String
adverstisementId = uri.getPathSegments().get( 1 ); count
= db.update( AdvertisementEntity.sTABLE_NAME, values, AdvertisementEntity.sKEY_ADID +
"='" +
adverstisementId +
"'" +
(!TextUtils.isEmpty(where) ? "
AND ("
+ where +
')'
: "" ),
whereArgs); break ; case
DBConfig.sADVERTISEMENT: Log.i(DBConfig.DB_TAG,
"update::sADVERTISEMENT" ); String
aidString = values.getAsString(AdvertisementEntity.sKEY_ADID); values.remove(AdvertisementEntity.sKEY_ADID); count
= db.update( AdvertisementEntity.sTABLE_NAME, values, AdvertisementEntity.sKEY_ADID +
"='" +
aidString +
"'" +
(!TextUtils.isEmpty(where) ? "
AND ("
+ where +
')'
: "" ),
whereArgs); break ; default : throw
new
IllegalArgumentException( "Unknown
URI "
+ uri); } return
count; } @Override public
Cursor query(Uri uri, String[] projection, String selection, String[]
selectionArgs, String sortOrder) { Log.i(DBConfig.DB_TAG,
"query::" +uri.toString()); SQLiteQueryBuilder
qb = new
SQLiteQueryBuilder(); String
orderBy; switch
(URI_MATCHER.match(uri)) { case
DBConfig.sADVERTISEMENT: qb.setTables(AdvertisementEntity.sTABLE_NAME); break ; case
DBConfig.sADVERTISEMENT_ID: qb.setTables(AdvertisementEntity.sTABLE_NAME); qb.appendWhere(AdvertisementEntity.sKEY_ADID
+ "='" +
uri.getPathSegments().get( 1 )
+ "'" ); break ; default : throw
new
IllegalArgumentException( "Unknown
URI "
+ uri); } //
If no sort order is specified use the default if
(TextUtils.isEmpty(sortOrder)) { orderBy
= BasicEntity.sDEFAULTSORT; }
else
{ orderBy
= sortOrder; } SQLiteDatabase
db = dbHelper.getReadableDatabase(); Cursor
c = qb.query(db, projection, selection, selectionArgs, null , null ,
orderBy); c.setNotificationUri(getContext().getContentResolver(),
uri); return
c; } } |
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
|
<? xml
version = "1.0"
encoding = "utf-8" ?> < LinearLayout
xmlns:android = "http://schemas.android.com/apk/res/android" android:layout_width = "match_parent" android:layout_height = "match_parent" android:orientation = "vertical" android:padding = "15dip"
> < EditText android:id = "@+id/et_condition" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:ems = "10" android:text = "1" > < requestFocus
/> </ EditText > < Button android:id = "@+id/db_button1" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:clickable = "true" android:onClick = "handleEvent" android:text = "add
"
/> < Button android:id = "@+id/db_button2" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:clickable = "true" android:onClick = "handleEvent" android:text = "delete"
/> < Button android:id = "@+id/db_button3" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:clickable = "true" android:onClick = "handleEvent" android:text = "update"
/> < Button android:id = "@+id/db_button4" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:clickable = "true" android:onClick = "handleEvent" android:text = "query"
/> </ LinearLayout > |
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
|
package
cn.helloclq.android.activity; import
blockcheng.android.R; import
blockcheng.android.model.AdvertisementEntity; import
blockcheng.android.service.database.DBManager; import
android.app.Activity; import
android.os.Bundle; import
android.view.View; import
android.widget.EditText; public
class
TestDBAct extends
Activity { EditText
etCondition; @Override protected
void
onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.dbtest_activity); etCondition
= (EditText)findViewById(R.id.et_condition); } public
void
handleEvent(View view){ String
adIdString = etCondition.getText().toString(); AdvertisementEntity
aEntity = new
AdvertisementEntity(); aEntity.setEntityId(adIdString); aEntity.setEntityName( "ad" +adIdString); aEntity.setAdType(Integer.parseInt(adIdString)); aEntity.setAdTarget(adIdString); //public
WebTaskTest(Context context, boolean showDialog, //
boolean cancelAble, String dialogLoadingStr, //
WebRequestCallbackInfc cb) switch (view.getId())
{ case
R.id.db_button1: //add
DBManager.getInstance().addAdvertiseMent(getContentResolver(),
aEntity); break ; case
R.id.db_button2: //delete DBManager.getInstance().deleteAdvertiseMentById(getContentResolver(),
adIdString); break ; case
R.id.db_button3: //update aEntity.setEntityName( "ad
update" +adIdString); DBManager.getInstance().updateAdervertiseEntity(getContentResolver(),
aEntity); break ; case
R.id.db_button4: //query DBManager.getInstance().querAllAdvertisementEntity(getContentResolver()); break ; } } } |
執行日誌:
1
2
3
4
5
6
7
8
9
10
|
08-07
10:52:50.305: I /blockcheng (458):
query::content: //blockcheng .android /AdvertisementEntity/1 08-07
10:52:50.415: I /blockcheng (458):
createAdSql::CREATE TABLE 'AdvertisementEntity' ( 'adId'
TEXT NOT NULL, 'adName'
TEXT, 'lastUpdateTime'
real DEFAULT 0, 'adType'
INTEGER, 'adPic'
TEXT, 'adTarget'
TEXT,PRIMARY KEY ( 'adId' )); 08-07
10:52:50.434: I /blockcheng (458):
insert::content: //blockcheng .android /AdvertisementEntity 08-07
10:52:50.455: I /blockcheng (458):
addAdvertiseMent::AdvertisementEntity [ id =1,
name=ad1, lastUpdateTime=0adPic= null, adType=1, adTarget=1, adPosition=0, data=null] id =1,
name=ad1, lastUpdateTime=0 08-07
10:53:08.567: I /blockcheng (458):
query::content: //blockcheng .android /AdvertisementEntity 08-07
10:53:08.575: I /blockcheng (458):
ad:AdvertisementEntity [ id =1,
name=ad1, lastUpdateTime=1375843970443adPic= 1, adType=1, adTarget=null, adPosition=0, data=null] id =1,
name=ad1, lastUpdateTime=1375843970443 08-07
10:53:15.585: I /blockcheng (458):
update::content: //blockcheng .android /AdvertisementEntity :
value=adTarget=1 adType=1 adPic=null adId=1 lastUpdateTime=1375843995585 adName=ad update1 08-07
10:53:15.585: I /blockcheng (458):
update::sADVERTISEMENT 08-07
10:53:17.355: I /blockcheng (458):
delete::content: //blockcheng .android /AdvertisementEntity/1 08-07
10:53:18.835: I /blockcheng (458):
query::content: //blockcheng .android /AdvertisementEntity |
麻雀雖小,五臟俱全,加多張表的話,也是如此的,就這樣吧,都寫了好一會兒了。
補充manifest檔案:
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
|
<? xml
version = "1.0"
encoding = "utf-8" ?> < manifest
xmlns:android = "http://schemas.android.com/apk/res/android" package = "blockcheng.android" android:versionCode = "1" android:versionName = "1.0"
> < uses-sdk android:minSdkVersion = "4" android:targetSdkVersion = "17"
/> < uses-permission
android:name = "android.permission.RECEIVE_USER_PRESENT"
/> < uses-permission
android:name = "android.permission.INTERNET"
/> < uses-permission
android:name = "android.permission.WRITE_EXTERNAL_STORAGE"
/> < uses-permission
android:name = "android.permission.READ_EXTERNAL_STORAGE"
/> < uses-permission
android:name = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
/> < uses-permission
android:name = "android.permission.ACCESS_NETWORK_STATE"
/> < uses-permission
android:name = "android.permission.SYSTEM_ALERT_WINDOW" />
< uses-permission
android:name = "android.permission.READ_PHONE_STATE" /> < uses-permission
android:name = "android.permission.READ_LOGS" ></ uses-permission > < application android:allowBackup = "true" android:label = "@string/app_name" > < activity android:name = "cn.helloclq.android.activity.TestAct" android:label = "@string/app_name" android:configChanges = "mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation" android:screenOrientation = "portrait"
> </ activity > < activity android:name = "cn.helloclq.android.activity.TestDBAct" android:label = "@string/app_name" android:configChanges = "mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation" android:screenOrientation = "portrait"
> < intent-filter > < action
android:name = "android.intent.action.MAIN"
/> < category
android:name = "android.intent.category.LAUNCHER"
/> </ intent-filter > </ activity > < provider android:exported = "false" android:authorities = "blockcheng.android" android:name = "blockcheng.android.service.database.DemoContentProvider"
/> </ application > </ manifest > |
相關文章
- Android 原生 SQLite 資料庫的一次封裝實踐AndroidSQLite資料庫封裝
- SphereEx 正式步入雲原生時代:Database Mesh 2.0 實現雲原生資料庫理念革新Database資料庫
- 平安雲原生資料庫開發與實踐資料庫
- Android 中的升級資料庫最佳方法實踐Android資料庫
- 實現MySQL資料庫的實時備份MySql資料庫
- 通過原生js實現資料的雙向繫結JS
- Redis中單機資料庫的實現Redis資料庫
- Redis 資料庫、鍵過期的實現Redis資料庫
- 【JDBC的實現步驟……MySQL資料庫】JDBCMySql資料庫
- SnappyDB—Android上的NoSQL資料庫APPAndroidSQL資料庫
- 資料庫的未來:雲原生+分散式資料庫分散式
- Android資料庫檢視庫---Android-Debug-DatabaseAndroid資料庫Database
- Redis多機資料庫實現Redis資料庫
- 資料庫連線池實現資料庫
- 雲原生資料庫夯實企業數字新基建資料庫
- Redis 設計與實現 (五)--多機資料庫的實現Redis資料庫
- Android 連線資料庫Android資料庫
- 今日談:BoltDB資料庫,一款純Go實現的KV資料庫資料庫Go
- CAS配置資料庫,實現資料庫使用者認證資料庫
- 利用 Android 系統原生 API 實現分享功能(2)AndroidAPI
- 資料庫連線池的實現及原理資料庫
- 資料庫分散式事務的實現原理!資料庫分散式
- django中的資料庫連線池實現Django資料庫
- 基於json資料格式實現的簡單資料庫——jsonDBJSON資料庫
- Android除錯資料庫的福音:Android-Debug-DatabaseAndroid除錯資料庫Database
- Android 資料庫綜述(一) 資料庫片的升級與資料的遷移操作Android資料庫
- golang實現mysql資料庫備份GolangMySql資料庫
- django使用多個資料庫實現Django資料庫
- 資料庫實現原理#4(Hash Join)資料庫
- Mybatis實現分包定義資料庫MyBatis資料庫
- Redis 設計與實現:資料庫Redis資料庫
- 原生分散式資料庫與子資料庫子表中介軟體的區別分散式資料庫
- laravel實現100w大量資料插入資料庫Laravel資料庫
- Android 中使用 SQLite 資料庫AndroidSQLite資料庫
- Android連線資料庫sqlserverAndroid資料庫SQLServer
- AI原生資料庫Infinity正式開源AI資料庫
- 到底什麼是雲原生資料庫?資料庫
- 華為雲GaussDB NoSQL雲原生多模資料庫的超融合實踐SQL資料庫
- 當雲原生閘道器遇上圖資料庫,NebulaGraph 的 APISIX 最佳實踐資料庫API