第8章:android的存储方式(完)

Size: px
Start display at page:

Download "第8章:android的存储方式(完)"

Transcription

1 第 8 章 Android 的存储方式 学习目标 : Android 在存储方面的系统知识 ; 各类存储的使用及步骤 ; SQLite 方式的存储实现 ; contentprovider 方式的存储实现 8.1 存储概述 典型的桌面操作系统提供一种公共文件系统 任何应用软件可以使用它来存储和读取文件, 该文件也可以被其他的应用软件所读取 ( 会有一些权限控制设定 ) Android 采用了一种不同的系统, 在 Android 中, 所有的应用软件数据 ( 包括文件 ) 为该应用软件所私有 然而,Android 同样也提供了一种标准方式供应用软件将私有数据开放给其他应用软件 这一章节描述一个应用软件存储和获取数据 开放数据给其他应用软件 从其他应用软件请求数据并且开放它们的多种方式 在 Android 中, 可供选择的存储方式有 SharedPreferences 文件存储 SQLite 数据库方式 内容提供器 (content provider) 和网络, 我们将在本章详细介绍 8.2 SharedPreferences 存储 首先介绍的是 SharedPreferences, 其是 Android 提供用来存储一些简单的配置信息的一种机制, 例如, 一些默认欢迎语 登录的用户名和密码等 其以键值对的方式存储, 使得我们可以很方便的读取和存入, 下面看一个演示的例子 1. 第一步 在 Eclipse 中打开 ex_sharedpreferences 项目, 其步骤如下所示 (1) 新建一个项目 依次单击 File New > Android Project (2) 在新建项目的对话框中, 选择 Create project from existing source (3) 单击浏览, 找到 ex_sharedpreferences 项目, 然后单击确定 其程序的目录结构如图 所示 :

2 图 程序目录结构 2. 第二步单击运行项目, 可以看到主界面如图 所示, 这个界面的布局信息都在 main.xml 文件当中, 在一个 LinearLayout 当中放了 3 个 TextView 和两个 EditView, 代码如下所示 : <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=" Android:orientation="vertical" Android:layout_width="fill_parent" Android:layout_height="fill_parent" > <TextView Android:layout_width="fill_parent" Android:layout_height="wrap_content" Android:text="SharedPreferences demo" /> <TextView Android:layout_width="fill_parent" Android:layout_height="wrap_content" Android:text="Name:" /> <EditText Android:id="@+id/name" Android:layout_width="fill_parent" Android:layout_height="wrap_content" Android:text="" /> <TextView Android:layout_width="fill_parent" Android:layout_height="wrap_content" Android:text="Password:" /> <EditText Android:id="@+id/password" Android:layout_width="fill_parent" Android:layout_height="wrap_content" Android:password="true" Android:text="" /> </LinearLayout> 如上代码表示 : 使用 LinearLayout 布局, 其中放置三个用来做界面提示的文本框 (TextView) 组件和两个用于输入 Name 和 Password( 注意这里使用了 Android:password="true") 的编辑框 (EditText) 组件, 运行这个应用, 可以看到其界面如图 8-1 所示

3 图 主界面 在图 8-1 中, 我们可以看到, 初始状态下两个 EditView 都是空的, 现在输入一些字符, 如图 所示 图 输入 Name 和 Password 如图 8-2 所示, 我们在 Name 文本框中中输入 IceskYsl, 在 Password 文本框中输入 Password, 然后退出这个应用 我们在应用程序列表中找到这个应用, 重新启动, 可以看到其使用了前面输入的 Name 和 Password, 如图 所示

