第十一章 应用程序找一个朋友.docx
《第十一章 应用程序找一个朋友.docx》由会员分享,可在线阅读,更多相关《第十一章 应用程序找一个朋友.docx(44页珍藏版)》请在冰豆网上搜索。
![第十一章 应用程序找一个朋友.docx](https://file1.bdocx.com/fileroot1/2023-2/4/ca9c81f9-1424-4ea9-8c25-5ec80ab93c32/ca9c81f9-1424-4ea9-8c25-5ec80ab93c321.gif)
第十一章应用程序找一个朋友
应用程序:
找一个朋友第十一章
(1)
关键技能&概念
●创建一个SQLite数据库
●创建一个定制内容提供者
●从数据库检索条目并且传递到一个GoogleMapsOverlay
这是你将创建应用程序的最后一章,但是会是本书介绍的最大的一个应用程序。
我将介绍一些到目前为止没有遇到过的话题,而且你会用到这些话题所谈到的技能创建一个非常健全的应用程序。
在本章中,会学习如何在Android模拟器中创建SQLite数据库。
我会向你展示如何在定制的数据库中读取,写入并且输出数据。
这个过程包括创建并使用你自己的ContentProvider来和数据库一同工作。
然后,你拿取存储在数据库中的数据并写入到GoogleMapsOverlay中。
当你在前一章用Google地图时,还没有使用Overlay。
使用GoogleMapsOverlays在地图上绘制形状并且写文本,得到一个有信息量的地图。
在这个项目中,将创建一个两部分应用程序。
应用程序的第一部分将允许用户输入“friends”到移动数据库中。
(一个friend由姓名和地理坐标位置组成)。
用户将能增加,修改并且删除friends.
第二个部分将包括一个菜单项目。
当用户选择这个菜单项目,应用程序将显示一个Google地图。
这个Google地图和第九章创建的Google地图不同之处就是,这个地图会包含一个GoogleMapsOverlay,它会允许你在Google地图的标题处写入姓名,给定信息并且绘制项目。
要开始,在Eclipse内创建一个新的Android项目,命名为FindFriend,使用下图的设定(略)。
现在,你应该对创建一个Android应用程序非常的熟悉了,创建本项目会需要一点小小的帮助。
Google在AndroidSDK里有一个应用程序叫NotePad,非常简单但是允许你储存,修改并且删除数据库里的“notes”。
你会去修改这个例子的一些代码来创建Friends数据库。
如果你想要知道GoogleNotePad如何工作,在继续之前,在Eclipse中装载项目并且在模拟器中运行它。
不久将会开始修改这个代码,但是首先,在下一节里,将创建你的第一个SQLite数据库。
创建一个SQLite数据库
创建一个SQLite数据库第十一章
(2)
Android设备将发布时会有一个内部的SQLite数据库。
这个数据库的目的是允许用户和开发者一个可以在活动中储存信息的地方。
如果你用过MicrosoftSQL服务器或者SQLite,使用Andorid的SQLite数据库的结构和过程对你将不会陌生。
不管你有多少的经验,这个部分将涵盖所有需要创建和使用全功能SQlite数据库的技能。
你将要在Android模拟器上创建一个数据库。
要实现这个,你需要进入AndroidSDK命令行编辑器工具并使用shell命令来进入Android服务器。
提示
参考第三章来重拾你的记忆关于路径声明和使用命令行工具。
一旦你进入服务器,你需要导航到数据库的位置。
所有的AndroidSQLite数据库的位置是在data/data//
databases目录。
使用cd命令来从当前的目录改变到data目录,并且再到目录。
如果你不确定目录的名称,使用ls来列出文件和当前目录。
改变目录至android_programmers_
guide.FindAFriend,如下所示(略)
警告
如果你没有android_programmers_guide.FindAFriend目录,按照前一部分描述的方式创建你的应用程序并且运行“HelloWorld!
”默认的由项目创建的应用程序,那样会确保你有个正确的目录。
找到android_programmers_guide.FindAFriend目录后,运行Is命令。
这个命令列出特定文件夹内所有的文件和目录。
改命令应当返回空的内容。
因为,此时在该目录内没有文件和文件夹。
假定SQLite数据库必须在本目录下的一个数据库目录内,是时候来创建一个了。
mkdir工具为你创建目录。
因此,运行mkdirdatabases命令。
它将创建保留数据库的目录。
警告
现在,你几乎是在服务器的根目录上。
因此你刚刚创建的目录将被作为根目录进入。
当你运行活动时,可能会出问题,因为每一个活动有一个不同的用户。
出于开发的目的,要解决这个问题,运行chmod777databases来准许每个人都能进入到数据库目录。
将来,你必须对给予每个人的权力到一些敏感的Android条目非常谨慎才行。
只给予特定的用户需要使用特定条目的权力。
已经创建了数据库目录了,可以创建数据库了。
使用cd命令导航到数据库目录。
在数据库目录后,使用sqlite3工具来创建数据库并命名它为friends.db,如下:
#sqlitefriends.db
如果执行命令成功,你应当能看到一个SQLite3版本信息,本例是3.5.0,和一个SQLite3prompt—sqlite>。
这说明数据库已被建立但是是空的。
数据库没有包含表格和数据。
记住,下一步是为活动数据创建一个表格。
你需要创建一个名为friends的表格。
这个表将保留id,name,location,created,和modified字段。
这些字段将为你的项目提供足够的信息。
提示
如果你对SQLite不熟悉,一个SQLite命令必须以分号结束。
如果你想要跨越一个命令这个会有帮助。
没有终止SQLite命令的情况下,按下ENTER键会继续给你一个提示符,…>。
你不能在提示符继续输入命令,除非你使用分号。
一旦分号被使用,SQLite将把连续的命令作为一个完整的命令。
要在数据库内创建friends表格,在sqlite>提示符输入下列命令:
CREATETABLEfriends(_idINTEGERPRIMARYKEY,nameTEXT,locationTEXT,
createdINTEGER,modifiedINTEGER);
如果命令执行成功,将返回到sqlite>提示符,如下图所示(略)。
数据库现在可以被使用了,你可以退出SQLite了。
使用.exit来退出。
然后可以退出shell部分返回到Eclipse.
创建数据库是创建应用程序的第一步。
现在数据库和相应的表格已经被创建,你需要一个方法来存储数据。
受雇Android数据的存储方式是一个ContentProvider。
下面的部分带你走进如何为新数据库创建一个定制ContentProvider并存储数据。
创建一个定制的ContentProvider
创建一个定制的ContentProvider第十一章(3)
Android使用ContentProvider来存储数据。
你在第九章使用过ContentProvider存储并从一个GPS中读取坐标信息。
同样的过程应用于数据库。
有这样一些ContentProviderContactLists,IMs,和Recent
Calls。
总之,没有为你数据库准备的ContentProvider。
Android是非常之灵活并且允许你为定制的数据创建定制的ContentProvider。
在本节,将创建一个和Friends数据工作的ContentProvider。
这是存取friend数据和最终在屏幕上显示它们的关键所在。
下一节,让我们来编辑string.xml文件。
这个文件保留全局遍及活动的字符串内容。
编辑string.xml文件
首先,为项目编辑string.xml文件。
string.xml文件由每个项目创建但是你还没有使用过它。
这个文件保留可以在活动中使用的静态字符串。
通常,你不大可能在写这些字符串之前仔细查看所有的部分。
那就是,当你构造活动时,你通常增加到string.xml的入口。
因为那样会打断本书的流程,所以我预先给你所有string.xml文件的所有内容。
编辑string.xml文件使它看上去像这样:
xmlversion="1.0"encoding="utf-8"?
>
FindAFriend
Delete
AddFriend
FindFriends
Revert
Discard
Editlocation
Editname
CreateFriend
EditFriend
Friends
Location
FriendName:
OK
Error
Errorloadingnote
完成string.xml文件后,需要创建一个.java文件来保留你的代码。
应该命名这个文件为FriendsProvider.java。
还要创建另外一个.java文件来保留数据定义。
命名这个文件为Friends.java,因为它会定义一个Friends数据结构像什么样子并且允许你的ContentProvider正常进入。
(因为provider将会是项目中的一个类,没有必要来构建一个相应的.xml布局文件)。
提示
技术上,对于应用程序,你定制的ContentProvider不需要定居在同一项目或包装内作为代码的剩余部分。
为了简单明了,我在FindAFriend项目里做了一个类。
假如你计划创建一个可能用户多重项目的ContentProvider,在单独的包装中创建它吧。
这样,当你只想要使用ContentProvider时,会允许你呼叫一个包装。
让我们开始Friends.java文件。
你只要为相关的类需要输入两个包装:
import.Uri;
importandroid.provider.BaseColumns;
BaseColumns将被一个从主Friends类中的subclass执行。
命名这个subclass为Friend,因为它代表Friends数据集中的一个friend。
下面的代码显示如何设置类的概要:
publicfinalclassFriends{
publicstaticfinalclassFriendimplementsBaseColumns{
}
}
这个类将保留一下静态变量,它们定义Friends数据库中的每一列,ContentURI,和记录的默认排序。
提示
ContentURI被用于识别将要处理的内容。
这个数值必须唯一。
需要定义的字符串如下:
publicstaticfinalUriCONTENT_URI
=
Uri.parse("content:
//android_programmers_guide.FindAFriend.Friends/friend");
publicstaticfinalStringDEFAULT_SORT_ORDER="modifiedDESC";
publicstaticfinalStringNAME="name";
publicstaticfinalStringLOCATION="location";
publicstaticfinalStringCREATED_DATE="created";
publicstaticfinalStringMODIFIED_DATE="modified";
有了变量的设定以后,Friends类放在一起应该是这样的:
packageandroid_programmers_guide.FindAFriend;
import.Uri;
importandroid.provider.BaseColumns;
publicfinalclassFriends{
publicstaticfinalclassFriendimplementsBaseColumns{
publicstaticfinalUriCONTENT_URI
=
Uri.parse("content:
//android_programmers_guide.FindAFriend.Friends/friend");
publicstaticfinalStringDEFAULT_SORT_ORDER="modifiedDESC";
publicstaticfinalStringNAME="name";
publicstaticfinalStringLOCATION="location";
publicstaticfinalStringCREATED_DATE="created";
publicstaticfinalStringMODIFIED_DATE="modified";
}
}
创建ContentProvider
创建ContentProvider第十一章(4)
使用Eclipse打开将会成为项目ContentProvider的FriendsProvider.java文件。
你将要在活动中使用这个定制的ContentProvider来从Friends数据库中检索数据。
和往常一样。
让我们从导入文件开始。
你需要输入Friends类和一些其它的类:
importandroid_programmers_guide.FindAFriend.Friends;
importandroid.content.*;
importandroid.database.Cursor;
importandroid.database.SQLException;
importandroid.database.sqlite.SQLiteOpenHelper;
importandroid.database.sqlite.SQLiteDatabase;
importandroid.database.sqlite.SQLiteQueryBuilder;
import.Uri;
importandroid.text.TextUtils;
importandroid.util.Log;
importjava.util.HashMap;
如你所见,你输入类的大多数和SQL打交道。
当你用这些包装的时候,我再向你解释。
第一个将要使用的包装是android.content。
要成为一个ContentProvider,需要利用并优先要求的方法,你的FriendsProvider类需要扩展ContentProvider。
看一下下面类的概要,它们包括一些将要使用在Provider的变量定义:
publicclassFriendsProviderextendsContentProvider{
privateSQLiteDatabasemDB;
privatestaticfinalStringTAG="FriendsProvider";
privatestaticfinalStringDATABASE_NAME="friends";
privatestaticfinalintDATABASE_VERSION=2;
privatestaticHashMapFRIENDS_PROJECTION_MAP;
privatestaticfinalintFRIENDS=1;
privatestaticfinalintFRIENDS_ID=2;
privatestaticfinalUriMatcherURL_MATCHER;}
ContentProvider包含一些需要优先的方法,包括onCreate(),query(),insert(),delete(),和update()。
因为这些方法将被活动使用ContentProvider呼叫,你必须优先使用它们来进入Friends数据库。
你将优先onCreate()方法呼叫一个SQLiteOpenHelper。
因此,在能优先ContentProvider的onCreate()之前,你不得不创建一个类扩展SQLiteIpenHelper。
代码块跟从的是一个ContentProvider的子类。
它扩展SQLiteOpenHelper:
privatestaticclassDatabaseHelperextendsSQLiteOpenHelper{
@Override
publicvoidonCreate(SQLiteDatabasedb){
db.execSQL("CREATETABLEfriends(_idINTEGERPRIMARYKEY,"
+"nameTEXT,"+"locationTEXT,"+"createdINTEGER,"
+"modifiedINTEGER"+");");
}
@Override
publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,int
newVersion){
Log.w(TAG,"Upgradingdatabasefromversion"+oldVersion+"to"
+newVersion+",whichwilldestroyallolddata");
db.execSQL("DROPTABLEIFEXISTSfriends");
onCreate(db);
}
}
刚创建的DatabaseHelper类包含两个优先方法:
onCreater()和onUpgrade()。
onCreate()方法被用于当从代码中创建数据库,或者表格定义不存在的示例中。
注意
假定你从adb壳中创建数据库结构,你不会依赖于DatabaseHelper的onCreate()方法来建立你的数据库。
DatabaseHelper类创建后,你现在可以为ContentProvider优先onCreate()方法了:
@Override
publicbooleanonCreate(){
DatabaseHelperdbHelper=newDatabaseHelper();
mDB=dbHelper.openDatabase(getContext(),DATABASE_NAME,null,
DATABASE_VERSION);
return(mDB==null)?
false:
true;
}
这是个非常之简单的方法,结果就是返回一个布尔值代表你的数据库是否可以被打开。
你使用在兄弟类中创建的SQLiteOpenHelper来打开Friends数据库。
注意你传递数据库名称到DatabaseHelper类。
如果数据库对象——mDB返回的不是null,然后数据库就成功的被打开并可以查询了。
下一步,优先ContentProvider类的query()方法。
这将是ContentProvider的主要部分。
query()方法是从活动通过ContentProvider被呼叫来从数据库中获得记录。
看下优先版本的query()方法代码:
@Override
publicCursorquery(Uriurl,String[]projection,Stringselection,
String[]selectionArgs,Stringsort){
SQLiteQueryBuilderqb=newSQLiteQueryBuilder();
switch(URL_MATCHER.match(url)){
caseFRIENDS:
qb.setTables("friends");
qb.setProjectionMap(FRIENDS_PROJECTION_MAP);
break;
caseFRIENDS_ID:
qb.setTables("friends");
qb.appendWhere("_id="+url.getPathSegments().get
(1));
break;
default:
thrownewIllegalArgumentException("UnknownURL"+url);
}
StringorderBy;
if(TextUtils.isEmpty(sort)){
orderBy=Friends.Friend.DEFAULT_SORT_ORDER;
}else{
orderBy=sort;
}
Cursorc=qb.query(mDB,projection,selection,selectionArgs,null,
null,orderBy);
c.setNotificationUri(getContext().getContentResolver(),url);
returnc;
}
query()方法做了一点家务管理之类的事宜,通过检查传递到其中的数据库URL的有效性和定义一个查询分类序列达到的。
URL检查是为了确保你只是要进入Friends数据库。
如果你试图从其它活动进入数据库,或者从其它的ContentProvider,query()方法投递一个例外。
到方法的结尾,你使用SQLiteQueryBuilder来执行一个查询。
通过下面的代码,导致的数据集被赋值到一个光标:
Cursorc=qb.query(mDB,projection,selection,selectionArgs,null,
null,or