4 图 重新启动应用 由此可见, 应用保存了我们输入的 Name 和 Password, 现在来看看其实现的代码, 在 DBSharedPreferences.java 文件中, 此文件的代码如下所示 : package us.imnet.iceskysl.db; import Android.app.Activity; import Android.content.SharedPreferences; import Android.os.Bundle; import Android.widget.EditText; public class DBSharedPreferences extends Activity { public static final String SETTING_INFOS = "SETTING_Infos"; public static final String NAME = "NAME"; public static final String PASSWORD = "PASSWORD"; private EditText field_name; // 接收用户名的组件 private EditText filed_pass; // 接收密码的组件 /** Called when the activity is first created. public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); //Find VIew field_name = (EditText) findviewbyid(r.id.name); // 首先获取用来输入用户名的组件 filed_pass = (EditText) findviewbyid(r.id.password); // 同时也需要获取输入密码的组件 // Restore preferences SharedPreferences settings = getsharedpreferences(setting_infos, 0); // 获取一个 SharedPreferences 对象 String name = settings.getstring(name, ""); // 取出保存的 NAME String password = settings.getstring(password, ""); // 取出保存的 PASSWORD //Set value field_name.settext(name); // 将取出来的用户名赋给 field_name filed_pass.settext(password); // 将取出来的密码赋给 protected void onstop(){ super.onstop(); SharedPreferences settings = getsharedpreferences(setting_infos, 0); // 首先获取一个 SharedPreferences 对象 settings.edit().putstring(name, field_name.gettext().tostring()).putstring(password, filed_pass.gettext().tostring()).commit(); // 将用户名和密码保存进去

5 通过上述代码可以看到, 在 oncreate 中使用 findviewbyid 得到两个 EditView 后, 使用 getsharedpreferences 取得 SharedPreferences 对象 settings, 然后使用 getstring 取得其中保存的值, 最后使用 settext 将其值设置为两个 EditText 的值 而在程序运行 onstop 过程, 也就是在程序退出时, 首先使用 getsharedpreferences 得到 settings, 然后调用 edit() 方法使其处于可以编辑状态, 并使用 putstring 将两个 EditText 中的值保存起来, 最后使用 commit() 方法提交即可保存 小知识 : SharedPreferences 保存到哪里去了? SharedPreferences 是以 XML 的格式以文件的方式自动保存的, 在 DDMS 中的 File Explorer 中展开到 /data/data/<package name>/shared_prefs 下, 以上面这个为例, 可以看到一个叫做 SETTING_Infos.xml 的文件, 如图 所示 图 SharedPreferences 文件 将其导出到设备中, 可以打开这个文件, 看到其代码内容为 : <?xml version='1.0' encoding='utf-8' standalone='yes'?> <map> <string name="password">password</string> <string name="name">iceskysl</string> </map> 小知识 : 我们可以通过 getxxx 函数, 从 SharedPreferences 中读取不同类型的内容, 例如, 上面我们使用 [ getstring ] 读取 String 类型的内容 注意 : Preferences 只能在同一个包内使用, 不能在不同的包之间使用 8.3 文件存储 前面介绍的 Shared Preferences 存储方式非常方便, 但是其只适合存储比较简单的数据, 如果需要存储更多的数据, 可行选择的方式有好几种, 这里先给读者介绍文件存储的方法 和传统的 Java 中实现 I/O 的程序类似, 在 Android 中, 其提供了 openfileinput 和 openfileouput 方法读取设备上的文件, 下面看个例子代码, 具体如下所示 : String FILE_NAME = tempfile.tmp ; // 确定要操作文件的文件名 // Create a new output file stream that s private to this application.

6 FileOutputStream fos = openfileoutput(file_name, Context.MODE_PRIVATE); // 初始化 // Create a new file input stream. FileInputStream fis = openfileinput(file_name); // 创建写入流 上述代码中两个方法只支持读取该应用目录下的文件, 读取非其自身目录下的文件将会抛出异常 需要提醒的是, 如果调用 FileOutputStream 时指定的文件不存在,Android 会自动创建它 另外, 在默认情况下, 写入的时候会覆盖原文件内容, 如果想把新写入的内容附加到原文件内容后, 则可以指定其 mode 为 Context.MODE_APPEND 注意 : 默认情况下, 使用 openfileoutput 方法创建的文件只能被其调用的应用使用, 其他应用无法读取这个文件, 如果需要在不同的应用中共享数据, 可以使用 Content Provider 实现, 关于 Content Provider 我们将在稍后的内容中介绍 小知识 : 资源文件放在哪里? 如果你的应用需要一些额外的资源文件, 例如, 一些用来测试你写的音乐播放器是否可以正常工作的 MP3 文件, 可以将这些文件放在应用程序的 /res/raw/ 下, 如 mydatafile.mp3 那么就可以在你的应用中使用 getresources 获取资源后, 以 openrawresource 方法 ( 不带后缀的资源文件名 ) 打开这个文件, 实现代码如下所示 : Resources myresources = getresources(); InputStream myfile = myresources.openrawresource(r.raw.myfilename); 除了前面介绍的读写文件外,Android 还提供了诸如 deletefile filelist 等方法来操作文件, 不再赘述 8.4 SQLite 存储方式 Android 中对数据库进行操作 在前边的章节当中主要学习关于 Android 在布局和显示方面的知识, 在这一节中将开始学习 Android 应用的另外一个方面 : 数据存储 用户可以将自己的数据存储到文件系统或者数据库当中, 当然最经常的是, 用户将自己的数据存储到 SQLite 数据库当中 SQLite 是 Android 所带的一个标准的数据库, 它支持 SQL 语句, 它是一个轻量级的嵌入式数据库 在这个例子里边, 我们在程序的主界面有一些按钮, 通过这些按钮可以对数据库进行标准的增 删 改 查 通过这个例子我们可以学到 如何新建一个数据库 如何新建数据库里边的数据表 如何删除数据库里边的数据表 如何在数据表中添加新数据 如何删除数据库表中的数据 如何使用 Android 提供的工具 File explore, 来查看和删除模拟器当中的数据库表 如何使用 LogCat 来看程序当中打印的日志 1. 第一步 在 Eclipse 中, 打开 ex08_1_sqlite 项目, 具体步骤如下 新建一个项目 单击 File > New > Android Project 在新建项目的对话框中, 选择 Create project from existing source. 单击浏览, 找到 ex08_1_sqlite 项目, 然后单击确定 程序的目录结构如图 所示 :

7 2. 第二步单击运行项目, 我们可以看到主界面如图 所示, 这个界面的布局信息都在 main.xml 文件中, 在一个 LinearLayout 当中数值排列了 5 个 button 图 主界面 3. 第三步 小知识 : 什么是 SQLiteDatabase? 一个 SQLiteDatabase 的实例代表了一个 SQLite 的数据库, 通过 SQLiteDatabase 实例的一些方法, 我们可以执行 SQL 语句, 对数据库进行增 删 查 改的操作 需要注意的是, 数据库对于一个应用来说是私有的, 并且在一个应用当中, 数据库的名字也是惟一的 小知识 : 什么是 SQLiteOpenHelper? 根据这名字, 我们可以看出这个类是一个辅助类 这个类主要生成一个数据库, 并对数据库的版本进行管理 当在程序当中调用这个类的方法 getwritabledatabase(), 或者 getreadabledatabase() 方法的时候, 如果当时没有数据, 那么 Android 系统就会自动生成一个数据库 SQLiteOpenHelper 是一个抽象类, 我们通常需要继承它, 并且实现里边的 3 个函数, 具体函数如下所示 oncreate(sqlitedatabase): 在数据库第一次生成的时候会调用这个方法, 一般我们在这个方法里边生成数据库表 onupgrade(sqlitedatabase, int, int) : 当数据库需要升级的时候,Android 系统会主动的调用这个方法 一般我们在这个方法里边删除数据表, 并建立新的数据表, 当然是否还需要做其他的操作, 完全取决于应用的需求 onopen(sqlitedatabase): 这是当打开数据库时的回调函数 Google, 一般也不会用到 Android 开发入门与实战 第八章样章

8 我们在 ActivityMain 当中看下边这个内部类 DatabaseHelper 类继承 SQLiteOpenHelper, 具体代码如下所示 : private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, public void oncreate(sqlitedatabase db) { // sql 语句 String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE + " text not null, " + BODY + " text not null " + ");"; Log.i("haiyang:createDB=", sql); // 执行这条 sql 语句 public void onupgrade(sqlitedatabase db, int oldversion, int newversion) { DatabaseHelper 类继承了 SQLiteOpenHelper 类, 并且重写了 oncreate 和 onupgrade 方法 在 oncreate() 方法里边首先我们构造和 sql 语句, 然后调用 db.execsql(sql) 执行 sql 语句 这条 sql 语句为我们生成了一张数据库表 目前我们还不需要升级数据库, 所以我们在 onupgrade() 函数里边没有执行任何操作 4. 第四步我们单击插入两条记录的按钮, 如果数据成功插入到数据库当中的 diary 表中, 那么在界面的 title 区域就会有成功的提示, 如图 所示 图 成功插入两条数据 单击这个按钮后, 程序执行了监听器里的 onclick 方法, 并最终执行了上述程序里的 insertitem 方法, 其具体代码如下所示 : private void insertitem() { SQLiteDatabase db = mopenhelper.getwritabledatabase(); // 首先生成 sql 语句 String sql1 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY + ") values('haiyang', 'Android 的发展真是迅速啊 ');"; String sql2 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY + ") values('icesky', 'Android 的发展真是迅速啊 ');"; try { Log.i("haiyang:sql1=", sql1); Log.i("haiyang:sql2=", sql2); db.execsql(sql1); db.execsql(sql2); settitle(" 插入两条数据成功 "); catch (SQLException e) { settitle(" 插入两条数据失败 "); 代码解释

9 SQLiteDatabase db = mopenhelper.getwritabledatabase() 这条语句负责得到一个可写的 SQLite 数据库, 如果这个数据库还没有建立, 那么 mopenhelper 辅助类负责建立这个数据库 如果数据库已经建立, 那么直接返回一个可写的数据库 sql1 和 sql2 是我们构造的标准的插入 SQL 语句, 如果对 SQL 语句不是很熟悉, 可以参考相关的书记 鉴于本书的重点是在 Android 方面, 所以对 SQL 语句的构建不进行详细的介绍 Log.i() 会将参数内容打印到日志当中, 并且打印级别是 Info 级别, 在使用 LogCat 工具的时候我们会进行详细的介绍 db.execsql(sql1); 对 sql 语句进行执行 小知识 : 对 Android 的打印级别介绍 Android 支持五种打印级别, 分别是 Verbose,Debug Info Warning Error, 当然我们在程序当中一般用到的是 Info 级别, 即将一些自己需要知道的信息打印出来 如图 所示 : 图 Android 中的五种打印级别 注意 : 虽然执行 SQL 语句在 Android 当中并没有强制放在 try catch 语句当中, 但是我们最好这么做 并在 catch 模块中将错误信息打印在日志当中 这样一方面方便调试, 一方面可以加强程序的健壮性 5. 第五步单击查询数据库的按钮, 会在界面的 title 区域显示当前数据表当中数据的条数, 刚才我们插入了两条, 那么现在单击后应该显示为 2 条, 如图 所示 图 显示两条数据 单击这个按钮后, 程序执行了监听器里的 onclick 方法, 并最终执行了上述程序里的 showitems 方法, 具体代码如下所示 : private void showitems() { SQLiteDatabase db = mopenhelper.getreadabledatabase(); String col[] = { TITLE, BODY ; // 进行数据库查询

10 Cursor cur = db.query(table_name, col, null, null, null, null, null); Integer num = cur.getcount(); settitle(integer.tostring(num) + " 条记录 "); SQLiteDatabase db = mopenhelper.getreadabledatabase(); 首先得到一个可写的数据库 Cursor cur = db.query(table_name, col, null, null, null, null, null); 将查询到的数据放到一个 Cursor 当中 这个 Cursor 里边封装了这个数据表 TABLE_NAME 当中的所有条列 query() 方法相当的有用, 在这里我们简单的讲一下 第一个参数是数据库里边表的名字, 比如在我们这个例子, 表的名字就是 TABLE_NAME, 也就是 "diary" 第二个字段是我们想要返回数据包含的列的信息 在这个例子当中我们想要得到的列有 title body 我们把这两个列的名字放到字符串数组里边来 第三个参数为 selection, 相当于 sql 语句的 where 部分, 如果想返回所有的数据, 那么就直接置为 null 第四个参数为 selectionargs 在 selection 部分, 你有可能用到?, 那么在 selectionargs 定义的字符串会代替 selection 中的? 第五个参数为 groupby 定义查询出来的数据是否分组, 如果为 null 则说明不用分组 第六个参数为 having, 相当于 sql 语句当中的 having 部分 第七个参数为 orderby, 来描述我们期望的返回值是否需要排序, 如果设置为 null 则说明不需要排序 Integer num = cur.getcount(); 通过 getcount() 方法, 可以得到 cursor 当中数据的个数 小知识 : 什么是 Cursor? Cursor 在 Android 当中是一个非常有用的接口, 通过 Cursor 我们可以对从数据库查询出来的结果集进行随机的读写访问 6. 第六步单击删除一条数据库的按钮后, 如果成功删除, 我们可以看到在屏幕的标题 (title) 区域有文字提示, 如图 所示 图 成功删除一条记录 现在我们再单击查询数据库按钮, 看数据库里边的记录是不是少了一条 单击查询数据库按钮后, 出现如图 所示的界面 图 还剩一条记录 下面我们来看一下如何删除数据 单击删除一条记录的按钮后, 程序执行了监听器里的 onclick 方法, 并最终执行了上述程序里的 deleteitem 方法, 其代码如下所示 : private void deleteitem() { try { SQLiteDatabase db = mopenhelper.getwritabledatabase(); // 进行删除操作

11 db.delete(table_name, " title = 'haiyang'", null); settitle(" 删除 title 为 haiyang 的一条记录 "); catch (SQLException e) { db.delete(table_name, " title = 'haiyang'", null); 删除了一条 title='haiyang' 的数据 当然如果有很多条数据 title 都为 'haiyang', 那么一并删除 我们对 delete 方法的参数进行以下介绍 : 第一个参数是数据库表名, 在这里是 TABLE_NAME, 也就是 diary 第二个参数, 相当于 sql 语句当中的 where 部分, 也就是描述了删除的条件 如果在第二个参数当中有? 符号, 那么第三个参数中的字符串会依次替换在第二个参数当中出现的? 符号 7. 第七步单击删除数据表, 我们可以删除 diary 这张数据表, 如图 所示 图 删除数据库表 下边我们看在代码部分, 是怎么实现删除的, 具体代码如下所示 : private void droptable() { SQLiteDatabase db = mopenhelper.getwritabledatabase(); String sql = "drop table " + TABLE_NAME; try { // 执行 sql 语句 db.execsql(sql); settitle(" 数据表成功删除 :" + sql); catch (SQLException e) { settitle(" 数据表删除错误 "); 首先我们构造了一个标准的删除数据表的 sql 语句, 然后执行这条语句 db.execsql(sql); 8. 第八步现在单击其他的按钮, 程序运行时有可能会出现异常, 我们单击重新建立数据表按钮, 如图 所示 图 重新建立数据库表 现在我们单击查询数据库, 看里边是否有数据, 如图 所示

12 图 新建的表里边没有数据 下边我们看一下程序是怎么建立一张新表的, 具体实现代码如下所示 : private void CreateTable() { SQLiteDatabase db = mopenhelper.getwritabledatabase(); String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE + " text not null, " + BODY + " text not null " + ");"; Log.i("haiyang:createDB=", sql); try { db.execsql("drop TABLE IF EXISTS diary"); db.execsql(sql); settitle(" 数据表成功重建 "); catch (SQLException e) { settitle(" 数据表重建错误 "); sql 语句为标准的 sql 语句, 负责按要求建立一张新表 db.execsql("drop TABLE IF EXISTS diary"); 如果存在 diary 这张表, 我们需要先删除, 因为在同一个数据库当中不能出现两张同样名字的表 db.execsql(sql); 执行 sql 语句, 新表建立 安全和方便地访问数据库 在上一个例子中, 我们对 Android 系统自带的 SQLite 数据库进行了初步的学习, 了解了一些增 删 改 查的基本工作 在这一节的例子当中, 我们做了一个非常简便的日记本程序, 虽然没有完善, 但是已经是基本可以使用了 在例子当中, 我们不但要对数据库进行增 删 改 查的操作, 而且还要把数据库当中的数据显示在一个 ListView 当中, 通过对 ListView 的操作, 实现对数据的增 删 改 查操作 通过这个例子我们可以学到 如何对 DatabaseHelper 和 SQLiteDatabase 封装, 以便让我们访问数据库更加方便和安全 如何利用 ContentValues 类来代替原始的 sql 语句进行数据库的操作如何使用 SimpleCursorAdapter 类和 ListView 配合进行 ListView 的显示 日记本具体实现步骤如下所示 1. 第一步 在 Eclipse 中打开 ex08_2_sqlite 项目, 具体操作步骤如下 新建一个项目 但击 File > New > Android Project 在新建项目的对话框中, 选择 Create project from existing source. 单击浏览, 找到 ex08_2_sqlite 项目, 然后单击确定 程序的目录结构如图 所示 :

13 图 程序的目录结构 2. 第二步我们首先运行一下建立的程序, 将会出现如图 所示 图 没有任何数据的程序主界面 程序的主 Activity 是 ActivityMain, 它是一个 ListActivity, 和它关联的布局文件是 diary_list.xml 关于 ListActivity 的介绍 请参阅第 7 章关于 ListView 的介绍 3. 第三步 : 在继续操作前, 让我们重点关注一下 DiaryDbAdapter 类, 这个类封装了 DatabaseHelper 和 SQLiteDatabase 类, 使得我们对数据库的操作更加安全和方便 在 DiaryDbAdapter 的类变量里, 主要定义了一下几个变量 数据库 数据表 数据表中列的名字 DatabaseHelper 和 SQLiteDatabase 的实例 Context 实例 DatabaseHelper 类的定义和上一个例子一样, 只不过这个例子里边, 我们在 onupgrade 增加了升级的代码, 具体如下所示 : private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, public void oncreate(sqlitedatabase db) { // 生成数据库

14 public void onupgrade(sqlitedatabase db, int oldversion, int newversion) { db.execsql("drop TABLE IF EXISTS diary"); oncreate(db); 在 DiaryDbAdapter 类里, 我们向外界提供了这么一些方法 : open(), 调用这个方法后, 如果数据库还没有建立, 那么会建立数据库, 如果数据库已经建立了, 那么会返回可写的数据库实例 close(), 调用此方法,DatabaseHelper 会关闭对数据库的访问 creatediary(string title, String body) 通过一个 title 和 body 字段在数据库当中创建一条新的纪录 deletediary(long rowid) 通过记录的 id, 删除数据库中的那条记录 getallnotes() 得到 diary 表中所有的记录, 并且以一个 Cursor 的形式进行返回 getdiary(long rowid) 通过记录的主键 id, 得到特定的一条记录 updatediary(long rowid, String title, String body) 更新主键 id 为 rowid 那条记录中的两个字段 title 和 body 字段的内容 小知识 : 什么是 ContentValues 类? ContentValues 类和 Hashtable 比较类似, 它也是负责存储一些名值对, 但是它存储的名值对当中的名是一个 String 类型, 而值都是基本类型 我们回顾一下, 在上一个例子当中, 我们是通过 SQL 语句进行插入操作,SQL 语句的好处是比较直观, 但是容易出错 但是在这个例子当中我们有更好的办法, 在这里我们将要插入的值都放到一个 ContentValues 的实例当中, 然后执行插入操作, 具体代码如下所示 : public long creatediary(string title, String body) { ContentValues initialvalues = new ContentValues(); initialvalues.put(key_title, title); initialvalues.put(key_body, body); Calendar calendar = Calendar.getInstance(); // 生成年月日字符串 String created = calendar.get(calendar.year)+" 年 "+calendar.get(calendar.month)+" 月 "+calendar.get(calendar.day_of_month)+" 日 "+calendar.get(calendar.hour_of_day)+" 时 "+calendar.get(calendar.minute)+" 分 "; initialvalues.put(key_created, created); return mdb.insert(database_table, null, initialvalues); ContentValues initialvalues = new ContentValues(); 实例化一个 contentvalues 类 initialvalues.put(key_title, title); 将列名和对应的列值放置到 initialvalues 里边 mdb.insert(database_table, null, initialvalues); 负责插入一条新的纪录, 如果插入成功则会返回这条记录的 id, 如果插入失败会返回 -1. 在更新一条记录的时候, 我们也是采用 ContentValues 的这套机制, 具体代码如下所示 : public boolean updatediary(long rowid, String title, String body) { ContentValues args = new ContentValues(); args.put(key_title, title); args.put(key_body, body); Calendar calendar = Calendar.getInstance(); String created = calendar.get(calendar.year) + " 年 " + calendar.get(calendar.month) + " 月 " + calendar.get(calendar.day_of_month) + " 日 " + calendar.get(calendar.hour_of_day) + " 时 " + calendar.get(calendar.minute) + " 分 "; args.put(key_created, created); return mdb.update(database_table, args, KEY_ROWID + "=" + rowid, null) > 0; 4. 第四步

15 现在返回到程序的主界面, 对应的 Activity 是 ActivityMain. 当我们单击 menu 按钮后会出现如图 所示界面 图 点击 menu 根据我们第 7 章对 menu 的学习, 对单击 menu 里边按钮的处理逻辑全部放在 onmenuitemselected 函数里, 具体代码如下所示 : public boolean onmenuitemselected(int featureid, MenuItem item) { switch (item.getitemid()) { case INSERT_ID: creatediary(); return true; case DELETE_ID: mdbhelper.deletediary(getlistview().getselecteditemid()); renderlistview(); return true; return super.onmenuitemselected(featureid, item); 如果点击添加一篇新日记按钮那么会执行到 creatediary(); 如果点击删除一条记录, 会执行 : mdbhelper.deletediary(getlistview().getselecteditemid()); 首先先删除当前被选中的条列所对应的数据库当中的记录 renderlistview(); 重新对界面刷新 在 creatediary() 函数里边的代码如下所示 : private void creatediary() { Intent i = new Intent(this, ActivityDiaryEdit.class); startactivityforresult(i, ACTIVITY_CREATE); 首先构造了一个 intent, 这个 intent 负责跳转到 ActivityDiaryEdit 里 然后启动这个 intent, 并且需要返回值 5. 第五步在 ActivityMain 中, 有多处地方都用到了 renderlistview() 函数 在 oncreate 里边用这个函数显示 ListView 当 ListView 需要发生变化后, 例如, 删除了一条记录或者增加了一条记录的时候, 我们调用这个函数进行刷新 ListView, 下边来看一下此函数的实现, 具体代码如下所示 : private void renderlistview() { mdiarycursor = mdbhelper.getallnotes(); startmanagingcursor(mdiarycursor); String[] from = new String[] { DiaryDbAdapter.KEY_TITLE, DiaryDbAdapter.KEY_CREATED ; int[] to = new int[] { R.id.text1, R.id.created ; SimpleCursorAdapter notes = new SimpleCursorAdapter(this, R.layout.diary_row, mdiarycursor, from, to); setlistadapter(notes); mdiarycursor = mdbhelper.getallnotes(); 我们首先获取数据库当中的所有数据, 这些数据以 Cursor 的形式存在 startmanagingcursor(mdiarycursor); 我们将生成的 Cursor Google 交给 Activity Android 来管理开发入门与实战 第八章, 这样的好处是系统能自动样章

16 做很多事情, 比如当程序暂停的时候, 这个系统可以卸载 Cursor 以节省空间, 当程序重新启动的时候系统重新查询生成 Cursor String[] from 里边定义了 ListView 每一排对应的数据是从数据库中的哪个列表里边取 和 SimpleAdapter 类似 int[] to 里边是一个 View 的数组 这些 View 只能是 TextView 或者 ImageView 这些 View 是以 id 的形式来表示的, 比如 Android.R.id.text1 SimpleCursorAdapter notes = new SimpleCursorAdapter(this,R.layout.diary_row, mdiarycursor, from, to); 生成一个 SimpleCursorAdapter, 我们说一个下每一个参数的意义 : 第一个参数是 Context 第二个参数为 R.layout.diary_row, 它关联在 diary_row.xml 文件当中定义的 Layout, 这个 Layout 规定 ListView 当中每一项的布局 第三个参数就是 Cursor 第四个参数是数据表当中的列的数组, 只有在这里边出现的列名, 数据才会对应的填充在 to 里边对应的 TextView 或者 ImageView 当中 第五个参数是在 LisView 里边每一项中需要被数据填充的 TextView 或者 ImageView setlistadapter(notes); 将 SimpleCursorAdapter 和 ListActivity 里边的 ListView 绑定起来, 至此在界面当中才会显示出列表来 小知识 : 什么是 SimpleCursorAdapter? 在第 7 章, 我们已经介绍过了 ArrayAdapter 和 SimpleAdapter 和它们俩类似,SimpleCursorAdapter 也是集成 Adapter ArrayAdapter 负责把一个字符串数组中的数据填充到一个 ListView 当中, 而对应的 SimpleCursorAdapter 负责把 Cursor 里边的内容填充到 ListView 当中 通过 SimpleCursorAdapter 可以把数据库当中一列的数据和 ListView 中一排进行对应起来 和前两个 Adapter 类似, 要求和数据进行对应的 View 必须是 TextView 或者 ImageView 6. 第六步单击添加一条数据的按钮, 程序运行界面如图 所示 图 新建一篇日记的界面 这个界面对应的 Activity 是 ActivityDiaryEdit, 对应的布局文件是 diary_edit.xml 对于这个布局文件里边用到了 LinearLayout TextView 和 EditText, 这些我们都已经讲过, 在这里不在赘述, 具体看一下代码 : <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=" Android:orientation="vertical" Android:layout_width="fill_parent" Android:layout_height="fill_parent"> <LinearLayout Android:orientation="vertical" Android:layout_width="fill_parent"

17 Android:layout_height="wrap_content"> <TextView Android:layout_width="wrap_content" Android:layout_height="wrap_content" Android:padding="2px" /> <EditText Android:layout_width="fill_parent" Android:layout_height="wrap_content" Android:layout_weight="1" /> </LinearLayout> <TextView Android:layout_width="wrap_content" Android:layout_height="wrap_content" /> <EditText Android:layout_width="fill_parent" Android:layout_height="wrap_content" Android:layout_weight="1" Android:scrollbars="vertical" /> <Button Android:layout_width="wrap_content" Android:layout_height="wrap_content" /> </LinearLayout> 当程序运行输入内容后, 单击确定按钮, 日记就会保存到数据库当中 下边来看一下代码具体是怎么执行的 当但击确定按钮后, 系统回调执行和按钮绑定的单击监听器里边的 onclick 方法, 具体代码如下所示 : public void onclick(view view) { String title = mtitletext.gettext().tostring(); String body = mbodytext.gettext().tostring(); if (mrowid!= null) { mdbhelper.updatediary(mrowid, title, body); else mdbhelper.creatediary(title, body); Intent mintent = new Intent(); setresult(result_ok, mintent); finish(); 首先获得 EditView 里边的数据 目前 mrowid 为 null, 所以执行 mdbhelper.creatediary(title, body); 将数据保存到数据当中 setresult(result_ok, mintent); 设置返回值 执行完上边的代码后, 系统跳转到 ActivityMain, 并执行回调函数 onactivityresult, 具体代码如下所示 : protected void onactivityresult(int requestcode, int resultcode, Intent intent) { super.onactivityresult(requestcode, resultcode, intent); renderlistview(); 在回调函数中, 我们重新对 ListView 进行刷新, 将所有的数据重新显示出 7. 第七步单击 ListView 里边的条列, 可以对刚才保存的数据进行编辑 具体怎么实现这个功能的, 我们可以看一下 ActivityMain 当中的 onlistitemclick 方法代码 : protected void onlistitemclick(listview l, View v, int position, long id) { super.onlistitemclick(l, v, position, id); Cursor c = mdiarycursor; c.movetoposition(position); Intent i = new Intent(this, ActivityDiaryEdit.class); i.putextra(diarydbadapter.key_rowid, id); i.putextra(diarydbadapter.key_title, c.getstring(c.getcolumnindexorthrow(diarydbadapter.key_title))); i.putextra(diarydbadapter.key_body, c.getstring(c.getcolumnindexorthrow(diarydbadapter.key_body))); startactivityforresult(i, ACTIVITY_EDIT);

18 c.movetoposition(position); 将在 Cursor 当中的指针移到 position 位置, 这个 position 是我们点击的这个一列在整个列表中的位置 Intent i = new Intent(this, ActivityDiaryEdit.class); 构造一个跳转到 ActivityDiaryEdit 的 intent. putextra() 方法负责将要传递的数据放到 intent 当中 c.getstring(c.getcolumnindexorthrow(diarydbadapter.key_title)) 得到这一条数据中列名为 title 的值 c.getstring(c.getcolumnindexorthrow(diarydbadapter.key_body)) 得到这一条数据中列名为 body 的值 startactivityforresult(i, ACTIVITY_EDIT); 启动 intent, 发生 Activity 的跳转 我们来看一下 ActivityDiaryEdit 中的 oncreate() 里边的代码 : Bundle extras = getintent().getextras(); if (extras!= null) { String title = extras.getstring(diarydbadapter.key_title); String body = extras.getstring(diarydbadapter.key_body); mrowid = extras.getlong(diarydbadapter.key_rowid); if (title!= null) { mtitletext.settext(title); if (body!= null) { mbodytext.settext(body); 对于 ActivityDiaryEdit 这个 Activity 有两个 intent 可以跳转进来, 一个是新建一篇日记的时候, 这个时候 intent 里边的 extras 部分没有任何数据 第二中情况是在点击列表的某一个条列的时候, 这个时候的 intent 如上所示会携带 extras 数据 所以在 ActivityDiaryEdit 中我们通过判断 extras 是否为 null, 就可以判断是那种 intent 启动的 当 extras 不为 null, 我们将 extras 里边的数据显示出来 8. 第八步在程序的主界面当中, 上下移动焦点 ( 可以通过键盘的上下键或者模拟器中的上下按键 ), 可以对拥有焦点的那一项进行删除, 如图 图 图 所示 图 上下移动焦点

19 图 进行删除 具体执行为 onmenuitemselected 中的代码 : 图 删除后

20 mdbhelper.deletediary(getlistview().getselecteditemid()); renderlistview(); getlistview() 方法获取当前的 ListView 引用 getselecteditemid() 方法得到当前这一列所对应的数据项的 rowid, 也就是这条数据在数据库中的主键 id mdbhelper.deletediary() 方法删去数据库中的这一列数据 renderlistview() 负责对列表重新刷新一遍 至此, 对数据库的学习就先告一段落, 接下来将学习 contentprovider, 它是 Android 应用当中非常重要的一部分 而且程序间的大部分数据交换都是通过 contentprovider 机制进行 8.5 ContentProvider 方式 初识 ContentProvider 在第 6 章当中, 介绍了组成 Android 程序的主要 4 部分, 它们分别是 Activity Broadcast Intent Receiver Service Content Provider 关于 Actvity 和相关 View 的部分, 已经在前边章节进行了比较详细的介绍, 在这一节中, 将学习 Android 应用里另外一个非常重要的部分 contentprovider 1. 什么是 ContentProvider Android 这个系统和其它的操作系统还不太一样, 读者需要记住的是, 数据在 Android 当中是私有的, 当然这些数据包括文件数据和数据库数据以及一些其他类型的数据 那这个时候有读者就会提出问题, 难道两个程序之间就没有办法对于数据进行交换?Android 这么优秀的系统不会让这种情况发生的 解决这个问题主要靠 ContentProvider 一个 Content Provider 类实现了一组标准的方法接口, 从而能够让其他的应用保存或读取此 Content Provider 的各种数据类型 也就是说, 一个程序可以通过实现一个 content Provider 的抽象接口将自己的数据暴露出去 外界根本看不到, 也不用看到这个应用暴露的数据在应用当中是如何存储的, 或者是用数据库存储还是用文件存储, 还是通过网上获得, 这些一切都不重要, 重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道, 可以读取程序的数据, 也可以删除程序的数据, 当然, 中间也会涉及到一些权限的问题 下边列举一些较常见的接口, 这些接口如下所示 query(uri uri, String[] projection, String selection, String[] selectionargs,string sortorder): 通过 Uri 进行查询, 返回一个 Cursor insert(uri url, ContentValues values): 将一组数据插入到 Uri 指定的地方 update(uri uri, ContentValues values, String where, String[] selectionargs) 更新 Uri 指定位置的数据 delete(uri url, String where, String[] selectionargs) 删除指定 Uri 并且符合一定条件的数据 2. 什么是 ContentResolver 外界的程序通过 ContentResolver 接口可以访问 ContentProvider 提供的数据, 在 Activity 当中通过 getcontentresolver() 可以得到当前应用的 ContentResolver 实例 ContentResolver 提供的接口和 ContentProvider 中需要实现的接口对应, 主要有一下几个 query(uri uri, String[] projection, String selection, String[] selectionargs,string sortorder): 通过 Uri 进行查询, 返回一个 Cursor insert(uri url, ContentValues values): 将一组数据插入到 Uri 指定的地方 update(uri uri, ContentValues values, String where, String[] selectionargs) Google 更新 Android Uri 指定位置的数据 开发入门与实战 第八章样章

21 delete(uri url, String where, String[] selectionargs) 删除指定 Uri 并且符合一定条件的数据 3.ContentProvider 和 ContentResolver 中用到的 Uri 在 ContentProvider 和 ContentResolver 当中用到了 Uri 的形式通常有两种, 一种是指定全部数据, 另一种是指定某个 ID 的数据 我们看下面的例子 content://contacts/people/ 这个 Uri 指定的就是全部的联系人数据 content://contacts/people/1 这个 Uri 指定的是 ID 为 1 的联系人的数据 在上边两个类中用到的 Uri 一般由 3 部分组成 第一部分是 :"content://" 第二部分是要获得数据的一个字符串片段 最后就是 ID( 如果没有指定 ID, 那么表示返回全部 ) 由于 URI 经常比较长, 而且有时候容易出错, 且难以理解 所以, 在 Android 当中定义了一些辅助类, 并且定义了一些常量来代替这些长字符串的使用, 例如下边的代码 : Contacts.People.CONTENT_URI ( 联系人的 URI) 使用 contentprovider 读取系统数据 在这个例子里边, 首先在系统的联系人应用当中插入一些联系人信息, 然后把这些联系人的名字和电话再显示出来, 通过这个例子可以学到 如何在联系人应用当中添加联系人 如何使用系统提供的 ContentProvder 如何使用 ContentResolver 当中的 query() 方法 具体实现步骤如下所示 1. 第一步 在 Eclipse 中打开 ex09_1_contentprovider 项目, 具体操作如下 (1) 新建一个项目, 依次单击 File > New > Android Project 项 (2) 在新建项目的对话框中, 选择 Create project from existing source (3) 单击浏览按钮, 找到 ex09_1_contentprovider 项目, 然后单击确定按钮 程序的目录结构如图 所示 :

22 图 程序的目录结构 2. 第二步首先运行这个项目, 将会看到如图 所示的界面 图 未添加任何数据的主界面 图 8-18 所示的列表中没有任何数据, 接下来的操作是为应用添加几条联系人数据 3. 第三步 : 按照下列图示添加几条数据到联系人列表中, 具体步骤如下 (1) 单击模拟器的 home 键, 在转出来的界面上, 单击桌面上的 Contacts 应用, 如图 所示 图 点击 Contacts 应用 (2) 进入应用后, 单击 Menu 项, 在出现的界面上单击 New contact 按钮, 如图 所示 图 单击 New contact 选项 Google Android 开发入门与实战 第八章样章

23 (3) 添加联系人姓名和电话号码信息, 如图 所示 图 添加联系人姓名和电话号码 (4) 单击 Menu 项, 在返回的界面上单击 Save 项保存, 如图 所示 图 保存联系人姓名和电话号码信息 (5) 按照上边的操作步骤, 添加了两条数据后显示如下图 8-5-7

24 图 添加后结果 4. 第四步再次运行程序, 模拟器显示如图 所示 图 模拟器显示 我们看一下程序 ActivityMain 中的 oncreate() 方法, 具体代码如下所示 : protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); Cursor c = getcontentresolver().query(phones.content_uri, null, null, null, null); startmanagingcursor(c); ListAdapter adapter = new SimpleCursorAdapter(this, Android.R.layout.simple_list_item_2, c, new String[] { Phones.NAME, Phones.NUMBER, new int[] { Android.R.id.text1, Android.R.id.text2 ); setlistadapter(adapter); getcontentresolver() 方法得到应用的 ContentResolver 实例 query(phones.content_uri, null, null, null, null) 它是 ContentResolver 里的方法, 负责查询所有联系人, 并返回一个 Cursor 这个方法参数比较多, 每个参数的具体含义如下 第一个参数为 Uri, 在这个例子里边这个 Uri 是联系人的 Uri. 第二个参数是一个字符串的数组, 数组里边的每一个字符串都是数据表中某一列的名字, 它指定返回数据表中那些列的值 第三个参数相当于 sql 语句的 where 部分, 描述哪些值是我们需要的 第四个参数是一个字符串数组, 它里边的值依次代替在第三个参数中出现的? 符号 第五个参数指定了排序的方式 startmanagingcursor(c); 让系统来管理生成的 Cursor. ListAdapter adapter = new SimpleCursorAdapter(this,Android.R.layout.simple_list_item_2, c, new String[] { Phones.NAME, Phones.NUMBER, new int[] { Android.R.id.text1, Android.R.id.text2 ); 生成一个 SimpleCursorAdapter ( 关于 SimpleCursorAdapter 我们在第 7 章已经详细介绍过了 ) setlistadapter(adapter) 将 ListView 和 SimpleCursorAdapter 进行绑定

25 8.5.3 使用 ContentProvider 操作数据 在上一个例子当中学习了 ContentProvider, 并且使用了系统中的一个联系人的 ContentProvider, 在本节当中, 我们仍然实现类似第 8 章所讲的日记本的例子, 只不过这次我们用 ContentProvider 实现, 而不是直接用数据库实现 这样外界的程序就可以访问得到日记本这个应用数据 通过这个例子可以学到 如何实现一个 ContentProvider 理解 UriMatcher 的含义 onprepareoptionsmenu 方法介绍 具体实现步骤如下所示 1. 第一步 在 Eclipse 中打开 ex09_2_contentprovider 项目, 具体操作步骤如下 (1) 新建一个项目, 依次单击 File New Android Project 项 (2) 在新建项目的对话框中, 选择 Create project from existing source 项 (3) 单击浏览项, 找到 ex09_2_contentprovider 项目, 然后单击确定 程序的目录结构如图 图 程序的目录结构 2. 第二步首先运行这个项目, 将会看到如图 所示界面

26 图 未添加任何数据的主界面 图 所示的这个界面我们在第 8 章的例子里边已经见过 本例的主 activity 仍然是一个 ListActivity, 而且关联的布局文件也是 diary_list.xml, 关于 ListActivity 的使用方法和详细说明可以参见第 7 章第 6 节 diary_list.xml 的代码如下所示 : <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=" Android:layout_width="wrap_content" Android:layout_height="wrap_content"> <ListView Android:id="@+id/Android:list" Android:layout_width="wrap_content" Android:layout_height="wrap_content" /> <TextView Android:id="@+id/Android:empty" Android:layout_width="wrap_content" Android:layout_height="wrap_content" Android:text=" 您还没有开始写日记呢! 点击下边的 Menu 按钮开始写日记吧 :)" /> </LinearLayout> 3. 第三步和第 8 章的日记本例子一样, 日记本的数据还是存储在 SQLite 数据库中, 但是不一样的是, 在这个例子里, 执行增 删 改 查操作时不是直接访问数据库, 而是通过日记本程序的 ContentProivder 来实现 我们先来看一下 Diary 这个类, 这个类里边有一个内部静态类 DiaryColumns, 和它的名字意思一样, 这个类里边主要定义了日记本数据库的列表字段的名字, 具体代码如下所示 : public static final class DiaryColumns implements BaseColumns { private DiaryColumns() { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/diaries"); public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.diary"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.diary"; public static final String DEFAULT_SORT_ORDER = "created DESC"; public static final String TITLE = "title"; public static final String BODY = "body"; public static final String CREATED = "created"; BaseColumns 是一个接口, 里边有两个变量, 一个是 _ID= _id, 一个是 _COUNT="_count" 在 Android 当中, 每一个张数据库表至少有一个字段, 而且这个字段是 _id. 所以当我们构造列名的辅助类时, 直接实现 BaseColumns, 这样我们便默认的拥有了 _id 字段 在我们的日记本的数据表里边, 一共有四个字段, 分别是 : id "title" "body" "created" 小知识 : 在 Android 的设计 哲学 里边是鼓励开发者使用内部类的, 这样不但使用方便, 而且执行效率也高 4. 第四步现在我们来看 DiaryContentProvider 类, 这个类继承自 ContentProvider, 在这个类里边实现了 ContentProvider 的一些接口方法, 并且成为日记本的一个 ContentProvider 我们现在通过学习这个类, 来详细了解实现一个自定义的 ContentProvider 的具体步骤

27 (1) 继承 ContentProvider.DiaryContentProvider 类是继承 ContentProvider 类的 (2) 定义一个 public static final 的 Uri 类型的变量, 并且命名为 CONTENT_URI DiaryContentProvider 所能处理的 Uri 都是基于 CONTENT_URI 来构建的, 需要注意的是, 这个 CONTENT_URI 中的内容以 content:// 开头, 并且全部小写, 且全局惟一 下边是在这个例子中的一个普通 URI, 通过分析这个 URI, 可以了解 content URI 的构成, 代码如下所示 : content://com.ex09_2_contentprovider.diarycontentprovider/diaries/1 第一部分是 content://, 这部分是支持存在的, 也是不用做什么修改的 第二部分是授权 (AUTHORITY) 部分, 在这个例子里边就是 com.ex09_2_contentprovider.diarycontentprovider, 授权部分是惟一的, 一般为在程序是我们实现的那个 ContentProvider 的全称, 并且全都小写 这部份是和在 AndroidManifest.xml 文件当中的 <provider Android:name="DiaryContentProvider" Android:authorities="com.ex09_2_contentprovider.diarycontentprovider" /> 部分对应的 第三部分是请求数据的类型 例如, 在这个例子当中定义的类型是 diaries 当然这一部分可以是 0 个片段或者多个片段构成, 如果 content provider 只是暴露出了一种类型的数据, 那么这部分可以为空, 但是如果暴露出了多种, 尤其是包含子类的的时候, 就不能为空 例如, 日记本程序里边可以暴露出来两种数据, 一种是用户自己的 "diaries/my", 另一种是其他人的 "diaries/others 第四部分就是 1, 当然这部分是允许为空的 如果为空, 表示请求全部数据 ; 如果不为空, 表示请求特定 ID 的数据 3. 构建用户的数据存储系统 在这个例子当中, 是将数据存储到数据库系统当中 通常是将数据存储在数据库系统中, 但是也可以将数据存储在其他的地方, 如文件系统等 4. 实现 ContentProvider 这个抽象类的抽象方法, 具体如下所示 : public boolean oncreate(), 当 ContentProvider 生成的时候调用此方法 public Cursor query(uri uri, String[] projection, String selection,string[] selectionargs, String sortorder), 此方法返回一个 Cursor 对象作为查询结果集 public Uri insert(uri uri, ContentValues initialvalues), 此方法负责往数据集当中插入一列, 并返回这一列的 Uri public int delete(uri uri, String where, String[] whereargs), 此方法负责删除指定 Uri 的数据 public int update(uri uri, ContentValues values, String where,string[] whereargs), 此方法负责更新指定 Uri 的数据 public String gettype(uri uri), 返回所给 Uri 的 MIME 类型 5. 在 AndroidManifest.xml 文件中增加 <provider> 标签 例如, 在这个例子中, 实现的代码为 :<provider Android:name="DiaryContentProvider" Android:authorities="com.ex09_2_contentprovider.diarycontentprovider" />, 其中 Android:name 为我们实现的这个 content provider 的类名 ;Android:authorities 为 content URI 的第二部分, 即授权部分, 代码为 :"com.ex09_2_contentprovider.diarycontentprovider" 5. 第五步 : 继续来看 DiaryContentProvider 类的代码 此实例的数据是存储在数据库当中, 和前面章节学过的数据例子一样, 此实例中我们定义了 DatabaseHelper 辅助类 这个类在第 8 章中有详细的讲解, 这里就不再进行详细说明, 具体代码如下所示 : private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, public void oncreate(sqlitedatabase db) { db.execsql("create TABLE " + DIARY_TABLE_NAME + " (" + DiaryColumns._ID + " INTEGER PRIMARY KEY," + DiaryColumns.TITLE + " TEXT," + DiaryColumns.BODY + " TEXT," + DiaryColumns.CREATED + " TEXT" + ");");

28 @Override public void onupgrade(sqlitedatabase db, int oldversion, int newversion) { db.execsql("drop TABLE IF EXISTS notes"); oncreate(db); 在 DiaryContentProvider 中, 我们定义了一些变量和常量, 其中这些常量主要是描述数据库的的信息 private static final String DATABASE_NAME = "database"; private static final int DATABASE_VERSION = 1; private static final String DIARY_TABLE_NAME = "diary"; private static HashMap<String, String> sdiariesprojectionmap; private static final int DIARIES = 1; private static final int DIARY_ID = 2; private static final UriMatcher surimatcher; 小知识 : 什么是 UriMatcher? UriMatcher 是匹配 Uri 的一个辅助类 例如, 在我们的 DiaryContentProvider 中的 static 模块中, 有下边的代码 : surimatcher = new UriMatcher(UriMatcher.NO_MATCH); surimatcher.adduri(diary.authority, "diaries", DIARIES); surimatcher.adduri(diary.authority, "diaries/#", DIARY_ID); surimatcher.adduri(diary.authority, "diaries", surimatcher.match(uri)) 表示, 如果我们的 Uri 是 content://com.ex09_2_contentprovider.diarycontentprovider/diaries/, 那么 surimatcher.match(uri) 的返回值就是 DIARIES surimatcher.adduri(diary.authority, "diaries/#", DIARY_ID) 表示, 如果我们的 Uri 是 content://com.ex09_2_contentprovider.diarycontentprovider/diaries/id( 其中后边的 id 是一个数字 ), 那么 surimatcher.match(uri) 的返回值就是 DIARY_ID 通过 UriMatcher 类我们可以很方便的判断一个 URi 的类型, 特别是判断这个 Uri 是对单个数据的请求, 还是对全部数据的请求 6. 第六步现在我们来看一下在第四步列出来的抽象方法具体是怎么实现的 我们就从增 删 改 查的顺序说起 (1) 首先来看插入的方法 :insert() 具体实现代码如下所示 public Uri insert(uri uri, ContentValues initialvalues) { if (surimatcher.match(uri)!= DIARIES) { throw new IllegalArgumentException("Unknown URI " + uri); ContentValues values; if (initialvalues!= null) { values = new ContentValues(initialValues); else { values = new ContentValues(); if (values.containskey(diary.diarycolumns.created) == false) { values.put(diary.diarycolumns.created, getformatecreateddate());

29 if (values.containskey(diary.diarycolumns.title) == false) { Resources r = Resources.getSystem(); values.put(diary.diarycolumns.title, r.getstring(android.r.string.untitled)); if (values.containskey(diary.diarycolumns.body) == false) { values.put(diary.diarycolumns.body, ""); SQLiteDatabase db = mopenhelper.getwritabledatabase(); long rowid = db.insert(diary_table_name, DiaryColumns.BODY, values); if (rowid > 0) { Uri diaryuri= ContentUris.withAppendedId( Diary.DiaryColumns.CONTENT_URI, rowid); return diaryuri; throw new SQLException("Failed to insert row into " + uri); 首先我们通过语句 surimatcher.match(uri)!= DIARIES 对传进来的 Uri 进行了判断, 如果这个 Uri 不是 DIARIES 类型的, 那么这个 Uri 就是一个非法的 Uri SQLiteDatabase db = mopenhelper.getwritabledatabase() 语句负责得到一个 SQLiteDatabase 的实例 db.insert(diary_table_name, DiaryColumns.BODY, values) 语句负责插入一条记录到数据库中 需要注意的是,insert() 返回的是一个 Uri, 而不是一个记录的 id, 所以我们还应该把记录的 id 构造成一个 Uri:Uri diaryuri= ContentUris.withAppendedId(Diary.DiaryColumns.CONTENT_URI, rowid).withappendedid() 方法在下边的小知识当中详细介绍 小知识 : 什么是 ContentUris? ContentUris 是 content URI 的一个辅助类 它有两个方法很有用, 具体如下所示 public static Uri withappendedid(uri contenturi, long id), 这个方法负责把 id 和 contenturi 连接成一个新的 Uri 比如在我们这个例子当中是这么使用的 :ContentUris.withAppendedId(Diary.DiaryColumns.CONTENT_URI, rowid). 如果 rowid 为 100 的话, 那么现在的这个 Uri 的内容就是 : content://com.ex09_2_contentprovider.diarycontentprovider/diaries/100. public static long parseid(uri contenturi), 这个方法负责把 content URI 后边的 id 解析出来. 比如现在这个 content URI 是 content://com.ex09_2_contentprovider.diarycontentprovider/diaries/100, 那么这个函数的返回值就是 100. (2) 现在来看删除方法 :delete() 具体实现代码如下所示 public int delete(uri uri, String where, String[] whereargs) { SQLiteDatabase db = mopenhelper.getwritabledatabase(); String rowid = uri.getpathsegments().get(1); return db.delete(diary_table_name, DiaryColumns._ID + "=" + rowid, null); rowid = uri.getpathsegments().get(1) 负责得到 rowid 的值.getPathSegments() 方法得到一个 String 的 List, 在我们例子当中 uri.getpathsegments().get(1) 为 rowid, 如果是 uri.getpathsegments().get(0) 那就值为 "diaries" db.delete(diary_table_name, DiaryColumns._ID + "=" + rowid, null) 是标准的 SQLite 删除操作. 第一个参数数据表的名字, 第二个参数相当于 sql 语句当中的 where 部分

30 (3) 更新一条数据的方法 :update() 具体实现代码如下所示 public int update(uri uri, ContentValues values, String where, String[] whereargs) { SQLiteDatabase db = mopenhelper.getwritabledatabase(); String rowid = uri.getpathsegments().get(1); return db.update(diary_table_name, values, DiaryColumns._ID + "=" + rowid, null); 首先得到 SQLiteDatabase 的实例, 然后得到 rowid. 最后再调用 db.update(diary_table_name, values, DiaryColumns._ID + "="+ rowid, null); 执行更新工作 (4) 插入一条数据的方法 :query() 具体实现代码如下所示 public Cursor query(uri uri, String[] projection, String selection, String[] selectionargs, String sortorder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); switch (surimatcher.match(uri)) { case DIARIES: qb.settables(diary_table_name); break; case DIARY_ID: qb.settables(diary_table_name); qb.appendwhere(diarycolumns._id + "=" + uri.getpathsegments().get(1)); break; default: throw new IllegalArgumentException("Unknown URI " + uri); String orderby; if (TextUtils.isEmpty(sortOrder)) { orderby = Diary.DiaryColumns.DEFAULT_SORT_ORDER; else { orderby = sortorder; SQLiteDatabase db = mopenhelper.getreadabledatabase(); Cursor c = qb.query(db, projection, selection, selectionargs, null, null, orderby); return c; SQLiteQueryBuilder 是一个构造 SQL 查询语句的辅助类 surimatcher.match(uri), 根据返回值可以判断这次查询请求时, 它是请求全部数据还是某个 id 的数据 如果返回值是 DIARIES, 那么只需要执行 qb.settables(diary_table_name) 语句就可以了 如果返回值是 DIARY_ID, 那么还需要将 where 部分的参数设置进去 Google, 代码为 Android qb.appendwhere(diarycolumns._id 开发入门与实战 第八章样章

31 + "="+ uri.getpathsegments().get(1)) SQLiteDatabase db = mopenhelper.getreadabledatabase(), 得到一个可读的 SQLiteDatabase 实例 Cursor c = qb.query(db, projection, selection, selectionargs, null,null, orderby); 这个查询类似于一个标准的 SQL 查询, 但是这个查询是 SQLiteQueryBuilder 来发起的, 而不是 SQLiteDatabase 直接发起的, 所以在参数方面略有不同 这个函数为 query(sqlitedatabase db, String[] projectionin, String selection, String[] selectionargs, String groupby, String having, String sortorder, String limit) 下边将各个参数介绍一下 第一个参数为要查询的数据库实例 第二个参数是一个字符串数组, 里边的每一项代表了需要返回的列名 第三个参数相当于 sql 语句中的 where 部分 第四个参数是一个字符串数组, 里边的每一项依次替代在第三个参数中出现的问号 (?) 第五个参数相当于 sql 语句当中的 groupby 部分 第六个参数相当于 sql 语句当中的 having 部分 第七个参数描述是怎么进行排序 第八个参数相当于 sql 当中的 limit 部分 控制返回的数据的个数 在 DiaryContentProvider 类里边还有两个方法, 一个是 gettype(uri uri), 另外一个是 getformatecreateddate() 后者根据时间得到一个我们特定格式的字符串, 比较简单 而前者是必须要重写的方法 下边我们看一下我们如何重写了 gettype 方法 public String gettype(uri uri) { switch (surimatcher.match(uri)) { case DIARIES: return DiaryColumns.CONTENT_TYPE; case DIARY_ID: return DiaryColumns.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unknown URI " + uri); 此方法返回一个所给 Uri 的指定数据的 MIME 类型 它的返回值如果以 vnd.android.cursor.item 开头, 那么就代表这个 Uri 指定的是单条数据 如果是以 vnd.android.cursor.dir 开头的话, 那么说明这个 Uri 指定的是全部数据 7. 第七步在程序主界面但击 Menu 键, 程序运行界面如图 所示 在 ActivityMain 中执行的代码如下所示 public boolean oncreateoptionsmenu(menu menu) { super.oncreateoptionsmenu(menu); menu.add(0, MENU_ITEM_INSERT, 0, R.string.menu_insert); return true; 我们给菜单 (menu) 添加了一项, 如图 所示 图 单击 Menu 键

32 单击添加日记按钮后进入如图 所示界面 图 新建日记的页面 图 所示的这个页面的布局和第 8 章日记本程序里的布局完全一样, 关于布局, 读者可以参看第 8 章日记本程序例子的讲解 下面来看一下在 ActivityDiaryEditor 中, 是怎么处理两种不同情况进入日记本程序界面的 一种是通过新建日记进入此界面, 另一种是经过编辑日记进入此日记运行界面 下面是在 ActivityDiaryEditor 程序中使用的 oncreate() 函数, 其代码如下所示 protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); settheme(android.r.style.theme_black); final Intent intent = getintent(); final String action = intent.getaction(); setcontentview(r.layout.diary_edit); mtitletext = (EditText) findviewbyid(r.id.title); mbodytext = (EditText) findviewbyid(r.id.body); confirmbutton = (Button) findviewbyid(r.id.confirm); if (EDIT_DIARY_ACTION.equals(action)) {// 编辑日记 mstate = STATE_EDIT; muri = intent.getdata(); mcursor = managedquery(muri, PROJECTION, null, null, null); mcursor.movetofirst(); String title = mcursor.getstring(1); mtitletext.settextkeepstate(title); String body = mcursor.getstring(2); mbodytext.settextkeepstate(body); setresult(result_ok, (new Intent()).setAction(mUri.toString())); settitle(" 编辑日记 "); else if (INSERT_DIARY_ACTION.equals(action)) {// 新建日记 mstate = STATE_INSERT; settitle(" 新建日记 "); else { Log.e(TAG, "no such action error"); finish(); return; confirmbutton.setonclicklistener(new View.OnClickListener() {

33 public void onclick(view view) { if (mstate == STATE_INSERT) { insertdiary(); else { updatediary(); Intent mintent = new Intent(); setresult(result_ok, mintent); finish(); ); 通过 getintent() 得到启动当前 Activity 的那个 Intent 通过 intent.getaction() 得到这个 intent 的动作 (action) 在此操作中, 当前的 Activiy 是通过单击先前的新建日记的按钮进来的, 所以, 看一下 ActivityMain 当中的 onoptionsitemselected() 函数, 了解一下动作 (action) 是怎么设置的, 此代码如下所示 : Intent intent0 = new Intent(this, ActivityDiaryEditor.class); intent0.setaction(activitydiaryeditor.insert_diary_action); intent0.setdata(getintent().getdata()); startactivity(intent0); 从上述代码可以看到, 通过语句 intent0.setaction(activitydiaryeditor.insert_diary_action) 设置的 action 是 ActivityDiaryEditor.INSERT_DIARY_ACTION) 实现的 在 ActivityDiaryEditor 中的 oncreate() 函数中, 当动作 (action) 为 INSERT_DIARY_ACTION 时候, 我们执行 mstate = STATE_INSERT, 也就是标记当前的状态时插入状态 如果当前的动作 (action) 是 EDIT_DIARY_ACTION, 那么当前就是编辑状态 :mstate = STATE_EDIT 当运行程序填充数据后单击确定按钮, 执行 confirmbutton 的点击监听器当中的 onclick() 函数 这个函数根据不同的状态执行插入或者更新数据的操作 按照现在的程序执行流程, 单击确定按钮后, 程序将执行插入操作, 实现插入操作代码如下所示 : private void insertdiary() { String title = mtitletext.gettext().tostring(); String body = mbodytext.gettext().tostring(); ContentValues values = new ContentValues(); values.put(diary.diarycolumns.created, DiaryContentProvider.getFormateCreatedDate()); values.put(diary.diarycolumns.title, title); values.put(diary.diarycolumns.body, body); getcontentresolver().insert(diary.diarycolumns.content_uri, values); 首先通过 gettext() 方法将编辑框中的数据都读出来 将要插入的数据都放到一个 ContentValues 的实例当中 调用 getcontentresolver() 得到当前应用的一个 ContentResolver 的实例 insert(diary.diarycolumns.content_uri, values) 语句为 ContentResolver 的插入方法, 这个方法有两个参数, 一个参数是数据的 Uri, 第二个参数是包含要插入数据的 ContentValues 的实例 执行这条语句的最终会调用 DiaryContentProvider 中的 insert() 方法

34 当单击确定按钮后, 程序运行出现如图 所示的界面 图 已经插入了一条数据 8. 第八步 按方向键向下键将焦点移动到这条数据上, 然后单击 Menu 键会出现如图 所示界面 图 单击 Menu 键界面 图 所示的这个界面和刚才讲到的单击 Menu 键出现的界面一样, 下面看一下实现的代码, 以便了解此界面是怎么生成, 具体实现代码如下所示 : public boolean onprepareoptionsmenu(menu menu) { super.onprepareoptionsmenu(menu); final boolean haveitems = getlistadapter().getcount() > 0; if (haveitems) { if (getlistview().getselecteditemid() > 0) { menu.removegroup(1); Uri uri = ContentUris.withAppendedId(getIntent().getData(), getselecteditemid()); Intent intent = new Intent(null, uri); menu.add(1, MENU_ITEM_EDIT, 1, " 编辑内容 ").setintent(intent); menu.add(1, MENU_ITEM_DELETE, 1, " 删除当前日记 "); else{ menu.removegroup(1); return true; 当前和这个 ListView 相关联的 ListAdapter 里边元素的个数大于零的时候 haveitems 为真, 否则为假 menu.removegroup(1), 如果 menu 里边有分组为 1 组的子项的话, 那么就先全部删除 getintent().getdata() 得到的 Uri 是 DiaryColumns.CONTENT_URI Google Android 开发入门与实战 第八章样章

35 通过 ContentUris.withAppendedId(getIntent().getData(),getSelectedItemId()) 生成一个和 ListView 中获得焦点这一项的数据的 Uri menu.add(1, MENU_ITEM_EDIT, 1, " 编辑内容 ").setintent(intent), 在 menu 当中增加了一项 编辑内容, 并且这一项和一个 intent 关联, 这个 intent 主要用于携带数据, 它里边携带的就是一个 Uri 的数据, 在下边的 onoptionsitemselected() 函数当中会用到 menu.add(1, MENU_ITEM_DELETE, 1, " 删除当前日记 "), 增加另外一项 如果 haveitems 为假, 也就是当前和这个 ListView 相关联的 ListAdapter 里边元素的个数为零, 即数据显示在 ListView 上的时候, 点击 Menu 按钮的话, 如果 menu 当中还有第 1 分组的子项的话, 就给现删除了 : menu.removegroup(1) 小知识 : 单击 Menu 按钮的时候, 在 Activity 中回调的函数可能有两个 第一个是 onoptionsitemselected(), 这个函数只在第一次在当前应用当中单击 Menu 键的时候回调, 以后再不回调 第二个是 onprepareoptionsmenu(), 这个函数在每次点击 Menu 键后显示 menu 的时候被系统回调, 每次 menu 显示前都会回调此函数 我们一般的根据条件改变 menu 显示的逻辑都放在这个函数里边 当单击 Menu 键时出现 3 个按扭, 单击其中的某一个按钮会触发 Android 系统回调 onoptionsitemselected() 函数, 此函数实现代码如下所示 public boolean onoptionsitemselected(menuitem item) { switch (item.getitemid()) { // 插入一条数据 case MENU_ITEM_INSERT: Intent intent0 = new Intent(this, ActivityDiaryEditor.class); intent0.setaction(activitydiaryeditor.insert_diary_action); intent0.setdata(getintent().getdata()); startactivity(intent0); return true; // 编辑当前数据内容 case MENU_ITEM_EDIT: Intent intent = new Intent(this, ActivityDiaryEditor.class); intent.setdata(item.getintent().getdata()); intent.setaction(activitydiaryeditor.edit_diary_action); startactivity(intent); return true; // 删除当前数据 case MENU_ITEM_DELETE: Uri uri = ContentUris.withAppendedId(getIntent().getData(), getlistview().getselecteditemid()); getcontentresolver().delete(uri, null, null); renderlistview(); return super.onoptionsitemselected(item); 当用户单击添加新日记按钮后, 程序新建一个跳转 Activity 的 intent0, 并且设置了 Action 和 data, 这两个部分在程序跳转到 ActivityDiaryEditor 后会用到 当用户单击了编辑按钮后, 程序也新建了一个跳转 Activity 的 intent, 并且也设置了 Action 和 data 同样, 这两部分在程序跳转到 ActivityDiaryEditor 后会用到 需要注意的是, 通过 item.getintent().getdata() 得到了所需要的 Uri 当用户单击删除按钮后, 程序通过 ContentUris.withAppendedId(getIntent().getData(),getListView().getSelectedItemId()) 先得到需要删除数据的 Uri, 然后得到当前的 ContentResolvor, 然后调用它的 delete 方法进行删除 删除数据后, 调用 renderlistview() 函数对 ListView 进行及时刷新

36 8.6 网络存储 前面介绍的几种存储都是将数据存储在本地设备上, 除此之外, 还有一种存储 ( 获取 ) 数据的方式, 通过网络来实现数据的存储和获取, 下面看一个在 Android 上调用 WebService 的例子 注意 : 在 Android 的早期版本中, 曾经支持过进行 XMPP SERVICE 和 WEB SERVICE 的远程访问 Android SDK 1.0 以后的版本对它以前的 API 作了许多的变更 Android 1.0 以上版本不再支持 XMPP SERVICE, 而且访问 WEB SERVIC 的 API 全部变更 1. 例子介绍通过邮政编码查询该地区的天气预报, 以 POST 发送的方式发送请求到 webservicex.net 站点, 访问 WebService.webservicex.net 站点上提供查询天气预报的服务, 具体信息请参考其 WSDL 文档, 网址为 : 输入 : 美国某个城市的邮政编码 输出 : 该邮政编码对应城市的天气预报 2. 实现步骤如下 (1) 如果需要访问外部网络, 则需要在 AndroidManifest.xml 文件中加入如下代码申请权限许可 : <!-- Permissions --> <uses-permission Android:name="Android.permission.INTERNET" /> (2) 以 HTTP POST 的方式发送 ( 注意 :SERVER_URL 并不是指 WSDL 的 URL, 而是服务本身的 URL) 实现的代码如下所示 : private static final String SERVER_URL = " // 定义需要获取的内容来源地址 HttpPost request = new HttpPost(SERVER_URL); // 根据内容来源地址创建一个 Http 请求 // Add Parameters List <NameValuePair> params = new ArrayList <NameValuePair>(); // set a zip code: WASHINGTON params.add(new BasicNameValuePair("ZipCode", "200120")); // 添加必须的参数 request.setentity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); // 设置参数的编码 try { // Send request HttpResponse httpresponse = new DefaultHttpClient().execute(request); // 发送请求并获取反馈 // 解析返回的内容 if(httpresponse.getstatusline().getstatuscode()!= 404) { String result = EntityUtils.toString(httpResponse.getEntity()); Log.d(LOG_TAG, result); catch (Exception e) { Log.e(LOG_TAG, e.getmessage()); 如上代码使用 Http 从 webservicex 获取 ZipCode 为 ( 美国 WASHINGTON D.C) 的内容, 其返回的内容如下 : <WeatherForecasts xmlns:xsd=" xmlns:xsi=" xmlns=" <Latitude> </Latitude> <Longitude> </Longitude> <AllocationFactor> </AllocationFactor> <FipsCode>11</FipsCode> <PlaceName>WASHINGTON</PlaceName> <StateCode>DC</StateCode> <Details>

37 <WeatherData> <Day>Saturday, April 25, 2009</Day> <WeatherImage> <MaxTemperatureF>88</MaxTemperatureF> <MinTemperatureF>57</MinTemperatureF> <MaxTemperatureC>31</MaxTemperatureC> <MinTemperatureC>14</MinTemperatureC> </WeatherData> <WeatherData> <Day>Sunday, April 26, 2009</Day> <WeatherImage> <MaxTemperatureF>89</MaxTemperatureF> <MinTemperatureF>60</MinTemperatureF> <MaxTemperatureC>32</MaxTemperatureC> <MinTemperatureC>16</MinTemperatureC> </WeatherData>.. </Details> </WeatherForecasts> 这个例子演示了如何在 Android 中通过网络获取数据, 掌握该类内容, 开发者需要熟悉 java.net.*,android.net.* 这两个包的内容, 在这就不赘述了, 请读者参阅相关文档 8.7 本章小结 本章介绍了如何在 Android 中实现数据的存储和获取, 详细介绍了轻量级的的 SharedPreferences, 比较传统的文件存储和数据库存储, 也介绍了 Android 特有的 ContentProvider 方式, 最后以一个获取天气预报的例子, 演示了如何通过网络获取 WebService 的数据 通过本章的介绍, 读者应该比较全面地了解了 Andoid 中的存储数据的方式, 至于在应用程序中使用哪一种数据存储方式取决于开发者的具体需求, 通过这章的介绍, 读者将会做出合适的选择

建模与图形思考

建模与图形思考 F06_c 观摩 :ContentProvider 基於軟硬整合觀點 架构與 DB 引擎移植方法 ( c) By 高煥堂 4 通用性基类 ContentProvider 基於軟硬整合觀點 的使用范例 刚才的范例里, 我们直接使用 DataPersist 类的接口来与 SQLite 沟通 本节将替 DataPersist 配上 ContentProvider 基类, 让 Client 能透过 ContentProvider

More information

res/layout 目录下的 main.xml 源码 : <?xml version="1.0" encoding="utf 8"?> <TabHost android:layout_height="fill_parent" xml

res/layout 目录下的 main.xml 源码 : <?xml version=1.0 encoding=utf 8?> <TabHost android:layout_height=fill_parent xml 拓展训练 1- 界面布局 1. 界面布局的重要性做应用程序, 界面是最基本的 Andorid 的界面, 需要写在 res/layout 的 xml 里面, 一般情况下一个 xml 对应一个界面 Android 界面布局有点像写 html( 连注释代码的方式都一样 ), 要先给 Android 定框架, 然后再在框架里面放控件,Android 提供了几种框架,AbsoluteLayout,LinearLayout,

More information

主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new

主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new ListView 自訂排版 主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new int[]{r.drawable.dog1, R.drawable.dog2,

More information

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例 帝国 CMS 下在 PHP 文件中调用数据库类执行 SQL 语句实例 这篇文章主要介绍了帝国 CMS 下在 PHP 文件中调用数据库类执行 SQL 语句实例, 本文还详细介绍了帝国 CMS 数据库类中的一些常用方法, 需要的朋友可以参考下 例 1: 连接 MYSQL 数据库例子 (a.php)

More information

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 odps-sdk 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基 开放数据处理服务 ODPS SDK SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基础功能的主体接口, 搜索关键词 "odpssdk-core" 一些

More information

Android Service

Android Service Android Service- 播放音樂 建國科技大學資管系 饒瑞佶 2013/7 V1 Android Service Service 是跟 Activity 並行 一個音樂播放程式若沒使用 Service, 即使按 home 鍵畫面離開之後, 音樂還是照播 如果再執行一次程式, 新撥放的音樂會跟先前撥放的一起撥, 最後程式就會出錯 執行中的程式完全看不到! 但是, 寫成 Service 就不同了

More information

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double

More information

用手機直接傳值不透過網頁連接, 來當作搖控器控制家電 ( 電視遙控器 ) 按下按鍵發送同時會回傳值來確定是否有送出 問題 :1. 應該是使用了太多 thread 導致在傳值上有問題 2. 一次按很多次按鈕沒辦法即時反應

用手機直接傳值不透過網頁連接, 來當作搖控器控制家電 ( 電視遙控器 ) 按下按鍵發送同時會回傳值來確定是否有送出 問題 :1. 應該是使用了太多 thread 導致在傳值上有問題 2. 一次按很多次按鈕沒辦法即時反應 專題進度 老師 : 趙啟時老師 學生 : 陳建廷 2013/10/13 用手機直接傳值不透過網頁連接, 來當作搖控器控制家電 ( 電視遙控器 ) 按下按鍵發送同時會回傳值來確定是否有送出 問題 :1. 應該是使用了太多 thread 導致在傳值上有問題 2. 一次按很多次按鈕沒辦法即時反應 程式碼 : package com.example.phone; import java.util.arraylist;

More information

Guava学习之Resources

Guava学习之Resources Resources 提供提供操作 classpath 路径下所有资源的方法 除非另有说明, 否则类中所有方法的参数都不能为 null 虽然有些方法的参数是 URL 类型的, 但是这些方法实现通常不是以 HTTP 完成的 ; 同时这些资源也非 classpath 路径下的 下面两个函数都是根据资源的名称得到其绝对路径, 从函数里面可以看出,Resources 类中的 getresource 函数都是基于

More information

untitled

untitled JavaEE+Android - 6 1.5-2 JavaEE web MIS OA ERP BOSS Android Android Google Map office HTML CSS,java Android + SQL Sever JavaWeb JavaScript/AJAX jquery Java Oracle SSH SSH EJB+JBOSS Android + 1. 2. IDE

More information

预览图 : (2) 在 SelectCity.java 中增加控件, 用于绑定 select_city 文件的 ListView, TextView,EditTest 等控件 代码和注释如下 :

预览图 : (2) 在 SelectCity.java 中增加控件, 用于绑定 select_city 文件的 ListView, TextView,EditTest 等控件 代码和注释如下 : EditText 实现城市搜索 1801210778 邹宇航 摘要 : 使用 EditText 实现搜索城市的功能, 以此为依据更新 ListView 1. 效果图 : 2. 主要步骤 (1) 在 select-city.xml 布局文件中中添加 EditText 控件用作搜索框, 然后添加 ListView 控件用来显示城市名字内容 代码如下 : 预览图 : (2) 在 SelectCity.java

More information

Microsoft Word - 第1章 Android基本概念.docx

Microsoft Word - 第1章 Android基本概念.docx Android 系 统 下 Java 编 程 详 解 作 者 : 华 清 远 见 第 1 章 Android 基 本 概 念 本 章 简 介 本 章 主 要 介 绍 Android 基 本 概 念 方 面 的 内 容, 包 括 Android 平 台 特 性 Android 系 统 架 构 Android 开 发 框 架 和 Android 开 发 环 境 搭 建 1.1 Android 简 介 Android

More information

Microsoft Word - 01.DOC

Microsoft Word - 01.DOC 第 1 章 JavaScript 简 介 JavaScript 是 NetScape 公 司 为 Navigator 浏 览 器 开 发 的, 是 写 在 HTML 文 件 中 的 一 种 脚 本 语 言, 能 实 现 网 页 内 容 的 交 互 显 示 当 用 户 在 客 户 端 显 示 该 网 页 时, 浏 览 器 就 会 执 行 JavaScript 程 序, 用 户 通 过 交 互 式 的

More information

實作SQLiteOpenHelper類別

實作SQLiteOpenHelper類別 SQLiteOpenHelper 類別存取 SQLite 建國科技大學資管系 饒瑞佶 2013/5 V1 Android 連結資料庫 MySQL SQL Server Web Service 遠端資料庫 Internet Intranet Android SQLite 單機資料庫 Android vs. SQLite 透過 SQLiteOpenHelper 類別來操作 建立資料庫 ( 建構子 ) 建立資料表

More information

Android 开发教程

Android 开发教程 封面 1 文件存取编程基础 文件 文件可以用来存储比使用引用更大数量的数据 Android 提供方法来读 写文件 只有本地文件可以被访问 优点 : 可以存储大容量的数据 缺点 : 文件更新或是格式改变可能会导致巨大的编程工作 文件操作 读文件 Context.openFileInput(String name) 打开一个与应用程序联系的私有文件输入流 当文件不存在时抛出 FileNotFoundException

More information

专题二-android数据存储

专题二-android数据存储 Android 数据存储的应用 报告内容 Android 系统安全简介 参数化 / 文件存储 数据库 Content Provider 网络存储 Linux 层面 Android 系统安全简介 在 Android 中, 各个应用程序之间是相互独立的, 彼此的数据不能共享 程序运行时检查 Android 允许应用程序在执行时调用其他程序的组件 当应用程序安装时, 操作系统要求安装程序必须签名 公钥私钥对技术

More information

Android Android Android SDK iv

Android Android Android SDK iv Android Market Google Android SDK Apple Google Microsoft b2c b 2010 Internet Android how why iii Android 240... Android Android SDK iv Android Market Google Android SDK Visual C++ Java N-tier J2EE Unix/Linux

More information

使用Cassandra和Spark 2.0实现Rest API服务

使用Cassandra和Spark 2.0实现Rest API服务 使用 Cassandra 和 Spark 2.0 实现 Rest API 服务 在这篇文章中, 我将介绍如何在 Spark 中使用 Akkahttp 并结合 Cassandra 实现 REST 服务, 在这个系统中 Cassandra 用于数据的存储 我们已经见识到 Spark 的威力, 如果和 Cassandra 正确地结合可以实现更强大的系统 我们先创建一个 build.sbt 文件, 内容如下

More information

本章学习目标 小风 Java 实战系列教程 SpringMVC 简介 SpringMVC 的入门案例 SpringMVC 流程分析 配置注解映射器和适配器 注解的使用 使用不同方式的跳转页面 1. SpringMVC 简介 Spring web mvc

本章学习目标 小风 Java 实战系列教程 SpringMVC 简介 SpringMVC 的入门案例 SpringMVC 流程分析 配置注解映射器和适配器 注解的使用 使用不同方式的跳转页面 1. SpringMVC 简介 Spring web mvc 本章学习目标 SpringMVC 简介 SpringMVC 的入门案例 SpringMVC 流程分析 配置注解映射器和适配器 配置视图解析器 @RequestMapping 注解的使用 使用不同方式的跳转页面 1. SpringMVC 简介 Spring web mvc 和 Struts2 都属于表现层的框架, 它是 Spring 框架的一部分, 我们可 以从 Spring 的整体结构中看得出来 :

More information

Android Fragment

Android Fragment Android Fragment 建國科技大學資管系饒瑞佶 2017/10 V1 Android 3.0 後才支援 Fragment 解決部分 App 適應螢幕大小的問題 它類似於 Activity, 可以像 Activity 可以擁有自己的版面設計, 也和 Activity 一樣有自己的生命週期 ( 具備 oncreate() oncreateview() 與 onpause() 方法 ) LifeCycle

More information

Microsoft PowerPoint - chapter08.ppt

Microsoft PowerPoint - chapter08.ppt 第 8 章数据存储和访问 本章学习目标 : 掌握 SharedPreferences 的使用方法 掌握各种文件存储的区别与适用情况 了解 SQLite 数据库的特点和体系结构 掌握 SQLite 数据库的建立和操作方法 理解 ContentProvider 的用途和原理 掌握 ContentProvider 的创建与使用方法 数据存取的四种方式 SharedPreferences 轻量级键 - 值方式存储,

More information

單步除錯 (1/10) 打開 Android Studio, 點選 Start a new Android Studio project 建立專案 Application name 輸入 BMI 點下 Next 2 P a g e

單步除錯 (1/10) 打開 Android Studio, 點選 Start a new Android Studio project 建立專案 Application name 輸入 BMI 點下 Next 2 P a g e Android Studio Debugging 本篇教學除了最基本的中斷點教學之外, 還有條件式中斷的教學 條件式中斷是進階的除錯技巧, 在某些特定情況中, 我們有一個函數可能會被呼叫數次, 但是我們只希望在某種條件成立時才進行中斷, 進而觀察變數的狀態 而條件式中斷這項技巧正是符合這項需求 本教學分兩部分 單步除錯 (Page2~11, 共 10) 條件式中斷點 (Page12~17, 共 6)

More information

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double x) { d

More information

中 文 摘 要 智 慧 型 手 機 由 於 有 強 大 的 功 能, 以 及 優 渥 的 便 利 性, 還 能 與 網 路 保 持 隨 時 的 鏈 結 與 同 步 更 新, 因 此 深 受 廣 大 消 費 者 喜 愛, 當 然, 手 機 遊 戲 也 成 為 現 代 人 不 可 或 缺 的 娛 樂 之

中 文 摘 要 智 慧 型 手 機 由 於 有 強 大 的 功 能, 以 及 優 渥 的 便 利 性, 還 能 與 網 路 保 持 隨 時 的 鏈 結 與 同 步 更 新, 因 此 深 受 廣 大 消 費 者 喜 愛, 當 然, 手 機 遊 戲 也 成 為 現 代 人 不 可 或 缺 的 娛 樂 之 臺 北 市 大 安 高 級 工 業 職 業 學 校 資 訊 科 一 百 零 一 學 年 度 專 題 製 作 報 告 ------ 以 Android 製 作 ------ ----- 連 線 塔 防 遊 戲 ------ Tower defense game using Internet technology 班 級 : 資 訊 三 甲 組 別 : A9 組 組 員 : 葉 冠 麟 (9906129)

More information

Database_001

Database_001 作者 : 林致宇日期 :2011/10/26 主要參考來源 : http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applicat ions/ 問題 : 如在存取一個已經建立好的資料庫? 解答 : 有一些應用程式會需要讀取已經建立好的資料庫, 例如一個試題測驗應用程式, 裡面的試題可能已經於電腦上, 使用任何的

More information

// HDevelopTemplateWPF projects located under %HALCONEXAMPLES%\c# using System; using HalconDotNet; public partial class HDevelopExport public HTuple

// HDevelopTemplateWPF projects located under %HALCONEXAMPLES%\c# using System; using HalconDotNet; public partial class HDevelopExport public HTuple halcon 与 C# 混合编程之 Halcon 代码调用 写在前面 完成 halcon 与 C# 混合编程的环境配置后, 进行界面布局设计构思每一个按钮所需要实现 的功能, 将 Halcon 导出的代码复制至相应的 C# 模块下即可 halcon 源程序 : dev_open_window(0, 0, 512, 512, 'black', WindowHandle) read_image (Image,

More information

建立Android新專案

建立Android新專案 Android 智 慧 型 手 機 程 式 設 計 Android WebService 建 國 科 技 大 學 資 管 系 饒 瑞 佶 2012/4 V1 2012/8 V2 2013/5 V3 2014/10 v4 提 醒 這 節 的 內 容 針 對 的 是 MS 的 Web Service 或 是 使 用 SOAP(Simple Object Access Protocol) 標 準 建 立

More information

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6 www.brainysoft.net 1.JasperReport ireport...4 1.1 JasperReport...4 1.2 ireport...4 2....4 2.1 JDK...4 2.1.1 JDK...4 2.1.2 JDK...5 2.1.3 JDK...5 2.2 ant...6 2.2.1 ant...6 2.2.2 ant...6 2.3 JasperReport...7

More information

使用MapReduce读取XML文件

使用MapReduce读取XML文件 使用 MapReduce 读取 XML 文件 XML( 可扩展标记语言, 英语 :extensible Markup Language, 简称 : XML) 是一种标记语言, 也是行业标准数据交换交换格式, 它很适合在系统之间进行数据存储和交换 ( 话说 Hadoop H ive 等的配置文件就是 XML 格式的 ) 本文将介绍如何使用 MapReduce 来读取 XML 文件 但是 Had oop

More information

通过Hive将数据写入到ElasticSearch

通过Hive将数据写入到ElasticSearch 我在 使用 Hive 读取 ElasticSearch 中的数据 文章中介绍了如何使用 Hive 读取 ElasticSearch 中的数据, 本文将接着上文继续介绍如何使用 Hive 将数据写入到 ElasticSearch 中 在使用前同样需要加入 elasticsearch-hadoop-2.3.4.jar 依赖, 具体请参见前文介绍 我们先在 Hive 里面建个名为 iteblog 的表,

More information

EJB-Programming-4-cn.doc

EJB-Programming-4-cn.doc EJB (4) : (Entity Bean Value Object ) JBuilder EJB 2.x CMP EJB Relationships JBuilder EJB Test Client EJB EJB Seminar CMP Entity Beans Session Bean J2EE Session Façade Design Pattern Session Bean Session

More information

一 登录 crm Mobile 系统 : 输入 ShijiCare 用户名和密码, 登录系统, 如图所示 : 第 2 页共 32 页

一 登录 crm Mobile 系统 : 输入 ShijiCare 用户名和密码, 登录系统, 如图所示 : 第 2 页共 32 页 第 1 页共 32 页 crm Mobile V1.0 for IOS 用户手册 一 登录 crm Mobile 系统 : 输入 ShijiCare 用户名和密码, 登录系统, 如图所示 : 第 2 页共 32 页 二 crm Mobile 界面介绍 : 第 3 页共 32 页 三 新建 (New) 功能使用说明 1 选择产品 第 4 页共 32 页 2 填写问题的简要描述和详细描述 第 5 页共

More information

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 ->

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 -> 目录 1 大概思路... 1 2 创建 WebAPI... 1 3 创建 CrossMainController 并编写... 1 4 Nuget 安装 microsoft.aspnet.webapi.cors... 4 5 跨域设置路由... 4 6 编写 Jquery EasyUI 界面... 5 7 运行效果... 7 8 总结... 7 1 1 大概思路 创建 WebAPI 创建 CrossMainController

More information

epub83-1

epub83-1 C++Builder 1 C + + B u i l d e r C + + B u i l d e r C + + B u i l d e r C + + B u i l d e r 1.1 1.1.1 1-1 1. 1-1 1 2. 1-1 2 A c c e s s P a r a d o x Visual FoxPro 3. / C / S 2 C + + B u i l d e r / C

More information

基于ECO的UML模型驱动的数据库应用开发1.doc

基于ECO的UML模型驱动的数据库应用开发1.doc ECO UML () Object RDBMS Mapping.Net Framework Java C# RAD DataSetOleDbConnection DataGrod RAD Client/Server RAD RAD DataReader["Spell"].ToString() AObj.XXX bug sql UML OR Mapping RAD Lazy load round trip

More information

ChinaBI企业会员服务- BI企业

ChinaBI企业会员服务- BI企业 商业智能 (BI) 开源工具 Pentaho BisDemo 介绍及操作说明 联系人 : 杜号权苏州百咨信息技术有限公司电话 : 0512-62861389 手机 :18616571230 QQ:37971343 E-mail:du.haoquan@bizintelsolutions.com 权限控制管理 : 权限控制管理包括 : 浏览权限和数据权限 ( 权限部分两个角色 :ceo,usa; 两个用户

More information

untitled

untitled 1 Access 料 (1) 立 料 [] [] [ 料 ] 立 料 Access 料 (2) 料 [ 立 料 ] Access 料 (3) 料 料 料 料 料 料 欄 ADO.NET ADO.NET.NET Framework 類 來 料 料 料 料 料 Ex MSSQL Access Excel XML ADO.NET 連 .NET 料.NET 料 料來 類.NET Data Provider

More information

软件工程文档编制

软件工程文档编制 实训抽象类 一 实训目标 掌握抽象类的定义 使用 掌握运行时多态 二 知识点 抽象类的语法格式如下 : public abstract class ClassName abstract void 方法名称 ( 参数 ); // 非抽象方法的实现代码 在使用抽象类时需要注意如下几点 : 1 抽象类不能被实例化, 实例化的工作应该交由它的子类来完成 2 抽象方法必须由子类来进行重写 3 只要包含一个抽象方法的抽象类,

More information

(TestFailure) JUnit Framework AssertionFailedError JUnit Composite TestSuite Test TestSuite run() run() JUnit

(TestFailure) JUnit Framework AssertionFailedError JUnit Composite TestSuite Test TestSuite run() run() JUnit Tomcat Web JUnit Cactus JUnit Java Cactus JUnit 26.1 JUnit Java JUnit JUnit Java JSP Servlet JUnit Java Erich Gamma Kent Beck xunit JUnit boolean JUnit Java JUnit Java JUnit Java 26.1.1 JUnit JUnit How

More information

题目

题目 开发 Android 应用 目的 : 帮助大家了解 Android 平台开发 作者 : 王威威 技术爱好 : linux,qt, 嵌入式开发 MSN : wangweiweicdma@hotmail.com Email : wang.weiwei1@ztenc.com.cn wangjiecdma@126.com 下载开发资源 1 下载最新的 Android SDK http://code.google.com/android/download.html

More information

RecyclerView and CardVew

RecyclerView and CardVew RecyclerView and CardView 建國科技大學資管系饒瑞佶 2017/10 V1 CardView CardView A CardView is a ViewGroup. Like any other ViewGroup, it can be added to youractivity or Fragment using a layout XML file. To create an

More information

PowerPoint Presentation

PowerPoint Presentation 第 8 章数据存储和访问 本章结构 : 简单存储 SharedPreferences 文件存储 数据库存储 SQLite 数据共享 ContentProvider 简单存储 SharedPreferences SharedPreferences 简介 一种轻量级的数据保存方式 可以将 NVP(Name/Value Pair, 名称 / 值对 ) 保存在 Android 的文件系统中, 而丏 SharedPreferences

More information

6-1 Table Column Data Type Row Record 1. DBMS 2. DBMS MySQL Microsoft Access SQL Server Oracle 3. ODBC SQL 1. Structured Query Language 2. IBM

6-1 Table Column Data Type Row Record 1. DBMS 2. DBMS MySQL Microsoft Access SQL Server Oracle 3. ODBC SQL 1. Structured Query Language 2. IBM CHAPTER 6 SQL SQL SQL 6-1 Table Column Data Type Row Record 1. DBMS 2. DBMS MySQL Microsoft Access SQL Server Oracle 3. ODBC SQL 1. Structured Query Language 2. IBM 3. 1986 10 ANSI SQL ANSI X3. 135-1986

More information

Microsoft Word - Hibernate与Struts2和Spring组合指导.doc

Microsoft Word - Hibernate与Struts2和Spring组合指导.doc 1.1 组合 Hibernate 与 Spring 1. 在 Eclipse 中, 新建一个 Web project 2. 给该项目增加 Hibernate 开发能力, 增加 Hibernate 相关类库到当前项目的 Build Path, 同时也提供了 hibernate.cfg.xml 这个配置文件 3. 给该项目增加 Spring 开发能力, 增加 spring 相关类库到当前项目的 Build

More information

7 TextView tv = (TextView)findViewById(R.id.tv_birthday); 8 9 Context otherapp = null; try { 12 otherapp = createpackagecontext("lincyu.horoscop

7 TextView tv = (TextView)findViewById(R.id.tv_birthday); 8 9 Context otherapp = null; try { 12 otherapp = createpackagecontext(lincyu.horoscop Chapter 12 資料分享 作者 : 林致孙 上一章中我們學到了資料的儲存方式, 也瞭解資料會依應用程式的 Package name 儲存於適當的資料夾內, 然而 Android 並沒有一個共同的儲存空間讓所有應用程式存取, 因此若要讓其它的應用程式使用我的資料, 就必須使用一些技巧, 本章主要在學習資料分享的方式 在先前的章節中已經數次提到, 一個 Android 應用程式是由四個構成要素所組成的

More information

Microsoft Office SharePoint Server MOSS Web SharePoint Web SharePoint 22 Web SharePoint Web Web SharePoint Web Web f Lists.asmx Web Web CAML f

Microsoft Office SharePoint Server MOSS Web SharePoint Web SharePoint 22 Web SharePoint Web Web SharePoint Web Web f Lists.asmx Web Web CAML f Web Chapter 22 SharePoint Web Microsoft Office SharePoint Server MOSS Web SharePoint Web SharePoint 22 Web 21 22-1 SharePoint Web Web SharePoint Web Web f Lists.asmx Web Web CAML f Views.asmx View SharePoint

More information

手册 doc

手册 doc 1. 2. 3. 3.1 3.2 3.3 SD 3.4 3.5 SD 3.6 3.7 4. 4.1 4.2 4.3 SD 4.4 5. 5.1 5.2 5.3 SD 6. 1. 1~3 ( ) 320x240~704x288 66 (2G SD 320x2401FPS ) 32M~2G SD SD SD SD 24V DC 3W( ) -10~70 10~90% 154x44x144mm 2. DVR106

More information

untitled

untitled 1 Access 料 (1) 立 料 [] [] [ 料 ] 立 料 Access 料 (2) 料 [ 立 料 ] Access 料 (3) 料 料 料 料 料 料 欄 ADO.NET ADO.NET.NET Framework 類 來 料 料 料 料 料 Ex MSSQL Access Excel XML ADO.NET 連 .NET 料.NET 料 料來 類.NET Data Provider

More information

<4D6963726F736F667420506F776572506F696E74202D20332D322E432B2BC3E6CFF2B6D4CFF3B3CCD0F2C9E8BCC6A1AAD6D8D4D8A1A2BCCCB3D0A1A2B6E0CCACBACDBEDBBACF2E707074>

<4D6963726F736F667420506F776572506F696E74202D20332D322E432B2BC3E6CFF2B6D4CFF3B3CCD0F2C9E8BCC6A1AAD6D8D4D8A1A2BCCCB3D0A1A2B6E0CCACBACDBEDBBACF2E707074> 程 序 设 计 实 习 INFO130048 3-2.C++ 面 向 对 象 程 序 设 计 重 载 继 承 多 态 和 聚 合 复 旦 大 学 计 算 机 科 学 与 工 程 系 彭 鑫 pengxin@fudan.edu.cn 内 容 摘 要 方 法 重 载 类 的 继 承 对 象 引 用 和 拷 贝 构 造 函 数 虚 函 数 和 多 态 性 类 的 聚 集 复 旦 大 学 计 算 机 科 学

More information

Spark读取Hbase中的数据

Spark读取Hbase中的数据 Spark 读取 Hbase 中的数据 Spark 和 Flume-ng 整合, 可以参见本博客 : Spark 和 Flume-ng 整合 使用 Spark 读取 HBase 中的数据 如果想及时了解 Spark Hadoop 或者 Hbase 相关的文章, 欢迎关注微信公共帐号 :iteblog_hadoop 大家可能都知道很熟悉 Spark 的两种常见的数据读取方式 ( 存放到 RDD 中 ):(1)

More information

<4D6963726F736F667420576F7264202D20BBF9D3DA416E64726F6964C6BDCCA8B5C4B5E7D7D3C5C4C2F4CFB5CDB32E646F63>

<4D6963726F736F667420576F7264202D20BBF9D3DA416E64726F6964C6BDCCA8B5C4B5E7D7D3C5C4C2F4CFB5CDB32E646F63> 基 于 Android 平 台 的 电 子 拍 卖 系 统 摘 要 本 电 子 拍 卖 系 统 其 实 就 是 一 个 电 子 商 务 平 台, 只 要 将 该 系 统 部 署 到 互 联 网 上, 客 户 都 可 以 在 该 系 统 上 发 布 想 出 售 的 商 品, 也 可 以 对 拍 卖 中 的 商 品 参 与 竞 价 整 个 过 程 无 须 人 工 干 预, 由 系 统 自 动 完 成 本

More information

Android 编程基础 Android 开发教程 & 笔记 1

Android 编程基础 Android 开发教程 & 笔记 1 Android 开发教程 & 笔记 1 多式样 ProgressBar 撰写 : 地狱怒兽 联系 :zyf19870302@126.com 普通圆形 ProgressBar 该类型进度条也就是一个表示运转的过程, 例如发送短信, 连接网络等等, 表示一个过程正 在执行中 一般只要在 XML 布局中定义就可以了

More information

untitled

untitled 1 Outline 數 料 數 數 列 亂數 練 數 數 數 來 數 數 來 數 料 利 料 來 數 A-Z a-z _ () 不 數 0-9 數 不 數 SCHOOL School school 數 讀 school_name schoolname 易 不 C# my name 7_eleven B&Q new C# (1) public protected private params override

More information

詞 彙 表 編 號 詞 彙 描 述 1 預 約 人 資 料 中 文 姓 名 英 文 姓 名 身 份 證 字 號 預 約 人 電 話 性 別 2 付 款 資 料 信 用 卡 別 信 用 卡 號 信 用 卡 有 效 日 期 3 住 房 條 件 入 住 日 期 退 房 日 期 人 數 房 間 數 量 入

詞 彙 表 編 號 詞 彙 描 述 1 預 約 人 資 料 中 文 姓 名 英 文 姓 名 身 份 證 字 號 預 約 人 電 話 性 別 2 付 款 資 料 信 用 卡 別 信 用 卡 號 信 用 卡 有 效 日 期 3 住 房 條 件 入 住 日 期 退 房 日 期 人 數 房 間 數 量 入 100 年 特 種 考 試 地 方 政 府 公 務 人 員 考 試 試 題 等 別 : 三 等 考 試 類 科 : 資 訊 處 理 科 目 : 系 統 分 析 與 設 計 一 請 參 考 下 列 旅 館 管 理 系 統 的 使 用 案 例 圖 (Use Case Diagram) 撰 寫 預 約 房 間 的 使 用 案 例 規 格 書 (Use Case Specification), 繪 出 入

More information

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0,

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0, http://debut.cis.nctu.edu.tw/~chi Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0, : POSITIVE_INFINITY NEGATIVE_INFINITY

More information

《大话设计模式》第一章

《大话设计模式》第一章 第 1 章 代 码 无 错 就 是 优? 简 单 工 厂 模 式 1.1 面 试 受 挫 小 菜 今 年 计 算 机 专 业 大 四 了, 学 了 不 少 软 件 开 发 方 面 的 东 西, 也 学 着 编 了 些 小 程 序, 踌 躇 满 志, 一 心 要 找 一 个 好 单 位 当 投 递 了 无 数 份 简 历 后, 终 于 收 到 了 一 个 单 位 的 面 试 通 知, 小 菜 欣 喜

More information

Dynamic Layout in Android

Dynamic Layout in Android Dynamic Layout in Android 建國科技大學資管系 饒瑞佶 2013/5 V1 Layout 多半都透過 res/layout/xml 格式設定來達成 Android 是 OOP, 所以可以動態產生 Layout 重點是 Layout 的階層關係 (Hierarchy) 需要處理對應事件 最後一樣用 setcontentview 加入 Layout 一 加入現有 Layout 中

More information

OOP with Java 通知 Project 3: 3 月 29 日晚 9 点 4 月 1 日上课

OOP with Java 通知 Project 3: 3 月 29 日晚 9 点 4 月 1 日上课 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 3: 3 月 29 日晚 9 点 4 月 1 日上课 复习 Java 包 创建包 : package 语句, 包结构与目录结构一致 使用包 : import restaurant/ - people/ - Cook.class - Waiter.class - tools/ - Fork.class

More information

无类继承.key

无类继承.key 无类继承 JavaScript 面向对象的根基 周爱 民 / aimingoo aiming@gmail.com https://aimingoo.github.io https://github.com/aimingoo rand = new Person("Rand McKinnon",... https://docs.oracle.com/cd/e19957-01/816-6408-10/object.htm#1193255

More information

目錄

目錄 資 訊 素 養 線 上 教 材 單 元 五 資 料 庫 概 論 及 Access 5.1 資 料 庫 概 論 5.1.1 為 什 麼 需 要 資 料 庫? 日 常 生 活 裡 我 們 常 常 需 要 記 錄 一 些 事 物, 以 便 有 朝 一 日 所 記 錄 的 事 物 能 夠 派 得 上 用 場 我 們 能 藉 由 記 錄 每 天 的 生 活 開 銷, 就 可 以 在 每 個 月 的 月 底 知

More information

untitled

untitled 1 .NET 料.NET 料 料來 類.NET Data Provider SQL.NET Data Provider System.Data.SqlClient 料 MS-SQL OLE DB.NET Data Provider System.Data.OleDb 料 Dbase FoxPro Excel Access Oracle Access ODBC.NET Data Provider 料

More information

59 1 CSpace 2 CSpace CSpace URL CSpace 1 CSpace URL 2 Lucene 3 ID 4 ID Web 1. 2 CSpace LireSolr 3 LireSolr 3 Web LireSolr ID

59 1 CSpace 2 CSpace CSpace URL CSpace 1 CSpace URL 2 Lucene 3 ID 4 ID Web 1. 2 CSpace LireSolr 3 LireSolr 3 Web LireSolr ID 58 2016. 14 * LireSolr LireSolr CEDD Ajax CSpace LireSolr CEDD Abstract In order to offer better image support services it is necessary to extend the image retrieval function of our institutional repository.

More information

EJB-Programming-3.PDF

EJB-Programming-3.PDF :, JBuilder EJB 2.x CMP EJB Relationships JBuilder EJB Test Client EJB EJB Seminar CMP Entity Beans Value Object Design Pattern J2EE Design Patterns Value Object Value Object Factory J2EE EJB Test Client

More information

LEFT, RIGHT // 左 // 右 (2) 当图片移动后, 按钮的坐标发生改变, 此操作通过 setloca tion() 方法实现 setlocation() 方法是从 Component 类继承的, 其定义如下 : public void setlocation(int x, int y

LEFT, RIGHT // 左 // 右 (2) 当图片移动后, 按钮的坐标发生改变, 此操作通过 setloca tion() 方法实现 setlocation() 方法是从 Component 类继承的, 其定义如下 : public void setlocation(int x, int y 拼图游戏 任务说明 本实例实现了拼图游戏的开发 运行程序, 单击 开始 按钮将打乱图片的位置, 效果如图 1 所示, 然后通过鼠标单击图片进行移动, 直到将所有图片都移动到正确位置, 游戏过关, 过关后的效果如图 2 所示 图 1 打乱图片位置的效果图 2 图片移动到正确位置的效果 关键技术 本程序主要通过 Swing 与枚举类实现, 程序将一幅完整的图片平均分成 9 部分, 每一部分为一个正方形,

More information

2 WF 1 T I P WF WF WF WF WF WF WF WF 2.1 WF WF WF WF WF WF

2 WF 1 T I P WF WF WF WF WF WF WF WF 2.1 WF WF WF WF WF WF Chapter 2 WF 2.1 WF 2.2 2. XAML 2. 2 WF 1 T I P WF WF WF WF WF WF WF WF 2.1 WF WF WF WF WF WF WF WF WF WF EDI API WF Visual Studio Designer 1 2.1 WF Windows Workflow Foundation 2 WF 1 WF Domain-Specific

More information

1: public class MyOutputStream implements AutoCloseable { 3: public void close() throws IOException { 4: throw new IOException(); 5: } 6:

1: public class MyOutputStream implements AutoCloseable { 3: public void close() throws IOException { 4: throw new IOException(); 5: } 6: Chapter 15. Suppressed Exception CH14 Finally Block Java SE 7 try-with-resources JVM cleanup try-with-resources JVM cleanup cleanup Java SE 7 Throwable getsuppressed Throwable[] getsuppressed() Suppressed

More information

FileMaker 16 ODBC 和 JDBC 指南

FileMaker 16 ODBC 和 JDBC 指南 FileMaker 16 ODBC JDBC 2004-2017 FileMaker, Inc. FileMaker, Inc. 5201 Patrick Henry Drive Santa Clara, California 95054 FileMaker FileMaker Go FileMaker, Inc. FileMaker WebDirect FileMaker Cloud FileMaker,

More information

1. 2. Flex Adobe 3.

1. 2. Flex Adobe 3. 1. 2. Flex Adobe 3. Flex Adobe Flex Flex Web Flex Flex Flex Adobe Flash Player 9 /rich Internet applications/ria Flex 1. 2. 3. 4. 5. 6. SWF Flash Player Flex 1. Flex framework Adobe Flex 2 framework RIA

More information

untitled

untitled ArcGIS Server Web services Web services Application Web services Web Catalog ArcGIS Server Web services 6-2 Web services? Internet (SOAP) :, : Credit card authentication, shopping carts GIS:, locator services,

More information

云数据库 RDS SDK

云数据库 RDS SDK 云数据库 RDS SDK SDK SDK 下载 SDK 下载 最新版本 java_sdk.zip python_sdk.zip php_sdk.zip c#_sdk.zip 历史版本 2015-11-3 java_sdk.zip python_sdk.zip php_sdk.zip c#_sdk.zip JAVA 教程 JAVA 创建 Access Key 登陆阿里云账号 打开 我的 Access

More information

Lecture01_Android介绍

Lecture01_Android介绍 移动平台应用软件开发 Android 介绍 主讲 : 张齐勋 zhangqx@ss.pku.edu.cn 移动平台应用软件开发 课程建设小组 北京大学 二零一七年秋北京 Android是什么 Android不仅仅是一个操作系统 它更是一个完整的软件框 架 Android基于Linux内核 2005年Google公司收购了Android公司 Google公司选择使用Apache许可证开放Android源码

More information

三种方法实现Hadoop(MapReduce)全局排序(1)

三种方法实现Hadoop(MapReduce)全局排序(1) 三种方法实现 Hadoop(MapReduce) 全局排序 () 三种方法实现 Hadoop(MapReduce) 全局排序 () 我们可能会有些需求要求 MapReduce 的输出全局有序, 这里说的有序是指 Key 全局有序 但是我们知道,MapReduce 默认只是保证同一个分区内的 Key 是有序的, 但是不保证全局有序 基于此, 本文提供三种方法来对 MapReduce 的输出进行全局排序

More information

建模与图形思考

建模与图形思考 C03_c 基 於 軟 硬 整 合 觀 點 JNI: 从 C 调 用 Java 函 数 ( c) By 高 煥 堂 3 How-to: 基 於 軟 硬 整 合 觀 點 从 C 调 用 Java 函 数 如 果 控 制 点 摆 在 本 地 C 层, 就 会 常 常 1. 从 本 地 C 函 数 去 调 用 Java 函 数 ; 2. 从 本 地 C 函 数 去 存 取 Java 层 对 象 的 属 性

More information

SQL Server SQL Server SQL Mail Windows NT

SQL Server SQL Server SQL Mail Windows NT ... 3 11 SQL Server... 4 11.1... 7 11.2... 9 11.3... 11 11.4... 30 11.5 SQL Server... 30 11.6... 31 11.7... 32 12 SQL Mail... 33 12.1Windows NT... 33 12.2SQL Mail... 34 12.3SQL Mail... 34 12.4 Microsoft

More information

季刊9web.indd

季刊9web.indd 在 全 国 现 场 会 上 成 功 展 示 全 国 烟 叶 收 购 暨 现 代 烟 草 农 业 建 设 现 场 会 7 月 6 日 至 8 日 在 昆 明 召 开 在 国 家 局 的 领 导 下, 由 我 司 技 术 开 发 的 烟 站 ( 单 元 ) 烟 叶 管 理 信 息 系 统 在 现 场 会 上 成 功 展 示, 并 得 到 参 会 领 导 及 代 表 们 的 关 注 与 认 可 该 系 统

More information

概述

概述 OPC Version 1.6 build 0910 KOSRDK Knight OPC Server Rapid Development Toolkits Knight Workgroup, eehoo Technology 2002-9 OPC 1...4 2 API...5 2.1...5 2.2...5 2.2.1 KOS_Init...5 2.2.2 KOS_InitB...5 2.2.3

More information

建立Android新專案

建立Android新專案 Android 智慧型手機程式設計 程式設計與應用班 Android 資料庫處理 建國科技大學資管系饒瑞佶 2012/4 V1 2012/8 V2 Android 資料庫 -SQLite 資料庫 SQLite 檔案式資料庫 適合嵌入式系統, 不需要額外安裝系統 OPEN SOURCE 資料庫 SQL 指令與一般 DBMS 大同小異, 但有些微差異 SQLite Android 結構 1 資料 結構

More information

目 錄 版 次 變 更 記 錄... 2 原 始 程 式 碼 類 型 之 使 用 手 冊... 3 一 安 裝 軟 體 套 件 事 前 準 備... 3 二 編 譯 流 程 說 明... 25 1

目 錄 版 次 變 更 記 錄... 2 原 始 程 式 碼 類 型 之 使 用 手 冊... 3 一 安 裝 軟 體 套 件 事 前 準 備... 3 二 編 譯 流 程 說 明... 25 1 科 技 部 自 由 軟 體 專 案 原 始 程 式 碼 使 用 手 冊 Source Code Manual of NSC Open Source Project 可 信 賴 的 App 安 全 應 用 框 架 -App 應 用 服 務 可 移 轉 性 驗 證 Trusted App Framework -Transferability Verification on App MOST 102-2218-E-011-012

More information

Microsoft Word - Broker.doc

Microsoft Word - Broker.doc Broker 模式 采用 broker 模式对分布式计算进行简单模拟 系统在一个进程内模拟分布式环境, 因此不涉及网络编程和进程间通信,Broker 通过本地函数调用的方式实现 request 和 response 的转发 采用 broker 模式对分布式计算进行简单的模拟, 要求如下 : 设计四个 server, 一个 server 接收两个整数, 求和并返回结果, 一个 server 接收两个整数,

More information

WebSphere Studio Application Developer IBM Portal Toolkit... 2/21 1. WebSphere Portal Portal WebSphere Application Server stopserver.bat -configfile..

WebSphere Studio Application Developer IBM Portal Toolkit... 2/21 1. WebSphere Portal Portal WebSphere Application Server stopserver.bat -configfile.. WebSphere Studio Application Developer IBM Portal Toolkit... 1/21 WebSphere Studio Application Developer IBM Portal Toolkit Portlet Doug Phillips (dougep@us.ibm.com),, IBM Developer Technical Support Center

More information

RxJava

RxJava RxJava By 侦跃 & @hi 头 hi RxJava 扩展的观察者模式 处 观察者模式 Observable 发出事件 Subscriber 订阅事件 bus.post(new AnswerEvent(42)); @Subscribe public void onanswer(answerevent event) {! }! Observable observable = Observable.create(new

More information

Flume-ng与Mysql整合开发

Flume-ng与Mysql整合开发 Flume-ng 与 Mysql 整合开发 我们知道,Flume 可以和许多的系统进行整合, 包括了 Hadoop Spark Kafka Hbase 等等 ; 当然, 强悍的 Flume 也是可以和 Mysql 进行整合, 将分析好的日志存储到 Mysql( 当然, 你也可以存放到 pg oracle 等等关系型数据库 ) 不过我这里想多说一些 :Flume 是分布式收集日志的系统 ; 既然都分布式了,

More information

Oracle 4

Oracle 4 Oracle 4 01 04 Oracle 07 Oracle Oracle Instance Oracle Instance Oracle Instance Oracle Database Oracle Database Instance Parameter File Pfile Instance Instance Instance Instance Oracle Instance System

More information

OOP with Java 通知 Project 3 提交时间 3 月 29 日晚 9 点 Piazza Project 2 投票

OOP with Java 通知 Project 3 提交时间 3 月 29 日晚 9 点 Piazza Project 2 投票 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 3 提交时间 3 月 29 日晚 9 点 Piazza Project 2 投票 复习 创建对象 构造函数 函数重载 : 函数 = 函数名 + 参数列表 public class MyType { int i; double d; char c; void set(double x)

More information

Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7.

Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7. Kubernetes 包管理理 工具 Helm 蔺礼强 Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7. Kubernetes

More information

Android + NFC

Android + NFC Android + NFC 建國科技大學資管系饒瑞佶 2017/3 v1 讀取 Tag UUID Android 2.3.3 (API Level 10) 才有支援完整的 NFC 功能 只要 NFC 相容都讀的到 (NFC 或 Mifare) 建立新專案修改 AndroidManifest.xml 加入 , 如果有 NFC Tag 進入感測範圍, 本 App 也會變成可處理的

More information

回滚段探究

回滚段探究 oracle oracle internal DBA oracle document oracle concepts oracle document oracle DBWR update t set object_id = '0' where object_id = '12344'; 1 row updated. commit; Commit complete. 0 12344 12344 0 10%

More information

Microsoft Word - PHP7Ch01.docx

Microsoft Word - PHP7Ch01.docx PHP 01 1-6 PHP PHP HTML HTML PHP CSSJavaScript PHP PHP 1-6-1 PHP HTML PHP HTML 1. Notepad++ \ch01\hello.php 01: 02: 03: 04: 05: PHP 06:

More information

长 安 大 学 硕 士 学 位 论 文 基 于 数 据 仓 库 和 数 据 挖 掘 的 行 为 分 析 研 究 姓 名 : 杨 雅 薇 申 请 学 位 级 别 : 硕 士 专 业 : 计 算 机 软 件 与 理 论 指 导 教 师 : 张 卫 钢 20100530 长安大学硕士学位论文 3 1 3系统架构设计 行为分析数据仓库的应用模型由四部分组成 如图3 3所示

More information

sql> startup mount 改变数据库的归档模式 sql> alter database archivelog # 打开数据库 sql> alter database open 禁止归档模式 sql> shutdown immediate sql>startup mount sql> al

sql> startup mount 改变数据库的归档模式 sql> alter database archivelog # 打开数据库 sql> alter database open 禁止归档模式 sql> shutdown immediate sql>startup mount sql> al RMAN sql> sqlplus / as sysdba 查看数据库版本 sql> select * from v$version; 查看数据库名称 sql> show parameter db_name; 一 使用 RMAN 时, 需要将数据库设置成归档模式 sql> conn / as sysdba; sql> show user 查看数据库是否为归档模式 sql> archive log list

More information

1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10

1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10 Java V1.0.1 2007 4 10 1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10 6.2.10 6.3..10 6.4 11 7.12 7.1

More information

Microsoft Word zw

Microsoft Word zw 第 1 章 Android 概述 学习目标 : Android Android Android Studio Android Android APK 1.1 1. 智能手机的定义 Smartphone 2. 智能手机的发展 1973 4 3 PC IBM 1994 IBM Simon PDA PDA Zaurus OS 1996 Nokia 9000 Communicator Nokia 9000

More information

untitled

untitled 1 行 行 行 行.NET 行 行 類 來 行 行 Thread 類 行 System.Threading 來 類 Thread 類 (1) public Thread(ThreadStart start ); Name 行 IsAlive 行 行狀 Start 行 行 Suspend 行 Resume 行 行 Thread 類 (2) Sleep 行 CurrentThread 行 ThreadStart

More information

基于UML建模的管理管理信息系统项目案例导航——VB篇

基于UML建模的管理管理信息系统项目案例导航——VB篇 PowerBuilder 8.0 PowerBuilder 8.0 12 PowerBuilder 8.0 PowerScript PowerBuilder CIP PowerBuilder 8.0 /. 2004 21 ISBN 7-03-014600-X.P.. -,PowerBuilder 8.0 - -.TP311.56 CIP 2004 117494 / / 16 100717 http://www.sciencep.com

More information

全国计算机技术与软件专业技术资格(水平)考试

全国计算机技术与软件专业技术资格(水平)考试 全 国 计 算 机 技 术 与 软 件 专 业 技 术 资 格 ( 水 平 ) 考 试 2008 年 上 半 年 程 序 员 下 午 试 卷 ( 考 试 时 间 14:00~16:30 共 150 分 钟 ) 试 题 一 ( 共 15 分 ) 阅 读 以 下 说 明 和 流 程 图, 填 补 流 程 图 中 的 空 缺 (1)~(9), 将 解 答 填 入 答 题 纸 的 对 应 栏 内 [ 说 明

More information

2. AOP 底层技术实现 小风 Java 实战系列教程 关键词 : 代理模式 代理模型分为两种 : 1) 接口代理 (JDK 动态代理 ) 2) 子类代理 (Cglib 子类代理 ) 需求 :CustomerService 业务类, 有 save,update 方法, 希望在 save,updat

2. AOP 底层技术实现 小风 Java 实战系列教程 关键词 : 代理模式 代理模型分为两种 : 1) 接口代理 (JDK 动态代理 ) 2) 子类代理 (Cglib 子类代理 ) 需求 :CustomerService 业务类, 有 save,update 方法, 希望在 save,updat 本章学习目标 小风 Java 实战系列教程 AOP 思想概述 AOP 底层技术实现 AOP 术语介绍 SpringAOP 的 XML 方式 HelloWorld SpringAOP 的 XML 方式配置细节 SpringAOP 的注解方式 SpringAOP 的零配置方式 1. AOP 思想概述 1.1. AOP 思想简介 1.2. AOP 的作用 2. AOP 底层技术实现 小风 Java 实战系列教程

More information

Lecture01_Android介绍

Lecture01_Android介绍 移动平台应用软件开发 Android 介绍 主讲 : 张齐勋 zhangqx@ss.pku.edu.cn 移动平台应用软件开发 课程建设小组 北京大学 二零一八年秋北京 Android 是什么 Android 不仅仅是一个操作系统, 它更是一个完整的软件框架 Android 基于 Linux 内核 2005 年 Google 公司收购了 Android 公司 Google 公司选择使用 Apache

More information