3G应用开发之Android 传智播客 基础知识总结

3G应用开发之Android

Android应用开发之3G
 3G应用开发之Android 3G应用开发之Android 应用开发之 讲师: 讲师:黎活明 北京传智

播客教育 www.itcast.cn  

    什么是3G 3G,全称为3rd Generation,中文含义就是指第三代数字通信。 所谓3G,是

指将无线通信与国际互联网等多媒体通信结合的新一代移动通 信系统。 3G只是一种通信技术

标准,符合这个标准的技术有WCDMA、 CDMA2000、TD-SCDMA三种制式。中国联通使用的是

WCDMA(世界上 大部分3G网络都采用的是该标准) ;中国电信使用的是CDMA2000 (日、 韩

和北美使用);中国移动使用的是具有自主知识产权的TD-SCDMA(只 TD-SCDMA 有中国才使

用) 。相对第一代模拟制式手机(1G)和第二代GSM、CDMA等 数字手机(2G),3G网络能处理图

像、音乐、视频等多种媒体形式,提供包 括网页浏览、电话会议、电子商务等多种信息服务

。第三代与前两代的主要 区别是在传输声音和数据的速度上有很大的提升。 由于3G商用需要

相当浩大的工程,要从目前的2G迈向3G不可能一下就衔接 得上,因此前几年2.5G的手机就出

现了。符合2.5G标准的技术有 CDMA2000 1X和GPRS,中国联通使用的是CDMA2000 1X标准,中

国移动 使用的是GPRS标准。目前,我们可以把2.5G移动通信技术看作是2G迈向 3G的衔接性技

术,在2.5G网络下出现了如WAP、蓝牙(Bluetoot) 等技术。
    智能手机软件平台智能手机软件平台有: Symbian, Windows Mobile, RIM

BlackBerry, Android, iPhone, Palm, Brew, Java/J2ME。 2009年市场份额: Symbian

51% RIM BlackBerry 18% iPhone 13.3 windows Mobile 9.3% linux 4.6% Android 1.8%
    什么是Android Android一词的本义指“机器人”,Android是 Google于07年11月5日宣

布的基于Linux平台开 源手机操作系统名称,该平台由操作系统、中间 件、用户界面和应用

软件组成。在国内,联想、 戴尔、多普达、飞利浦、中兴、三星、摩托罗拉、 等厂商已经推

出基于Android平台的智能手机。 在3G应用开发领域,Android的发展前景不容小 视,也将

会成为未来主流的手机操作系统。
    如何安装 Android SDK 和Eclipse 插件所需开发环境: 所需开发环境 JDK 5 或 JDK 6

(仅有JRE不够) Eclipse 3.5 (galileo) 下载ADT 的Eclipse 插件 下载

http://dl.google.com/android/ADT-0.9.5.zip 安装 Eclipse 插件 (ADT) 启动 Eclipse

,选择 Help > Install New Software,在出现的对话框里,点击Add按钮,在对话框 的

name一栏输入“ADT”, 然后点击Archive...,浏览和选择已经下载的ADT插件 插件压缩文

件。 插件 点击 OK.。返回可用软件的视图,你会看到这个插件,然后选择Developer Tools

(会选中下面的 “Android Developer Tools”和 “Android Editors“),点击 Next,最

后重启 Eclipse。 下载Android SDK: 下载 http://dl.google.com/android/android-

sdk_r04-windows.zip 下载完SDK后,把.zip文件解压到你电脑上合适位置。启动 Eclipse,

选择window->preferences, 在打开的视图左边点击android,在右边的SDK Location中选

择Android SDK所在位置。
    开发第一个Android应用打开Eclipse,新建项目(点击File New Project),在项目列表

中展开Android目录,选择Android Project,......点击”finish”即可完成项目的创建,

创建后的项目已经是一个可运行 的Android应用,我们可以通过下面方式运行此应用: 点击

工具栏上手机形状的虚拟设备管理器(简称“AVD“),如下:  在打开的虚拟设备管理器中

创建一个虚拟手机: 在项目上右键点击run as Android application,Android应用程序架

构 src/ java原代码存放目录 原代码存放目录 gen/ 自动生成目录 gen 目录中存放所有由

Android开发工具自动生成的文件。目录中最重要的就是R.java文件。 这个 文件由Android

开发工具自动产生的。Android开发工具会自动根据你放入res目录的xml界面文件、 图标与常

量,同步更新修改R.java文件。正因为R.java文件是由开发工具自动生成的,所以我们应 避

免手工修改R.java。R.java在应用中起到了字典的作用,它包含了界面、图标、常量等各种资

源 的id,通过R.java,应用可以很方便地找到对应资源。另外编绎器也会检查R.java列表中

的资源是 否被使用到,没有被使用到的资源不会编绎进软件中,这样可以减少应用在手机占

用的空间。 res/ 资源 资源(Resource)目录 目录 在这个目录中我们可以存放应用使用到的

各种资源,如xml界面文件,图片或数据。具体请看ppt 下方备注栏。 AndroidManifest.xml

功能清单文件 这个文件列出了应用程序所提供的功能,在这个文件中,你可以指定应用程序

使用到的服务(如电 话服务、互联网服务、短信服务、GPS服务等等)。另外当你新添加一个

Activity的时候,也需要在 这个文件中进行相应配置,只有配置好后,才能调用此Activity

。 default.properties 项目环境信息,一般是不需要修改此文件 项目环境信息 电话拔号

器效果图: 电话拔号器因为应用要使用手机的电话服务,所以要在清单文件

AndroidManifest.xml中添加电话服务权限:
<?xml version="1.0" encoding="utf-8"?> <manifest

xmlns:android="http://schemas.android.com/apk/res/android"

package="cn.itcast.action" android:versionCode="1" android:versionName="1.0">

略.... <uses-sdk android:minSdkVersion=“6" /> <uses-permission

android:name="android.permission.CALL_PHONE"/> </manifest>

电话拔号器界面布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout

xmlns:android="http://schemas.android.com/apk/res/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="@string/inputmobile"/> <EditText

android:layout_width="fill_parent" android:layout_height="wrap_content"

android:id="@+id/mobile"/> <Button android:layout_width="wrap_content"

android:layout_height="wrap_content" android:text="@string/button"

android:id="@+id/button"/> </LinearLayout>

LinearLayout (线性布局)、AbsoluteLayout(绝对布局)、RelativeLayout(相对布局)、

TableLayout(表格布局)、FrameLayout(帧布局)
 电话拔号器 Activity: public class DialerAction extends Activity { @Override

public void onCreate(Bundle savedInstanceState) { super.onCreate

(savedInstanceState); setContentView(R.layout.main); Button button = (Button)

findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener

(){ public void onClick(View v) { EditText editText = (EditText)findViewById

(R.id.mobile); Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse

("tel:"+ editText.getText())); DialerAction.this.startActivity(intent); } });

} }
电话拔号器测试步骤: 1>在Eclipse中运行此应用 2>在Dos窗口中进入android SDK安装路径

的tools目录,输入以下命令再开启一个Android模拟器: emulator -data itcast 注:

itcast为用户数据存取文件,如果该文件不存在,默认在tools目录创建该文件 3>在电话扰号

器中输入上图现显的电话号码
短信发送器效果图: 短信发送器因为应用要使用手机的短信服务,所以要在清单文件

AndroidManifest.xml中添加短信服务权限:
<?xml version="1.0" encoding="utf-8"?> <manifest

xmlns:android="http://schemas.android.com/apk/res/android"

package="cn.itcast.sms" android:versionCode="1" android:versionName="1.0">

略.... <uses-sdk android:minSdkVersion=“4" /> <uses-permission

android:name="android.permission.SEND_SMS"/> </manifest>
 
短信发送器界面布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout

xmlns:android="http://schemas.android.com/apk/res/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="@string/inputmobile"/> <EditText

android:layout_width="fill_parent" android:layout_height="wrap_content"

android:id="@+id/mobile"/> <TextView android:layout_width="fill_parent"

android:layout_height="wrap_content" android:text="@string/content"/>

<EditText android:layout_width="fill_parent"

android:layout_height="wrap_content" android:minLines="3"

android:id="@+id/content"/> <Button android:layout_width="wrap_content"

android:layout_height="wrap_content" android:text="@string/button"

android:id="@+id/button"/> </LinearLayout>

短信发送器 Activity主要代码: String mobile = mobileView.getText().toString();

String content = contentView.getText().toString(); SmsManager smsManager =

SmsManager.getDefault(); PendingIntent sentIntent =

PendingIntent.getBroadcast(SMSSender.this, 0, new Intent(), 0); if

(content.length()>70){//如果字数超过70,需拆分成多条短信发送 List<String> msgs =

smsManager.divideMessage(content); for(String msg : msgs){

smsManager.sendTextMessage(mobile, null, msg, sentIntent, null); //最后二个参数

为短信已发送的广播意图,最后一个参数为短信对方已收到短信的广播意图 } }else{

smsManager.sendTextMessage(mobile, null, content, sentIntent, null); }

Toast.makeText(SMSSender.this, "短信发送完成", Toast.LENGTH_LONG).show();

短信发送器测试步骤: 1>在Eclipse中运行此应用 2>在Dos窗口中进入android SDK安装路径

的tools目录,输入以下命令再开启一个Android模拟器: emulator -data itcast 注:

itcast为用户数据存取文件,如果该文件不存在,默认在tools目录创建该文件 3>在短信发送

器的手机号中输入上图现显的电话号码 注:目前Android系统对中文短信尚未支持,所以发送

中文短信会有乱码,这个问题日后会被解决的。
    数据存储与访问很多时候我们的软件需要对处理后的数据进行存储或再次访问。 Android

为数据存储提供了多种方式,分别有如下几种: 文件 SharedPreferences SQLite数据库 内

容提供者(Content provider) 网络 使用文件进行数据存储首先给大家介绍使用文件如何

对数据进行存储,Activity提供了openFileOutput()方法可以用于把数 据输出到文件中,具

体的实现过程与在J2SE环境中保存数据到文件中是一样的。
 public class FileActivity extends Activity { @Override public void onCreate

(Bundle savedInstanceState) { ... FileOutputStream outStream =

this.openFileOutput("itcast.txt", Context.MODE_PRIVATE); outStream.write("传智

播客".getBytes()); outStream.close(); } } openFileOutput()

方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在, Android

会自动创建它。创建的文件保存在/data/data/<package name>/files目录,如:

/data/data/cn.itcast.action/files/itcast.txt ,通过点击Eclipse菜单“Window”-“

Show View”-“Other”, 在对话窗口中展开android文件夹,选择下面的File Explorer视

图,然后在File Explorer视图中展开 /data/data/<package name>/files目录就可以看到

该文件。

openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为:

Context.MODE_PRIVATE = 0 Context.MODE_APPEND = 32768

Context.MODE_WORLD_READABLE = 1 Context.MODE_WORLD_WRITEABLE = 2  使用文件进行

数据存储 Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本

身访问,在 该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文

件中。可以使用 Context.MODE_APPEND Context.MODE_APPEND:模式会检查文件是否存在,

存在就往文件追加内容,否则就创建新文 件。 Context.MODE_WORLD_READABLE和

Context.MODE_WORLD_WRITEABLE用来控制其他应 用是否有权限读写该文件。

MODE_WORLD_READABLE:表示当前文件可以被其他应用读取; MODE_WORLD_WRITEABLE:表示

当前文件可以被其他应用写入。 如果希望文件被其他应用读和写,可以传入:

openFileOutput("itcast.txt", Context.MODE_WORLD_READABLE +

Context.MODE_WORLD_WRITEABLE); android有一套自己的安全模型,当应用程序(.apk)在安

装时系统就会分配给他一个userid,当该应 用要去访问其他资源比如文件的时候,就需要

userid匹配。默认情况下,任何应用创建的文件, sharedpreferences,数据库都应该是私

有的(位于/data/data/<package name>/files),其他程序 无法访问。除非在创建时指定

了Context.MODE_WORLD_READABLE或者 Context.MODE_WORLD_WRITEABLE ,只有这样其他程

序才能正确访问。
    读取文件内容如果要打开存放在/data/data/<package name>/files目录应用私有的文件

,可以使用Activity提供 openFileInput()方法。 FileInputStream inStream =

this.getContext().openFileInput("itcast.txt"); Log.i("FileTest", readInStream

(inStream)); readInStream()的方法请看本页下面备注。 或者直接使用文件的绝对路径:

File file = new File("/data/data/cn.itcast.action/files/itcast.txt");

FileInputStream inStream = new FileInputStream(file); Log.i("FileTest",

readInStream(inStream)); 注意:上面文件路径中的“cn.itcast.action”为应用所在包

,当你在编写代码时应替换为你自己应用 使用的包。 对于私有文件只能被创建该文件的应用

访问,如果希望文件能被其他应用读和写,可以在创建文 件时,指定

Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。 Activity还提供

了getCacheDir()和getFilesDir()方法: getCacheDir()方法用于获

取/data/data/<package name>/cache目录 getFilesDir()方法用于获

取/data/data/<package name>/files目录
    把文件存放在SDCard 使用Activity的openFileOutput()方法保存文件,文件是存放在手

机空间上,一般手机的存储空间不是很大, 存放些小文件还行,如果要存放像视频这样的大

文件,是不可行的。对于像视频这样的大文件,我们可 以把它存放在SDCard。 SDCard是干什

么的?你可以把它看作是移动硬盘或U盘。 在模拟器中使用SDCard,你需要先创建一张SDCard

卡(当然不是真的SDCard,只是镜像文件)。创建 SDCard可以在Eclipse创建模拟器时随同

创建,也可以使用DOS命令进行创建,如下: 在Dos窗口中进入android SDK安装路径的tools

目录,输入以下命令创建一张容量为2G的SDCard,文件 后缀可以随便取,建议使用.img:

mksdcard 2048M D:\AndroidTool\sdcard.img 在程序中访问SDCard,你需要申请访问

SDCard的权限。 ,你需要申请访问 的权限。 在程序中访问 的权限 在

AndroidManifest.xml中加入访问SDCard的权限如下:
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    把文件存放在SDCard 要往SDCard存放文件,程序必须先判断手机是否装有SDCard,并且

可以进行读写。
     注意:访问SDCard必须在AndroidManifest.xml中加入访问SDCard的权限
     if(Environment.getExternalStorageState().equals

(Environment.MEDIA_MOUNTED)){ File sdCardDir =

Environment.getExternalStorageDirectory();//获取SDCard目录 File saveFile = new

File(sdCardDir, “itcast.txt”); FileOutputStream outStream = new

FileOutputStream(saveFile); outStream.write("传智播客".getBytes());

outStream.close(); } Environment.getExternalStorageState()方法用于获取SDCard的

状态,如果手机装有SDCard,并 且可以进行读写,那么方法返回的状态等于

Environment.MEDIA_MOUNTED。 Environment.getExternalStorageDirectory()方法用于获

取SDCard的目录,当然要获取SDCard的 目录,你也可以这样写: File sdCardDir = new

File("/sdcard"); //获取SDCard目录 File saveFile = new File(sdCardDir,

"itcast.txt"); //上面两句代码可以合成一句: File saveFile = new File

("/sdcard/itcast.txt"); FileOutputStream outStream = new FileOutputStream

(saveFile); outStream.write("传智播客test".getBytes()); outStream.close();
     
    使用SAX或者DOM或者pull读取XML文件在Android平台上可以使用Simple API for XML

(SAX) 、 Document Object Model(DOM)和Android附带的 pull解析器解析XML文件。 下面

是本例子要解析的XML文件: 文件名称:itcast.xml <?xml version="1.0"

encoding="UTF-8"?> <persons> <person id="23"> <name>李明</name> <age>30</age>

</person> <person id="20"> <name>李向梅</name> <age>25</age> </person>

</persons> 例子定义了一个javabean用于存放上面解析出来的xml内容, 这个javabean为

Person,代码请见本页下面 备注:  使用SAX读取XML文件 SAX是一个解析速度快并且占用内

存少的xml解析器,非常适合用于Android等移动设备。 SAX解析XML文件 采用的是事件驱动

,也就是说,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,SAX会判断 当前

读到的字符是否合法XML语法中的某部分,如果符合就会触发事件。所谓事件,其实就是一些

回调 (callback)方法,这些方法(事件)定义在ContentHandler接口。下面是一些

ContentHandler接口常用的方法: startDocument() 当遇到文档的开头的时候,调用这个方

法,可以在其中做一些预处理的工作。 endDocument() 和上面的方法相对应,当文档结束的

时候,调用这个方法,可以在其中做一些善后的工作。 startElement(String

namespaceURI, String localName, String qName, Attributes atts) 当读到一个开始标

签的时候,会触发这个方法。namespaceURI就是命名空间,localName是不带命名空间 前缀

的标签名,qName是带命名空间前缀的标签名。通过atts可以得到所有的属性名和相应的值。

要注意的 是SAX中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会纪录

下以前所碰到的标签, 也就是说,在startElement()方法中,所有你所知道的信息,就是标

签的名字和属性,至于标签的嵌套结构, 上层标签的名字,是否有子元属等等其它与结构相

关的信息,都是不得而知的,都需要你的程序来完成。这 使得SAX在编程处理上没有DOM来得

那么方便。 endElement(String uri, String localName, String name) 这个方法和上面

的方法相对应,在遇到结束标签的时候,调用这个方法。 characters(char[] ch, int

start, int length) 这个方法用来处理在XML文件中读到的内容,第一个参数用于存放文件

的内容,后面两个参数是读到的字符 串在这个数组中的起始位置和长度,使用new String

(ch,start,length)就可以获取内容。
    使用SAX读取XML文件只要为SAX提供实现ContentHandler接口的类,那么该类就可以得到

通知事件(实际上就是SAX调用了该 类中的回调方法)。因为ContentHandler是一个接口,

在使用的时候可能会有些不方便,因此,SAX还为 其制定了一个Helper类:DefaultHandler

,它实现了这个接口,但是其所有的方法体都为空,在实现的时候, 你只需要继承这个类,

然后重载相应的方法即可。使用SAX解析itcast.xml的代码如下: public static

List<Person> readXML(InputStream inStream) { try { SAXParserFactory spf =

SAXParserFactory.newInstance(); SAXParser saxParser = spf.newSAXParser(); //创

建解析器 //设置解析器的相关特性,http://xml.org/sax/features/namespaces = true

表示开启命名空间特性 saxParser.setProperty

("http://xml.org/sax/features/namespaces",true); XMLContentHandler handler =

new XMLContentHandler(); saxParser.parse(inStream, handler); inStream.close();

return handler.getPersons(); } catch (Exception e) { e.printStackTrace(); }

return null; } SAX 支持已内置到JDK1.5中,你无需添加任何的jar文件。关于

XMLContentHandler的代码实现请看本页下 面备注。
   使用DOM读取XML文件除了可以使用 SAX解析XML文件,大家也可以使用熟悉的DOM来解析

XML文件。 DOM解析XML文件时, 会将XML文件的所有内容读取到内存中,然后允许您使用DOM

API遍历XML树、检索所需的数据。使用 DOM操作XML的代码看起来比较直观,并且,在某些方

面比基于SAX的实现更加简单。但是,因为DOM需 要将XML文件的所有内容读取到内存中,所以

内存的消耗比较大,特别对于运行Android的移动设备来说, 因为设备的资源比较宝贵,所以

建议还是采用SAX来解析XML文件,当然,如果XML文件的内容比较小采 用DOM是可行的。 代码

请看本页下方备注 北京传智播客教育 www.itcast.cn —高级软件人才实作培训专家! 高级

软件人才实作培训专家! 使用Pull解析器读取XML文件除了可以使用 SAX和DOM解析XML文件,

大家也可以使用Android内置的Pull解析器解析XML文件。 Pull解 析器的运行方式与 SAX 解

析器相似。它提供了类似的事件,如:开始元素和结束元素事件,使用 parser.next()可以进

入下一个元素并触发相应事件。事件将作为数值代码被发送,因此可以使用一个switch 对感

兴趣的事件进行处理。当元素开始解析时,调用parser.nextText()方法可以获取下一个Text

类型元素的 值。 使用Pull解析器读取itcast.xml的代码在本页下方备注 Pull解析器的源码

及文档下载网址:http://www.xmlpull.org/
   使用Pull解析器生成XML文件有些时候,我们需要生成一个XML文件,生成XML文件的方法有

很多,如:可以只使用一个StringBuilder 组拼XML内容,然后把内容写入到文件中;或者使

用DOM API生成XML文件,或者也可以使用pull解析器 生成XML文件,这里推荐大家使用Pull

解析器。 使用Pull解析器生成一个与itcast.xml文件内容相同的myitcast.xml文件,代码在

本页下方备注 使用代码如下(生成XML文件): File xmlFile = new File

("myitcast.xml"); FileOutputStream outStream = new FileOutputStream(xmlFile);

OutputStreamWriter outStreamWriter = new OutputStreamWriter(outStream, "UTF-

8"); BufferedWriter writer = new BufferedWriter(outStreamWriter); writeXML

(persons, writer); writer.flush(); writer.close(); 如果只想得到生成的xml内容,可

以使用StringWriter: StringWriter writer = new StringWriter(); writeXML

(persons, writer); String content = writer.toString();
    使用SharedPreferences进行数据存储很多时候我们开发的软件需要向用户提供软件参数

设置功能,例如我们常用的QQ,用户可以设置是否 允许陌生人添加自己为好友。对于软件配

置参数的保存,如果是window软件通常我们会采用ini文件进 行保存,如果是j2se应用,我们

会采用properties属性文件进行保存。如果是Android应用,我们最适合 采用什么方式保存

软件配置参数呢?Android平台给我们提供了一个SharedPreferences类,它是一个轻 量级的

存储类,特别适合用于保存软件配置参数。使用SharedPreferences保存数据,其背后是用

xml文 件存放数据,文件存放在/data/data/<package name>/shared_prefs目录下:

SharedPreferences sharedPreferences = getSharedPreferences("itcast",

Context.MODE_PRIVATE); Editor editor = sharedPreferences.edit();//获取编辑器

editor.putString("name", "传智播客"); editor.putInt("age", 4); editor.commit

();//提交修改 生成的itcast.xml文件内容如下: <?xml version='1.0' encoding='utf-

8' standalone='yes' ?> <map> <string name="name">传智播客</string> <int

name="age" value="4" /> </map> 因为SharedPreferences背后是使用xml文件保存数据,

getSharedPreferences(name,mode)方法的第一个参数用于指定 该文件的名称,名称不用带

后缀,后缀会由Android自动加上。方法的第二个参数指定文件的操作模式,共有四种操作模

式,这四种模式前面介绍使用文件方式保存数据时已经讲解过。如果希望SharedPreferences

背后使用的xml文件能被其 他应用读和写,可以指定Context.MODE_WORLD_READABLE和

Context.MODE_WORLD_WRITEABLE权限。 另外Activity还提供了另一个getPreferences

(mode)方法操作SharedPreferences,这个方法默认使用当前类不带包名的类 名作为文件的

名称。
    访问SharedPreferences中的数据访问SharedPreferences中的数据代码如下:

SharedPreferences sharedPreferences = getSharedPreferences("itcast",

Context.MODE_PRIVATE); //getString()第二个参数为缺省值,如果preference中不存在该

key,将返回缺省值 String name = sharedPreferences.getString("name", ""); int

age = sharedPreferences.getInt("age", 1); 如果访问其他应用中的Preference,前提条

件是:该preference创建时指定了 Context.MODE_WORLD_READABLE或者

Context.MODE_WORLD_WRITEABLE权限。如:有个 <package name>为cn.itcast.action的应

用使用下面语句创建了preference。 getSharedPreferences("itcast",

Context.MODE_WORLD_READABLE); 其他应用要访问上面应用的preference,首先需要创建上

面应用的Context,然后通过Context 访问 preference ,访问preference时会在应用所在

包下的shared_prefs目录找到preference : Context otherAppsContext =

createPackageContext("cn.itcast.action", Context.CONTEXT_IGNORE_SECURITY);

SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences

("itcast", Context.MODE_WORLD_READABLE); String name =

sharedPreferences.getString("name", ""); int age = sharedPreferences.getInt

("age", 0); 如果不通过创建Context访问其他应用的preference,可以以读取xml文件方式

直接访问其他应用preference对应的xml文 件,如: File xmlFile = new File

(“/data/data/<package name>/shared_prefs/itcast.xml”);//<package name>应替换

成应用的包名
    使用嵌入式关系型SQLite数据库存储数据除了可以使用文件或SharedPreferences存储数

据,还可以选择使用SQLite数据库存储数据。 在Android平台上,集成了一个嵌入式关系型数

据库—SQLite,SQLite3支持 NULL、INTEGER、REAL (浮点数字)、TEXT(字符串文本)和

BLOB(二进制对象)数据类型,虽然它支持的类型虽然只有五种,但实 际上sqlite3也接受

varchar(n)、char(n)、decimal(p,s) 等数据类型,只不过在运算或保存时会转成对应的五

种数据类型。 SQLite最大的特点是你可以保存任何类型的数据到任何字段中,无论这列声明

的数据类型是 什么。例如:可以在Integer字段中存放字符串,或者在布尔型字段中存放浮点

数,或者在字符型字段中存 放日期型值。 但有一种情况例外:定义为INTEGER PRIMARY KEY

的字段只能存储64位整数, 当向这种 字段中保存除整数以外的数据时,将会产生错误。 另

外, SQLite 在解析CREATE TABLE 语句时,会忽略 CREATE TABLE 语句中跟在字段名后面的

数据类型信息,如下面语句会忽略 name字段的类型信息: CREATE TABLE person (personid

integer primary key autoincrement, name varchar(20)) SQLite可以解析大部分标准

SQL语句,如:查询语句:select * from 表名 where 条件子句 group by 分组字句

having ... order by 排序子句 如:select * from person select * from person

order by id desc select name from person group by name having count(*)>1 分页

SQL与mysql类似,下面SQL语句获取5条记录,跳过前面3条记录 select * from Account

limit 5 offset 3 或者 select * from Account limit 3,5 插入语句:insert into 表

名(字段列表) values(值列表)。如: insert into person(name, age) values(‘传

智’,3) 更新语句:update 表名 set 字段名=值 where 条件子句。如:update person

set name=‘传智‘ where id=10 删除语句:delete from 表名 where 条件子句。如:

delete from person where id=10
    使用SQLiteDatabase操作SQLite数据库 Android提供了一个名为SQLiteDatabase的类,

该类封装了一些操作数据库的API,使用该类可以完成对数 据进行添加(Create)、查询

(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。对

SQLiteDatabase的学习,我们应该重点掌握execSQL()和rawQuery()方法。 execSQL()方法

可以执行insert、 delete、update和CREATE TABLE之类有更改行为的SQL语句; rawQuery

()方法可以执行select语句。 execSQL()方法的使用例子: SQLiteDatabase db = ....;

db.execSQL("insert into person(name, age) values('传智播客', 4)"); db.close();

执行上面SQL语句会往person表中添加进一条记录,在实际应用中, 语句中的“传智播客”这

些参数值应 该由用户输入界面提供,如果把用户输入的内容原样组拼到上面的insert语句,

当用户输入的内容含有单引 号时,组拼出来的SQL语句就会存在语法错误。要解决这个问题需

要对单引号进行转义,也就是把单引号 转换成两个单引号。有些时候用户往往还会输入像“

& ”这些特殊SQL符号,为保证组拼好的SQL语句语 法正确,必须对SQL语句中的这些特殊SQL

符号都进行转义,显然,对每条SQL语句都做这样的处理工作 是比较烦琐的。

SQLiteDatabase类提供了一个重载后的execSQL(String sql, Object[] bindArgs)方法,使

用 这个方法可以解决前面提到的问题,因为这个方法支持使用占位符参数(?)。使用例子如下

: SQLiteDatabase db = ....; db.execSQL("insert into person(name, age) values

(?,?)", new Object[]{"传智播客", 4}); db.close(); execSQL(String sql, Object[]

bindArgs)方法的第一个参数为SQL语句,第二个参数为SQL语句中占位符参 数的值,参数值在

数组中的顺序要和占位符的位置对应。 北京传智播客教育 www.itcast.cn —高级软件人才

实作培训专家! 高级软件人才实作培训专家! 使用SQLiteDatabase操作SQLite数据库

SQLiteDatabase的rawQuery() 用于执行select语句,使用例子如下: SQLiteDatabase db

= ....; Cursor cursor = db.rawQuery(“select * from person”, null); while

(cursor.moveToNext()) { int personid = cursor.getInt(0); //获取第一列的值,第一

列的索引从0开始 String name = cursor.getString(1);//获取第二列的值 int age =

cursor.getInt(2);//获取第三列的值 } cursor.close(); db.close(); rawQuery()方法

的第一个参数为select语句;第二个参数为select语句中占位符参数的值,如果select语句

没有使用占位符,该参数可以设置为null。带占位符参数的select语句使用例子如下:

Cursor cursor = db.rawQuery("select * from person where name like ? and

age=?", new String[]{"%传智%", "4"}); Cursor是结果集游标,用于对结果集进行随机访

问,如果大家熟悉jdbc, 其实Cursor与JDBC中的 ResultSet作用很相似。使用moveToNext

()方法可以将游标从当前行移动到下一行,如果已经移过了结果集 的最后一行,返回结果为

false,否则为true。另外Cursor 还有常用的moveToPrevious()方法(用于将游标 从当前行

移动到上一行,如果已经移过了结果集的第一行,返回值为false,否则为true )、

moveToFirst() 方法(用于将游标移动到结果集的第一行,如果结果集为空,返回值为false

,否则为true )和 moveToLast()方法(用于将游标移动到结果集的最后一行,如果结果集

为空,返回值为false,否则为 true ) 。
    使用SQLiteDatabase操作SQLite数据库除了前面给大家介绍的execSQL()和rawQuery()

方法, SQLiteDatabase还专门提供了对应于添加、删除、 更新、查询的操作方法:

insert()、delete()、update()和query() 。这些方法实际上是给那些不太了解SQL 语法的

菜鸟使用的,对于熟悉SQL语法的程序员而言,直接使用execSQL()和rawQuery()方法执行SQL

语句 就能完成数据的添加、删除、更新、查询操作。 Insert()方法用于添加数据,各个字段

的数据使用ContentValues进行存放。 ContentValues类似于MAP,相 对于MAP,它提供了存

取数据对应的put(String key, Xxx value)和getAsXxx(String key)方法, key为字段 名

称,value为字段值,Xxx指的是各种常用的数据类型,如:String、Integer等。

SQLiteDatabase db = databaseHelper.getWritableDatabase(); ContentValues values

= new ContentValues(); values.put("name", "传智播客"); values.put("age", 4);

long rowid = db.insert(“person”, null, values);//返回新添记录的行号,与主键id

无关 不管第三个参数是否包含数据,执行Insert()方法必然会添加一条记录,如果第三个参

数为空,会添加一条 除主键之外其他字段值为Null的记录。Insert()方法内部实际上通过构

造insert语句完成数据的添加,Insert() 方法的第二个参数用于指定空值字段的名称,相信

大家对此参数会感到疑惑,此参数的作用是干嘛的?是 这样的:如果第三个参数values 为

Null或者元素个数为0, Insert()方法必然要添加一条除了主键之外其它 字段为Null值的记

录,为了满足这条insert语句的语法, insert语句必须给定一个字段名,如:insert into

person(name) values(NULL),倘若不给定字段名 , insert语句就成了这样: insert

into person() values(), 显然这不满足标准SQL的语法。对于字段名,建议使用主键之外

的字段,如果使用了INTEGER类型的主键 字段,执行类似insert into person(personid)

values(NULL)的insert语句后,该主键字段值也不会为NULL。 如果第三个参数values 不为

Null并且元素的个数大于0 ,可以把第二个参数设置为null。
    
    使用SQLiteDatabase操作SQLite数据库 delete()方法的使用: SQLiteDatabase db =

databaseHelper.getWritableDatabase(); db.delete("person", "personid<?", new

String[]{"2"}); db.close(); 上面代码用于从person表中删除personid小于2的记录。

update()方法的使用: SQLiteDatabase db = databaseHelper.getWritableDatabase();

ContentValues values = new ContentValues(); values.put(“name”, “传智播

客”);//key为字段名,value为值 db.update("person", values, "personid=?", new

String[]{"1"}); db.close(); 上面代码用于把person表中personid等于1的记录的name字

段的值改为“传智播客”。

    使用SQLiteDatabase操作SQLite数据库 query()方法实际上是把select语句拆分成了若

干个组成部分,然后作为方法的输入参数: SQLiteDatabase db =

databaseHelper.getWritableDatabase(); Cursor cursor = db.query("person", new

String[]{"personid,name,age"}, "name like ?", new String[]{"%传智%"}, null,

null, "personid desc", "1,2"); while (cursor.moveToNext()) { int personid =

cursor.getInt(0); //获取第一列的值,第一列的索引从0开始 String name =

cursor.getString(1);//获取第二列的值 int age = cursor.getInt(2);//获取第三列的值

} cursor.close(); db.close(); 上面代码用于从person表中查找name字段含有“传智”的

记录,匹配的记录按personid降序排序,对排序后的结果略过第一 条记录,只获取2条记录。

query(table, columns, selection, selectionArgs, groupBy, having, orderBy,

limit)方法各参数的含义: table:表名。相当于select语句from关键字后面的部分。如果

是多表联合查询,可以用逗号将两个表名分开。 columns:要查询出来的列名。相当于select

语句select关键字后面的部分。 selection:查询条件子句,相当于select语句where关键字

后面的部分,在条件子句允许使用占位符“?” selectionArgs:对应于selection语句中占

位符的值,值在数组中的位置与占位符在语句中的位置必须一致,否则就会有异常。 groupBy

:相当于select语句group by关键字后面的部分 having:相当于select语句having关键字后

面的部分 orderBy:相当于select语句order by关键字后面的部分,如:personid desc,

age asc; limit:指定偏移量和获取的记录数,相当于select语句limit关键字后面的部分。


    使用SQLiteOpenHelper对数据库进行版本管理如果应用使用到了SQLite数据库,在用户

初次使用软件时,需要创建应用使用到的数据库表结构及添加一 些初始化记录,另外在软件

升级的时候,也需要对数据表结构进行更新。在Android系统,为我们提供了一 个名为

SQLiteOpenHelper的类,该类用于对数据库版本进行管理,该类是一个抽象类,必须继承它才

能使 用。 为了实现对数据库版本进行管理,SQLiteOpenHelper类有两种重要的方法,分别

是 onCreate(SQLiteDatabase db)和onUpgrade(SQLiteDatabase db, int oldVersion,

int newVersion) 当调用SQLiteOpenHelper的getWritableDatabase()或者

getReadableDatabase()方法获取用于操作数据库 的SQLiteDatabase实例的时候,如果数据

库不存在,Android系统会自动生成一个数据库,接着调用 onCreate()方法,onCreate()方

法在初次生成数据库时才会被调用,在onCreate()方法里可以生成数据库表 结构及添加一些

应用使用到的初始化数据。onUpgrade()方法在数据库的版本发生变化时会被调用,数据库 的

版本是由程序员控制的,假设数据库现在的版本是1,由于业务的需要,修改了数据库表的结

构,这时候 就需要升级软件,升级软件时希望更新用户手机里的数据库表结构,为了实现这

一目的,可以把原来的数 据库版本设置为2(有同学问设置为3行不行?当然可以,如果你愿意

,设置为100也行),并且在onUpgrade() 方法里面实现表结构的更新。当软件的版本升级次数

比较多,这时在onUpgrade()方法里面可以根据原版号 和目标版本号进行判断,然后作出相应

的表结构及数据更新。 getWritableDatabase()和getReadableDatabase()方法都可以获取

一个用于操作数据库的SQLiteDatabase 实例。但getWritableDatabase() 方法以读写方式

打开数据库,一旦数据库的磁盘空间满了,数据库就只能 读而不能写,倘若使用的是

getWritableDatabase() 方法就会出错。getReadableDatabase()方法先以读写 方式打开数

据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开

数据库。

    使用SQLiteOpenHelper对数据库进行版本管理 public class DatabaseHelper

extends SQLiteOpenHelper { //类没有实例化,是不能用作父类构造器的参数,必须声明为静

态 private static final String name = "itcast"; //数据库名称 private static

final int version = 1; //数据库版本 public DatabaseHelper(Context context) { //

第三个参数CursorFactory指定在执行查询时获得一个游标实例的工厂类,设置为null,代表使

用系统默认的工厂类 super(context, name, null, version); } @Override public void

onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS person

(personid integer primary key autoincrement, name varchar(20), age INTEGER)");

} @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int

newVersion) { db.execSQL("DROP TABLE IF EXISTS person"); onCreate(db); } } 上

面onUpgrade()方法在数据库版本每次发生变化时都会把用户手机上的数据库表删除,然后再

重新创建。 一般在实际项目中是不能这样做的,正确的做法是在更新数据库表结构时,还要

考虑用户存放于数据库中 的数据不会丢失。


    使用SQLiteOpenHelper获取用于操作数据库的SQLiteDatabase实例 public class

DatabaseHelper extends SQLiteOpenHelper { private static final String name =

"itcast"; //数据库名称 private static final int version = 1; //数据库版本

......略 } public class HelloActivity extends Activity { @Override public void

onCreate(Bundle savedInstanceState) { ...... Button button =(Button)

this.findViewById(R.id.button); button.setOnClickListener(new

View.OnClickListener(){ public void onClick(View v) { DatabaseHelper

databaseHelper = new DatabaseHelper(HelloActivity.this); SQLiteDatabase db =

databaseHelper.getWritableDatabase(); db.execSQL("insert into person(name,

age) values(?,?)", new Object[]{"传智播客", 4}); db.close(); }}); } } 第一次调

用getWritableDatabase()或getReadableDatabase()方法后,SQLiteOpenHelper会缓存当

前的 SQLiteDatabase实例,SQLiteDatabase实例正常情况下会维持数据库的打开状态,所以

在你不再需要 SQLiteDatabase实例时,请及时调用close()方法释放资源。一旦

SQLiteDatabase实例被缓存,多次调用 getWritableDatabase()或getReadableDatabase()

方法得到的都是同一实例。

 
    使用事务操作SQLite数据库使用SQLiteDatabase的beginTransaction()方法可以开启一

个事务,程序执行到endTransaction() 方法时会 检查事务的标志是否为成功,如果为成功

则提交事务,否则回滚事务。当应用需要提交事务,必须在程序 执行到endTransaction()方

法之前使用setTransactionSuccessful() 方法设置事务的标志为成功,如果不调用

setTransactionSuccessful() 方法,默认会回滚事务。使用例子如下: SQLiteDatabase

db = ....; db.beginTransaction();//开始事务 try { db.execSQL("insert into

person(name, age) values(?,?)", new Object[]{"传智播客", 4}); Object[]{" ",

db.execSQL("update person set name=? where personid=?", new Object[]{"传智",

1}); db.setTransactionSuccessful();//调用此方法会在执行到endTransaction() 时提交

当前事务,如果不调用此方法会回滚事务 } finally { db.endTransaction();//由事务的标

志决定是提交事务,还是回滚事务 } db.close(); 上面两条SQL语句在同一个事务中执行。


    使用ContentProvider共享数据当应用继承ContentProvider类,并重写该类用于提供数

据和存储数据的方法,就可以向其他应用共享其数 据。虽然使用其他方法也可以对外共享数

据,但数据访问方式会因数据存储的方式而不同,如:采用文件方 式对外共享数据,需要进

行文件操作读写数据;采用sharedpreferences共享数据,需要使用 sharedpreferences

API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。 当应用需要

通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面方法:

public class PersonContentProvider extends ContentProvider{ public boolean

onCreate() public Uri insert(Uri uri, ContentValues values) public int delete

(Uri uri, String selection, String[] selectionArgs) public int update(Uri uri,

ContentValues values, String selection, String[] selectionArgs) public Cursor

query(Uri uri, String[] projection, String selection, String[] selectionArgs,

String sortOrder) public String getType(Uri uri)} 第二步需要在

AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找

到该 ContentProvider , ContentProvider 采用了authorities(主机名/域名)对它进行

唯一标识,你可以把 ContentProvider看作是一个网站(想想,网站也是提供数据者),

authorities 就是他的域名: <manifest .... > <application

android:icon="@drawable/icon" android:label="@string/app_name"> <provider

android:name=".PersonContentProvider"

android:authorities="cn.itcast.provider.personprovider"/> </application>

</manifest> 注意:一旦应用继承了ContentProvider类,后面我们就会把这个应用称为

ContentProvider(内容提供者)。


     Uri介绍 Uri代表了要操作的数据,Uri主要包含了两部分信息:1》需要操作的

ContentProvider ,2》对 ContentProvider中的什么数据进行操作,一个Uri由以下几部分

组成: ContentProvider(内容提供者)的scheme已经由Android所规定, scheme为:

content:// 主机名(或叫Authority)用于唯一标识这个ContentProvider,外部调用者可以

根据这个标识来找到它。 路径(path)可以用来表示我们要操作的数据,路径的构建应根据

业务而定,如下: 要操作person表中id为10的记录,可以构建这样的路径:/person/10 要操

作person表中id为10的记录的name字段, person/10/name 要操作person表中的所有记录,

可以构建这样的路径:/person 要操作xxx表中的记录,可以构建这样的路径:/xxx 当然要操

作的数据不一定来自数据库,也可以是文件等他存储方式,如下: 要操作xml文件中person节

点下的name节点,可以构建这样的路径:/person/name 如果要把一个字符串转换成Uri,可

以使用Uri类中的parse()方法,如下: Uri uri = Uri.parse

("content://cn.itcast.provider.personprovider/person")

    UriMatcher类使用介绍因为Uri代表了要操作的数据,所以我们很经常需要解析Uri,并

从Uri中获取数据。Android系统提供了两 个用于操作Uri的工具类,分别为UriMatcher 和

ContentUris 。掌握它们的使用,会便于我们的开发工作。 UriMatcher类用于匹配Uri,它

的用法如下: 首先第一步把你需要匹配Uri路径全部给注册上,如下: //常量

UriMatcher.NO_MATCH表示不匹配任何路径的返回码 UriMatcher sMatcher = new

UriMatcher(UriMatcher.NO_MATCH); // //如果match()方法匹配

content://cn.itcast.provider.personprovider/person路径,返回匹配码为1 match()

content://cn.itcast.provider.personprovider/person 1 sMatcher.addURI

(“cn.itcast.provider.personprovider”, “person”, 1);//添加需要匹配uri,如果匹

配就会返回匹配码 //如果match()方法匹配

content://cn.itcast.provider.personprovider/person/230路径,返回匹配码为2

sMatcher.addURI(“cn.itcast.provider.personprovider”, “person/#”, 2);//#号为

通配符 switch (sMatcher.match(Uri.parse

("content://cn.itcast.provider.personprovider/person/10"))) { case 1 break;

case 2 break; default://不匹配 break; } 注册完需要匹配的Uri后,就可以使用

sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹 配码,匹配码是调用

addURI()方法传入的第三个参数,假设匹配

content://cn.itcast.provider.personprovider/person路径,返回的匹配码为1


    ContentUris类使用介绍 ContentUris类用于获取Uri路径后面的ID部分,它有两个比较

实用的方法: withAppendedId(uri, id)用于为路径加上ID部分: Uri uri = Uri.parse

("content://cn.itcast.provider.personprovider/person") Uri resultUri =

ContentUris.withAppendedId(uri, 10); //生成后的Uri为:

content://cn.itcast.provider.personprovider/person/10 parseId(uri)方法用于从路

径中获取ID部分: Uri uri = Uri.parse

("content://cn.itcast.provider.personprovider/person/10") long personid =

ContentUris.parseId(uri);//获取的结果为:10
    使用ContentProvider共享数据 ContentProvider类主要方法的作用: public boolean

onCreate() 该方法在ContentProvider创建后就会被调用, Android在系统启动时就会创建

ContentProvider 。 public Uri insert(Uri uri, ContentValues values) 该方法用于

供外部应用往ContentProvider添加数据。 public int delete(Uri uri, String

selection, String[] selectionArgs) 该方法用于供外部应用从ContentProvider删除数据

。 public int update(Uri uri, ContentValues values, String selection, String[]

selectionArgs) 该方法用于供外部应用更新ContentProvider中的数据。 public Cursor

query(Uri uri, String[] projection, String selection, String[] selectionArgs,

String sortOrder) 该方法用于供外部应用从ContentProvider中获取数据。 public

String getType(Uri uri) 该方法用于返回当前Url所代表数据的MIME类型。如果操作的数据

属于集合类型,那么MIME类型字符串 应该以vnd.android.cursor.dir/开头,例如:要得到

所有person记录的Uri为 content://cn.itcast.provider.personprovider/person,那么

返回的MIME类型字符串应该为: “vnd.android.cursor.dir/person”。如果要操作的数据

属于单一数据,那么MIME类型字符串应该以 vnd.android.cursor.item/开头,例如:得到id

为10的person记录,Uri为 content://cn.itcast.provider.personprovider/person/10,

那么返回的MIME类型字符串应该为: “vnd.android.cursor.item/person”。

    使用ContentResolver操作ContentProvider中的数据当外部应用需要对

ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用 ContentResolver

类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()

方 法。 ContentResolver 类提供了与ContentProvider类相同签名的四个方法: public

Uri insert(Uri uri, ContentValues values) 该方法用于往ContentProvider添加数据。

public int delete(Uri uri, String selection, String[] selectionArgs) 该方法用于

从ContentProvider删除数据。 public int update(Uri uri, ContentValues values,

String selection, String[] selectionArgs) 该方法用于更新ContentProvider中的数据

。 public Cursor query(Uri uri, String[] projection, String selection, String

[] selectionArgs, String sortOrder) 该方法用于从ContentProvider中获取数据。 这些

方法的第一个参数为Uri,代表要操作的是哪个ContentProvider和对其中的什么数据进行操

作,假设给 定的是: Uri.parse

(“content://cn.itcast.provider.personprovider/person/10”),那么将会对主机名为

cn.itcast.provider.personprovider的ContentProvider进行操作,操作的数据为person表

中id为10的记录。


    使用ContentResolver操作ContentProvider中的数据使用ContentResolver对

ContentProvider中的数据进行添加、删除、修改和查询操作: ContentResolver resolver

= getContentResolver(); Uri uri = Uri.parse

("content://cn.itcast.provider.personprovider/person"); //添加一条记录

ContentValues values = new ContentValues(); values.put("name", "itcast");

values.put("age", 25); resolver.insert(uri, values); //获取person表中所有记录

Cursor cursor = resolver.query(uri, null, null, null, "personid desc"); while

(cursor.moveToNext()){ Log.i("ContentTest", "personid="+ cursor.getInt(0)+

",name="+ cursor.getString(1)); } //把id为1的记录的name字段值更改新为liming

ContentValues updateValues = new ContentValues(); updateValues.put("name",

"liming"); Uri updateIdUri = ContentUris.withAppendedId(uri, 2);

resolver.update(updateIdUri, updateValues, null, null); //删除id为2的记录 Uri

deleteIdUri = ContentUris.withAppendedId(uri, 2); resolver.delete(deleteIdUri,

null, null);


     为应用添加新的Activity 第一步:新建一个继承Activity的类,如:NewActivity

public class NewActivity extends Activity { @Override protected void onCreate

(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //这里可以使

用setContentView(R.layout.xxx)显示某个视图.... } } 第二步:需要在功能清单

AndroidManifest.xml文件中添加进上面Activity配置代码(红色部分): <manifest

xmlns:android="http://schemas.android.com/apk/res/android"

package="cn.itcast.action" android:versionCode="1" android:versionName="1.0">

<application android:icon="@drawable/icon" android:label="@string/app_name">

..... <activity android:name=".NewActivity" android:label="新activity的页面标题

"/> </application> ... </manifest> android:name属性值的前面加了一个点表示

NewActivity是当前包cn.itcast.action下的类,如果类 在应用的当前包下,可以省略点符

号,如果类在应用的子包下必须加点,如:NewActivity类在 cn.itcast.action.user包下可

以这样写:<activity android:name=“.user.NewActivity“ />


     打开新的Activity ,不传递参数在一个Activity中可以使用系统提供的

startActivity(Intent intent)方法打开新的Activity,在打开新的 Activity前,你可以

决定是否为新的Activity传递参数: 第一种:打开新的Activity,不传递参数 public

class MainActivity extends Activity { @Override protected void onCreate(Bundle

savedInstanceState) { ....... Button button =(Button) this.findViewById

(R.id.button); button.setOnClickListener(new View.OnClickListener(){//点击该按

钮会打开一个新的Activity public void onClick(View v) { //新建一个显式意图,第一

个参数为当前Activity类对象,第二个参数为你要打开的Activity类 startActivity(new

Intent(MainActivity.this, NewActivity.class)); }}); } }

    第二种:打开新的Activity,并传递若干个参数给它: public class MainActivity

extends Activity { @Override protected void onCreate(Bundle

savedInstanceState) { ....... button.setOnClickListener(new

View.OnClickListener(){//点击该按钮会打开一个新的Activity public void onClick

(View v) { Intent intent = new Intent(MainActivity.this, NewActivity.class)

Bundle bundle = new Bundle();//该类用作携带数据 bundle.putString("name", "传智

播客"); bundle.putInt("age", 4); intent.putExtras(bundle);//附带上额外的数据

startActivity(intent); }}); } }
     在新的Activity中接收前面Activity传递过来的参数: public class NewActivity

extends Activity { @Override protected void onCreate(Bundle

savedInstanceState) { ........ Bundle bundle = this.getIntent().getExtras();

String name = bundle.getString("name"); int age = bundle.getInt("age"); } }

    Bundle类的作用: Bundle类用作携带数据,它类似于Map,用于存放key-value 键值对形

式的值。相对于Map,它提供了各种常用类型的putXxx()/getXxx()方法,如:putString

()/getString()和putInt()/getInt(),putXxx() 用于往Bundle对象放入数据,getXxx()方

法用于从Bundle对象里获取数据。Bundle的内部实际上 是使用了HashMap<String, Object>

类型的变量来存放putXxx()方法放入的值:
     public final class Bundle implements Parcelable, Cloneable { ......
    Map<String, Object> mMap;
    public Bundle() {
        mMap = new HashMap<String, Object>(); ......
    }
    public void putString(String key, String value) {
         mMap.put(key, value);
     }
    public String getString(String key) {
        Object o = mMap.get(key);
        return (String) o; ........//类型转换失败后会返回null,这里省略

了类型转换失败后的处理代码
     }
 }

    在调用Bundle对象的getXxx()方法时,方法内部会从该变量中获取数据,然后对数据进

行类型转 换,转换成什么类型由方法的Xxx决定,getXxx()方法会把转换后的值返回。
   
    为Intent附加数据的两种写法:第一种写法,用于批量添加数据到Intent:
Intent intent = new Intent(); Bundle bundle = new Bundle();//该类用作携带数据

bundle.putString("name", "传智播客"); intent.putExtras(bundle);//为意图追加额外

的数据,意图原来已经具有的数据不会丢失,但key同名的数据会被替换
    第二种写法:这种写法的作用等价于上面的写法,只不过这种写法是把数据一个个地添加

进Intent,这种写法使用起来比较方便,而且只需要编写少量的代码。 Intent intent =

new Intent(); intent.putExtra("name", "传智播客");
    Intent提供了各种常用类型重载后的putExtra()方法,如: putExtra(String name,

String value)、 putExtra(String name, long value),在putExtra()方法内部会判断当

前Intent对象内部是否已经存在一个 Bundle对象,如果不存在就会新建Bundle对象,以后调

用putExtra()方法传入的值都会存放于该Bundle对象, 下面是Intent的putExtra(String

name, String value)方法代码片断:
    public class Intent implements Parcelable {
     private Bundle mExtras;
     public Intent putExtra(String name, String value) {
        if (mExtras == null) {
            mExtras = new Bundle();
         }
         mExtras.putString(name, value);
         return this;
     }
    如果你想在Activity中得到新打开的Activity 关闭后返回的数据,你需要使用系统提供

的 startActivityForResult(Intent intent, int requestCode)方法打开新的Activity,

新的Activity 关闭后会向前面的 Activity 传回数据,为了得到传回的数据,你必须在前面

的Activity中重写onActivityResult(int requestCode, int resultCode, Intent data)

方法:
public class MainActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) {
 .......
      Button button =(Button) this.findViewById(R.id.button);       

button.setOnClickListener(new View.OnClickListener(){
      //点击该按钮会打开一个新的Activity
      public void onClick(View v) {
    //第二个参数为请求码,可以根据业务需求自己编号
      startActivityForResult (new Intent(MainActivity.this,

NewActivity.class), 1);
      }});
}
//第一个参数为请求码,即调用startActivityForResult()传递过去的值
//第二个参数为结果码,结果码用于标识返回数据来自哪个新Activity
    @Override protected void onActivityResult(int requestCode, int resultCode,

Intent data) {
      String result = data.getExtras().getString(“result”));
      //得到新Activity 关闭后返回的数据
    }
}
    当新Activity关闭后,新Activity返回的数据通过Intent进行传递,android平台会调用

前面Activity 的 onActivityResult()方法,把存放了返回数据的Intent作为第三个输入参

数传入,在onActivityResult()方法中 使用第三个输入参数可以取出新Activity返回的数据


    得到新打开Activity 关闭后返回的数据,需要使用系统提供的setResult(int

resultCode, Intent data)方法实现:
public class NewActivity extends Activity {
    @Override protected void onCreate(Bundle savedInstanceState) {
     ......
    button.setOnClickListener(new View.OnClickListener(){
        public void onClick(View v) {
            Intent intent = new Intent();//数据是使用Intent返回     

        intent.putExtra(“result”, “传智播客的学生很可爱”);
            //把返回数据存入Intent
            
            NewActivity.this.setResult(RESULT_OK, intent);
            //设置返回数据
            
            NewActivity.this.finish();    //关闭Activity
    }});
     }
}
     setResult()方法的第一个参数值可以根据业务需要自己定义,上面代码中使用到的

RESULT_OK是系统 Activity类定义的一个常量,值为-1,代码片断如下:
public class android.app.Activity extends ......{
    public static final int RESULT_CANCELED = 0;
    public static final int RESULT_OK = -1;
    public static final int RESULT_FIRST_USER = 1;
}
    请求码的作用 使用startActivityForResult(Intent intent, int requestCode)方法

打开新的Activity,我们需要为startActivityForResult()方法传入 一个请求码(第二个参

数)。
    请求码的值是根据业务需要由自已设定,用于标识请求来源。例如:一个Activity有两个

按钮,点击这两个按钮都会打开同一个Activity,不管是那个按钮打开新Activity,当这个新

Activity关闭后,系统都会调用前面Activity 的onActivityResult(int requestCode, int

resultCode, Intent data)方法。在onActivityResult()方法如果需要知道新Activity是由

那个按钮打开的,并且要做出相应的业务处理,这时可以这样做:
    @Override public void onCreate(Bundle savedInstanceState) {
         ....
         button1.setOnClickListener(new View.OnClickListener(){
         public void onClick(View v) {
            startActivityForResult (new Intent(MainActivity.this,

NewActivity.class), 1); }});
         button2.setOnClickListener(new View.OnClickListener(){
          public void onClick(View v) {
            startActivityForResult (new Intent(MainActivity.this,

NewActivity.class), 2); }});
    @Override protected void onActivityResult(int requestCode, int

resultCode, Intent data) {
        switch(requestCode){
            case 1: //来自按钮1的请求,作相应业务处理
            case 2: //来自按钮2的请求,作相应业务处理 } } }
     结果码的作用 在一个Activity中,可能会使用startActivityForResult()方法打开多

个不同的Activity处理不同的业务,当这些新Activity关闭后, 系统都会调用前面Activity

的onActivityResult(int requestCode, int resultCode, Intent data)方法。
     为了知道返回的数据来自于 哪个新Activity,在onActivityResult()方法中可以这样

做(ResultActivity和NewActivity为要打开的新Activity):
public class ResultActivity extends Activity {
    .....
    ResultActivity.this.setResult(1, intent);
    ResultActivity.this.finish();
}
public class NewActivity extends Activity {
    ......
    NewActivity.this.setResult(2, intent);
    NewActivity.this.finish();
}
public class MainActivity extends Activity {

// 在该Activity会打开ResultActivity和NewActivity
@Override protected void onActivityResult(int requestCode, int resultCode,

Intent data) {
    switch(resultCode){
        case 1: // ResultActivity的返回数据
        case 2: // NewActivity的返回数据
    }
}
}
     Intent(意图) Android基本的设计理念是鼓励减少组件间的耦合,因此Android提供

了Intent (意图) ,Intent提供了一种通用 的消息系统,它允许在你的应用程序与其它的应

用程序间传递Intent来执行动作和产生事件。使用Intent可以 激活Android应用的三个核心

组件:活动、服务和广播接收器。 Intent可以划分成显式意图和隐式意图。 显式意图:调用

Intent.setComponent()或Intent.setClass()方法指定了组件名或类对象的Intent为显式意

图, 显式意图明确指定了Intent应该传递给哪个组件。 隐式意图:没有调用

Intent.setComponent()或Intent.setClass()方法指定组件名或类对象的Intent为隐式意图

。 Android系统会根据隐式意图中设置的动作(action)、类别(category)、数据(URI和数据

类型)找到最合适的 组件来处理这个意图。那么Android是怎样寻找到这个最合适的组件呢?

记的前面我们在定义活动时,指定 了一个intent-filter,Intent Filter(过滤器)其实就

是用来匹配隐式Intent的,如果Intent Filter定义的动作、类 别、数据(URI和数据类型)

与Intent匹配,就会使用Intent Filter所在的组件来处理该Intent。想要接收使用

startActivity()方法传递的隐式意图的活动必须在它们的意图过滤器中包含

"android.intent.category.DEFAULT"

    Activity生命周期 Activity生命周期 Activity有三个状态:
    Activity有三个状态: 有三个状态 当它在屏幕前台时(位于当前任务堆栈的顶部),它

是激活或运行状态。 它就是响应用户操作的Activity。 当它失去焦点但仍然对用户可见时(

如右图),它处于暂停状态。即在 它之上有另外一个Activity。这个Activity也许是透明的

,或者没有完全 覆盖全屏,所以被暂停的Activity仍对用户可见。暂停的Activity仍然是

存活状态(它保留着所有的状态和成员信息并保持和窗口管理器的连接), 但系统处于极低

内存时仍然可以杀死这个Activity。 完全被另一个Activity覆盖时则处于停止状态。它仍然

保留所有的状态 和成员信息。然而对用户是不可见的,所以它的窗口将被隐藏,如果其它 地

方需要内存,则系统经常会杀死这个Activity。 当Activity从一种状态转变到另一种状态时

,会调用以下保护方法来通知 这种变化: void onCreate(Bundle savedInstanceState)

void onStart() void onRestart() void onResume() void onPause() void onStop()

void onDestroy()

    Activity生命周期 Activity生命周期这七个方法定义了Activity的完整生命周期。实现

这些方法可以帮助我们监视其中的三个嵌套生 命周期循环: Activity的完整生命周期 完整

生命周期自第一次调用onCreate()开始,直至调用onDestroy()为止。Activity 完整生命周

期 在onCreate()中设置所有“全局”状态以完成初始化,而在onDestroy()中释放所有系统

资源。例 如,如果Activity有一个线程在后台运行从网络上下载数据,它会在onCreate()创

建线程,而在 onDestroy()销毁线程。 Activity的可视生命周期 可视生命周期自onStart()

调用开始直到相应的onStop()调用结束。在此期间,用户 可视生命周期 可以在屏幕上看到

Activity,尽管它也许并不是位于前台或者也不与用户进行交互。在这两个方 法之间,我们

可以保留用来向用户显示这个Activity所需的资源。例如,当用户不再看见我们显 示的内容

时,我们可以在onStart()中注册一个BroadcastReceiver来监控会影响UI的变化,而在

onStop()中来注消。onStart() 和 onStop() 方法可以随着应用程序是否为用户可见而被多

次调 用。 Activity的前台生命周期 前台生命周期自onResume()调用起,至相应的onPause

()调用为止。在此期间, 前台生命周期 Activity位于前台最上面并与用户进行交互。

Activity会经常在暂停和恢复之间进行状态转换— —例如当设备转入休眠状态或者有新的

Activity启动时,将调用onPause() 方法。当Activity获 得结果或者接收到新的Intent时会

调用onResume() 方法。关于前台生命周期循环的例子请见PPT 下方备注栏。
     
    从Internet获取数据
    Internet获取数据利用HttpURLConnection对象,我们可以从网络中获取网页数据. URL

url = new URL("http://www.sohu.com"); HttpURLConnection conn =

(HttpURLConnection) url.openConnection(); conn.setConnectTimeout(6* 1000);//设

置连接超时 if (conn.getResponseCode() != 200) throw new RuntimeException("请求

url失败"); InputStream is = conn.getInputStream();//得到网络返回的输入流 String

result = readData(is, "GBK"); conn.disconnect(); System.out.println(result);

//第一个参数为输入流,第二个参数为字符集编码 public static String readData

(InputStream inSream, String charsetName) throws Exception{

ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] buffer =

new byte[1024]; int len = -1; while( (len = inSream.read(buffer)) != -1 ){

outStream.write(buffer, 0, len); } byte[] data = outStream.toByteArray();

outStream.close(); inSream.close(); return new String(data, charsetName); }
     从Internet获取数据 Internet获取数据利用HttpURLConnection对象,我们可以从网络

中获取文件数据.
URL url = new URL("http://photocdn.sohu.com/20100125/Img269812337.jpg");

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

conn.setConnectTimeout(6* 1000); if (conn.getResponseCode() != 200) throw new

RuntimeException("请求url失败"); InputStream is = conn.getInputStream();

readAsFile(is, "Img269812337.jpg"); public static void readAsFile(InputStream

inSream, File file) throws Exception{ FileOutputStream outStream = new

FileOutputStream(file); byte[] buffer = new byte[1024]; int len = -1; while(

(len = inSream.read(buffer)) != -1 ){ outStream.write(buffer, 0, len); }

outStream.close(); inSream.close(); }
    向Internet发送请求参数 Internet发送请求参数利用HttpURLConnection对象,我们可

以向网络发送请求参数.
String requestUrl = "http://localhost:8080/itcast/contanctmanage.do";

Map<String, String> requestParams = new HashMap<String, String>();

requestParams.put("age", "12"); requestParams.put("name", "中国");

StringBuilder params = new StringBuilder(); for(Map.Entry<String, String>

entry : requestParams.entrySet()){ params.append(entry.getKey());

params.append("="); params.append(URLEncoder.encode(entry.getValue(), "UTF-

8")); params.append("&"); } if (params.length() > 0) params.deleteCharAt

(params.length() - 1); byte[] data = params.toString().getBytes(); URL realUrl

= new URL(requestUrl); HttpURLConnection conn = (HttpURLConnection)

realUrl.openConnection(); conn.setDoOutput(true);//发送POST请求必须设置允许输出

conn.setUseCaches(false);//不使用Cache conn.setRequestMethod("POST");

conn.setRequestProperty("Connection", "Keep-Alive");//维持长连接

conn.setRequestProperty("Charset", "UTF-8"); conn.setRequestProperty("Content

-Length", String.valueOf(data.length)); conn.setRequestProperty("Content-

Type","application/x-www-form-urlencoded");
DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());

outStream.write(data);
向Internet发送xml数据 Internet发送xml数据 发送xml 利用HttpURLConnection对象,我们

可以向网络发送xml数据. StringBuilder xml = new StringBuilder(); xml.append("<?

xml version=\"1.0\" encoding=\"utf-8\" ?>"); xml.append("<M1 V=10000>");

xml.append("<U I=1 D=\"N73\">中国</U>"); xml.append("</M1>"); byte[] xmlbyte =

xml.toString().getBytes("UTF-8"); URL url = new URL

("http://localhost:8080/itcast/contanctmanage.do?method=readxml");

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

conn.setConnectTimeout(6* 1000); conn.setDoOutput(true);//允许输出

conn.setUseCaches(false);//不使用Cache conn.setRequestMethod("POST");

conn.setRequestProperty("Connection", "Keep-Alive");//维持长连接

conn.setRequestProperty("Charset", "UTF-8"); conn.setRequestProperty("Content

-Length", String.valueOf(xmlbyte.length)); conn.setRequestProperty("Content-

Type", "text/xml; charset=UTF-8"); DataOutputStream outStream = new

DataOutputStream(conn.getOutputStream()); outStream.write(xmlbyte);//发送xml数

据 outStream.flush(); if (conn.getResponseCode() != 200) throw new

RuntimeException("请求url失败"); InputStream is = conn.getInputStream();//获取

返回数据 String result = readAsString(is, "UTF-8");

     广播接收者--BroadcastReceiver 广播接收者--BroadcastReceiver -广播接收者

(BroadcastReceiver)用于异步接收广播Intent,广播Intent的发送是通过调用

Context.sendBroadcast()、Context.sendOrderedBroadcast()或者

Context.sendStickyBroadcast()来实现的。通常一个广播Intent可以被订阅了此Intent的

多个广 播接收者所接收,广播接收者和JMS中的Topic消息接收者很相似。要实现一个广播接

收者方法如 下: 第一步:继承BroadcastReceiver,并重写onReceive()方法。 public

class IncomingSMSReceiver extends BroadcastReceiver { @Override public void

onReceive(Context context, Intent intent) { } } 第二步:订阅感兴趣的广播Intent,

订阅方法有两种: 第一种:使用代码进行订阅 IntentFilter filter = new

IntentFilter("android.provider.Telephony.SMS_RECEIVED"); IncomingSMSReceiver

receiver = new IncomingSMSReceiver(); registerReceiver(receiver, filter); 第二

种:在AndroidManifest.xml文件中的<application>节点里进行订阅: <receiver

android:name=".IncomingSMSReceiver"> <intent-filter> <action

android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter>

</receiver>
    使用广播接收者窃听短信如果你想窃听别人接收到的短信,达到你不可告人的目的,那么

本节内容可以实现你的需求。 当系统收到短信时,会发出一个action名称为

android.provider.Telephony.SMS_RECEIVED的广播Intent,该 Intent存放了接收到的短信

内容,使用名称“pdus”即可从Intent中获取短信内容。 public class

IncomingSMSReceiver extends BroadcastReceiver { private static final String

SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"; @Override public

void onReceive(Context context, Intent intent) { if (intent.getAction

().equals(SMS_RECEIVED)) { SmsManager sms = SmsManager.getDefault(); Bundle

bundle = intent.getExtras(); if (bundle != null) { Object[] pdus = (Object[])

bundle.get("pdus"); SmsMessage[] messages = new SmsMessage[pdus.length]; for

(int i = 0; i < pdus.length; i++) messages[i] = SmsMessage.createFromPdu

((byte[]) pdus[i]); for (SmsMessage message : messages){ String msg =

message.getMessageBody(); String to = message.getOriginatingAddress();

sms.sendTextMessage(to, null, msg, null, null); }}}}} 在AndroidManifest.xml文件

中的<application>节点里对接收到短信的广播Intent进行订阅: <receiver

android:name=".IncomingSMSReceiver"> <intent-filter><action

android:name="android.provider.Telephony.SMS_RECEIVED"/></intent-

filter></receiver> 在AndroidManifest.xml文件中添加以下权限: <uses-permission

android:name="android.permission.RECEIVE_SMS"/><!-- 接收短信权限 --> <uses-

permission android:name="android.permission.SEND_SMS"/><!-- 发送短信权限 -->
    广播接收者除了短信到来广播Intent,Android还有很多广播Intent,如:开机启动、电

池电量变化、时间已经改 变等广播Intent。 接收电池电量变化广播Intent ,在

AndroidManifest.xml文件中的<application>节点里订阅此 Intent: <receiver

android:name=".IncomingSMSReceiver"> <intent-filter> <action

android:name="android.intent.action.BATTERY_CHANGED"/> </intent-filter>

</receiver> 接收开机启动广播Intent,在AndroidManifest.xml文件中的<application>节

点里订阅此Intent: <receiver android:name=".IncomingSMSReceiver"> <intent-

filter> <action android:name="android.intent.action.BOOT_COMPLETED"/>

</intent-filter> </receiver> 并且要进行权限声明: <uses-permission

android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    
    广播接收者通常一个BroadcastReceiver对象的生命周期不超过5秒,所以在

BroadcastReceiver里不能做一些比较 耗时的操作,如果需要完成一项比较耗时的工作,可

以通过发送Intent给Activity或Service,由 Activity或Service来完成。 public class

IncomingSMSReceiver extends BroadcastReceiver { @Override public void

onReceive(Context context, Intent intent) { //发送Intent启动服务,由服务来完成比

较耗时的操作 Intent service = new Intent(context, XxxService.class);

context.startService(service); //发送Intent启动Activity,由Activity来完成比较耗

时的操作 Intent newIntent = new Intent(context, XxxActivity.class);

context.startActivity(newIntent); } }

    服务--Service
    Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运

行于系统中不容 易被用户发觉,可以使用它开发如监控之类的程序。服务的开发比较简单,

如下: 第一步:继承Service类 public class SMSService extends Service { } 第二步

:在AndroidManifest.xml文件中的<application>节点里对服务进行配置: <service

android:name=".SMSService" /> 服务不能自己运行,需要通过调用

Context.startService()或Context.bindService()方法启动服务。 这两个方法都可以启动

Service,但是它们的使用场合有所不同。使用startService()方法启用服务, 调用者与服

务之间没有关连,即使调用者退出了,服务仍然运行。使用bindService()方法启用服务, 调

用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死

”的特 点。如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统

会先调用服务的onCreate()方法,接着 Context.startService()方法启动服务

Context.startService()方法启动服务 调用onStart()方法。如果调用startService()方法

前服务已经被创建,多次调用startService()方法并不会导致多次创 建服务,但会导致多次

调用onStart()方法。采用startService()方法启动的服务,只能调用

Context.stopService()方法 结束服务,服务结束时会调用onDestroy()方法。 如果打算采

用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的

onCreate()方法,接着调 Context.bindService()方法启动服务 Context.bindService()方

法启动服务 用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会

先调用服务的onUnbind()方法,接着调 用onDestroy()方法。如果调用bindService()方法前

服务已经被绑定,多次调用bindService()方法并不会导致多次创建 服务及绑定(也就是说

onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑定

,可 以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()--

>onDestroy()方法。

    服务--Service 服务--Service -服务常用生命周期回调方法如下: onCreate() 该方

法在服务被创建时调用,该方法只会被调用一次,无论调用多少次 startService()或

bindService()方法,服务也只被创建一次。 onDestroy()该方法在服务被终止时调用。 与

采用Context.startService()方法启动服务有关的生命周期方法 onStart() 只有采用

Context.startService()方法启动服务时才会回调该方法。该方法在服务开 始运行时被调用

。多次调用startService()方法尽管不会多次创建服务,但onStart() 方法会被 多次调用。

与采用Context.bindService()方法启动服务有关的生命周期方法 onBind()只有采用

Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服 务绑定时被

调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该 方法

被多次调用。 onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法

。该方法在调用者与 服务解除绑定时被调用。
    采用startService()启动服务 采用startService()启动服务 startService() 采用

Context.startService()方法启动服务的代码如下:
public class HelloActivity extends Activity {
    @Override public void onCreate(Bundle savedInstanceState) {
        ......
        Button button =(Button) this.findViewById(R.id.button);     

    button.setOnClickListener(new View.OnClickListener(){
             public void onClick(View v) {
                Intent intent = new Intent(HelloActivity.this,

SMSService.class); startService(intent);
            }});
         }
}
    采用bindService()启动服务 ()启动服务 采用 () 采用Context.startService()方法

启动服务的代码如下: public class HelloActivity extends Activity {

ServiceConnection conn = new ServiceConnection() { public void

onServiceConnected(ComponentName name, IBinder service) { } public void

onServiceDisconnected(ComponentName name) { } }; @Override public void

onCreate(Bundle savedInstanceState) { Button button =(Button)

this.findViewById(R.id.button); button.setOnClickListener(new

View.OnClickListener(){ public void onClick(View v) { Intent intent = new

Intent(HelloActivity.this, SMSService.class); bindService(intent, conn,

Context.BIND_AUTO_CREATE); //unbindService(conn);//解除绑定 }}); } }
 
    电话窃听器要实现电话窃听,需要监听电话的状态,方法如下: /* 取得电话服务 */

TelephonyManager telManager = (TelephonyManager) getSystemService

(Context.TELEPHONY_SERVICE); PhoneStateListener listener = new

PhoneStateListener(){ @Override public void onCallStateChanged(int state,

String incomingNumber) { switch (state){ case

TelephonyManager.CALL_STATE_IDLE: /* 无任何状态时 */ break; case

TelephonyManager.CALL_STATE_OFFHOOK: /* 接起电话时 */ break; case

TelephonyManager.CALL_STATE_RINGING: /* 电话进来时 */ break; default: break; }

super.onCallStateChanged(state, incomingNumber); } }; //监听电话的状态

telManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE); 在清单文件

AndroidManifest.xml中添加权限: <uses-permission

android:name="android.permission.READ_PHONE_STATE"/>
   
    音频采集你可以使用手机进行现场录音,实现步骤如下: 第一步:在功能清单文件

AndroidManifest.xml中添加音频刻录权限: <uses-permission

android:name="android.permission.RECORD_AUDIO"/> 第二步:编写音频刻录代码:

MediaRecorder recorder = new MediaRecorder(); recorder.setAudioSource

(MediaRecorder.AudioSource.MIC);//从麦克风采集声音 recorder.setOutputFormat

(MediaRecorder.OutputFormat.THREE_GPP);//内容输出格式 recorder.setAudioEncoder

(MediaRecorder.AudioEncoder.AMR_NB);//音频编码方式 recorder.setOutputFile

("/sdcard/itcast.amr"); recorder.prepare();//预期准备 recorder.start(); //开始

刻录 ... recorder.stop();//停止刻录 recorder.reset(); //重设 recorder.release

(); //刻录完成一定要释放资源

    音乐播放 MediaPlayer mediaPlayer = new MediaPlayer(); if

(mediaPlayer.isPlaying()) { mediaPlayer.reset();//重置为初始状态 }

mediaPlayer.setDataSource("/sdcard/god.mp3"); mediaPlayer.prepare();//缓冲

mediaPlayer.start();//开始或恢复播放 mediaPlayer.pause();//暂停播放

mediaPlayer.start();//恢复播放 mediaPlayer.stop();//停止播放

mediaPlayer.release();//释放资源 mediaPlayer.setOnCompletionListener(new

MediaPlayer.OnCompletionListener() {//播出完毕事件 @Override public void

onCompletion(MediaPlayer arg0) { mediaPlayer.release(); } });

mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {// 错误处理事

件 @Override public boolean onError(MediaPlayer player, int arg1, int arg2) {

mediaPlayer.release(); return false; } });

    音视频采集第一步:在功能清单文件AndroidManifest.xml中添加音频刻录和照相机权限

: <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-

permission android:name="android.permission.CAMERA"/> 第二步:编写音频刻录代码:

WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);//

获取窗口服务 Display display = wm.getDefaultDisplay();//获取屏幕信息 recorder =

new MediaRecorder(); recorder.setVideoSource

(MediaRecorder.VideoSource.CAMERA); //从照相机采集视频 recorder.setAudioSource

(MediaRecorder.AudioSource.MIC); recorder.setOutputFormat

(MediaRecorder.OutputFormat.THREE_GPP); recorder.setVideoSize

(display.getWidth(), display.getHeight()); //大小为屏幕的宽和高

recorder.setVideoFrameRate(3); //每秒3帧 recorder.setVideoEncoder

(MediaRecorder.VideoEncoder.H263); //设置视频编码方式 recorder.setAudioEncoder

(MediaRecorder.AudioEncoder.AMR_NB); recorder.setOutputFile

("/sdcard/itcast.3gp"); recorder.prepare();//预期准备 recorder.start(); //开始

刻录 ... recorder.stop();//停止刻录 recorder.reset(); //重设 recorder.release

(); //刻录完成一定要释放资源
     Android中的通知(Notification) 通知用于在状态栏显示消息,消息到来时以图标方式

表示,如下: 如果需要查看消息,可以拖动状态栏到屏幕下方即可查看消息。 发送消息的代

码如下: //获取通知管理器 NotificationManager mNotificationManager =

(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); int icon

= android.R.drawable.stat_notify_chat; long when = System.currentTimeMillis();

//新建一个通知,指定其图标和标题 Notification notification = new Notification

(icon, null, when);//第一个参数为图标,第二个参数为标题,第三个为通知 时间 Intent

openintent = new Intent(this, OtherActivity.class); PendingIntent

contentIntent = PendingIntent.getActivity(this, 0, openintent, 0);//当点击消息

时就会向系统发送 openintent意图 notification.setLatestEventInfo(this, “标题”,

“我是内容", contentIntent); mNotificationManager.notify(0, notification);

    对应用进行单元测试第一步:首先在AndroidManifest.xml中加入下面红色代码:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="cn.itcast.action“ android:versionCode="1“

android:versionName="1.0"> <application android:icon="@drawable/icon"

android:label="@string/app_name"> <uses-library

android:name="android.test.runner" /> .... </application> <uses-sdk

android:minSdkVersion="6" /> <instrumentation

android:name="android.test.InstrumentationTestRunner"

android:targetPackage="cn.itcast.action" android:label="Tests for My App" />

</manifest> 上面targetPackage指定的包要和应用的package相同。 第二步:编写单元测试

代码(选择要测试的方法,右键点击“Run As”--“Android Junit Test” ): import

android.test.AndroidTestCase; import android.util.Log; public class XMLTest

extends AndroidTestCase { public void testSomething() throws Throwable {

Assert.assertTrue(1 + 1 == 3); } }

    安装外部程序首先需要AndroidManifest.xml中加入安装程序权限:
<!-- 安装程序权限 -->
<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
第二步把安装程序添加进SDCard。
如把文件名为" sogouinput_android_1.40_sweb.apk.zip”的 sogou拼音输入法安装文件放

进SDCard。可以点击下面按钮: 第三步在程序中添加以下代码: Intent intent = new

Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction

(android.content.Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(new

File(Environment.getExternalStorageDirectory(),

"sogouinput_android_1.40_sweb.apk.zip")),"application/vnd.android.package-

archive"); startActivity(intent);
   
    打开对话框 new AlertDialog.Builder(this).setTitle("传智播客").setMessage("

浏览http://www.itcast.cn") .setPositiveButton("打开链接", new

DialogInterface.OnClickListener(){ public void onClick(DialogInterface

dialoginterface, int i){ Uri uri = Uri.parse("http://www.itcast.cn/"); Intent

intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } }

).show();
    单选框(RadioButton) 单选框(RadioButton) 效果图: 要完成单选框显示,我们需要使

用到RadioGroup和RadioButton(单选框),RadioGroup用于对单选 框进行分组,相同组内的

单选框只有一个单选框能被选中。(例子代码请见下方备注栏) RadioGroup.check

(R.id.dotNet);将id名为dotNet的单选框设置成选中状态。 (RadioButton) findViewById

(radioGroup.getCheckedRadioButtonId());//获取被选中的单选框。

RadioButton.getText();//获取单选框的值 调用setOnCheckedChangeListener()方法,处

理单选框被选择事件,把 RadioGroup.OnCheckedChangeListener实例作为参数传入
 
    多选框(CheckBox) 多选框(CheckBox) 效果图: 每个多选框都是独立的,可以通过迭代

所有多选框,然后根据其状态是否被选中再获取其值。 CheckBox.setChecked(true);//设置

成选中状态。 CheckBox.getText();//获取多选框的值 调用setOnCheckedChangeListener

()方法,处理多选框被选择事件,把 CompoundButton.OnCheckedChangeListener实例作为

参数传入

    下拉列表框(Spinner) 下拉列表框(Spinner) 效果图: Spinner.getItemAtPosition

(Spinner.getSelectedItemPosition());获取下拉列表框的值 调用

setOnItemSelectedListener()方法,处理下拉列表框被选择事件,把

AdapterView.OnItemSelectedListener实例作为参数传入

    下拉列表框—采用javabean作为 下拉列表框—采用javabean作为Adapter元素

javabean作为效果图: 很多时候显示在下拉列表框的值并不是希望得到的值,如果要做一个联

系人下拉列表框,列表框 列出的是联系人的姓名,因为姓名有可能相同,所以我们希望得到

的值应该为该联系人的id,要实 现这种需求我们需要自定义Adapter,当然自定义Adapter需

要我们编写一小段代码,如果我们不想 编写Adapter,又能实现我们的需求,那是最好不过的

了。通过观察ArrayAdapter中getView(int position, View convertView, ViewGroup

parent)的内部代码发现,如果为ArrayAdapter指定的实际泛型 参数类型没有实现

CharSequence(字符串)接口,将会调用该类型对象的toString()向下拉列表框输 出显示值

。利用这个特点我们可以重写javaBean的toString()向下拉列表框提供显示值。

    下拉列表框--自定义选项界面样式 下拉列表框--自定义选项界面样式 -效果图:

Spinner.getItemAtPosition(Spinner.getSelectedItemPosition());获取下拉列表框的值

调用setOnItemSelectedListener()方法,处理下拉列表框被选择事件,把

AdapterView.OnItemSelectedListener实例作为参数传入

    拖动条(SeekBar) 拖动条(SeekBar) 效果图: SeekBar.getProgress()获取拖动条当前

值 调用setOnSeekBarChangeListener()方法,处理拖动条值变化事件,把

SeekBar.OnSeekBarChangeListener实例作为参数传入
    菜单(Menu) 菜单(Menu) 效果图: 重写Activity的onCreateOptionsMenu(Menu menu)

方法,该方法用于创建选项菜单,在用户按下 手机的“Menu”按钮时就会显示创建好的菜单

,在onCreateOptionsMenu(Menu menu)方法内部可 以调用Menu.add()方法实现菜单的添加

。 重写Activity的onMenuItemSelected()方法,该方法用于处理菜单被选择事件
   进度对话框(ProgressDialog) 进度对话框(ProgressDialog) 效果图: 使用代码

ProgressDialog.show(ProgressDialogActivity.this, "请稍等", "数据正在加载中...",

true);创 建并显示一个进度对话框。 调用setProgressStyle()方法设置进度对话框风格。

有两种风格: ProgressDialog.STYLE_SPINNER 旋体进度条风格 (为默认风格)

ProgressDialog.STYLE_HORIZONTAL 横向进度条风格

    创建进度条在布局xml文件中添加进度条代码: <ProgressBar

android:layout_width="fill_parent" android:layout_height="20px" style="?

android:attr/progressBarStyleHorizontal" android:id="@+id/downloadbar"/> 在代码

中操作进度条: ProgressBar.setMax(100);//设置总长度为100

ProgressBar.setProgress(0);//设置已经开启长度为0,假设设置为50,进度条将进行到一半
    android样式和主题(style&theme) android中的样式和CSS样式作用相似,都是用于为界

面元素定义显示风格,它是一个包含一个或 者多个view控件属性的集合。如:需要定义字体

的颜色和大小。在CSS中是这样定义的: <style> .itcast{COLOR:#0000CC;font-

size:18px;} </style> 可以像这样使用上面的css样式:<div class="itcast">传智播客

</div> 在Android中可以这样定义样式: 在res/values/styles.xml文件中添加以下内容

<?xml version="1.0" encoding="utf-8"?> <resources> <style name=“itcast”> <!

-- 为样式定义一个全局唯一的名字--> <item name="android:textSize">18px</item> <!

-- name属性为样式要用在的View控件持有的属性 --> <item

name="android:textColor">#0000CC</item> </style> </resources> 在layout文件中可

以像下面这样使用上面的android样式: <?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ....>

<TextView style="@style/itcast" ..... /> </LinearLayout>
   
    android样式和主题(style&theme) <style>元素中有一个parent属性。这个属性可以让

当前样式继承一个父样式,当前样式可以继承 到父样式的值。当然,如果父样式的值不符合

你的需求,你也可以对它进行修改,如下: <?xml version="1.0" encoding="utf-8"?>

<resources> <style name="itcast"> <item name="android:textSize">18px</item>

<!-- name属性为样式要用在的View控件持有的属性 --> <item

name="android:textColor">#0000CC</item> </style> <style name="subitcast"

parent="@style/itcast"> <item name="android:textColor">#FF0000</item> </style>

</resources>

    android样式和主题(style&theme) android中主题也是用于为应用定义显示风格,它的

定义和样式的定义相同,如下: <?xml version="1.0" encoding="utf-8"?> <resources>

<style name=“itcastTheme"> <item name=“android:windowNoTitle”>true</item> <!

– 没标题 <item name=“android:windowFullscreen”>?android:windowNoTitle</item>

<!– 全屏显示 </style> </resources> 上面“?android:windowNoTitle”中的问号用于引

用在当前主题中定义过的资源的值。下面代码显示在 AndroidManifest.xml中如何为应用设

置上面定义的主题: <application android:icon="@drawable/icon"

android:label="@string/app_name" android:theme="@style/itcastTheme"> ......

</application> 除了可以在AndroidManifest.xml中设置主题,同样也可以在代码中设置主

题,如下: setTheme(R.style.itcastTheme); 尽管在定义上,样式和主题基本相同,但是

它们使用的地方不同。样式用在单独的View,如:EditText、TextView等;主 题通过

AndroidManifest.xml中的<application>和<activity>用在整个应用或者某个 Activity,

主题对整个应用或某个 Activity存在全局性影响。如果一个应用使用了主题,同时应用下的

view也使用了样式,那么当主题与样式属性发生冲突 时,样式的优先级高于主题。 另外

android系统也定义了一些主题,例如:<activity android:theme=

“@android:style/Theme.Dialog”>,该主题可以让 Activity看起来像一个对话框,如果

需要查阅这些主题,可以在文档的reference android-->R.style 中查看。

     Android中的显示单位 px (pixels)像素 一般HVGA代表320x480像素,这个用的比较多

。 dip或dp (device independent pixels)设备独立像素 这个和设备硬件有关,一般为了支

持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。 sp (scaled pixels — best for text

size)比例像素 主要处理字体的大小,可以根据系统的字体自适应。 下面几个不太常用: in

(inches)英寸 mm (millimeters)毫米 pt (points)点,1/72英寸 为了适应不同分辨率,不

同的像素密度,推荐使用dip ,文字使用sp。
 

大小: 1017.5KB

浏览:55次下载:6次

贡献时间:2010-11-16


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
视频名称 源码 -------------------------------------------------------------------------------- 04_开发与运行(卸载)第一个ANDROID应用.avi 所在项目:Hello 06_电话拔号器.avi 所在项目:phone 08_短信发送器.avi 所在项目:sms 09_深入了解各种布局技术.avi 所在项目:sms & FrameLayout 10_对应用进行单元测试.avi 所在项目:junitest 11_查看与输出日志信息.avi 所在项目:junitest 12_文件的保存与读取.avi 所在项目:File 13_文件的操作模式.avi 所在项目:File & other 14_把文件存放在SDCard.avi 所在项目:File 15_采用Pull解析器解析和生成XML内容.avi 所在项目:xml 16_采用SharedPreferences保存用户偏好设置参数.avi 所在项目:SharedPreferences 17_创建数据库与完成数据添删改查.avi 所在项目:db 18_在SQLite中使用事务.avi 所在项目:db 19_采用ListView实现数据列表显示.avi 所在项目:db 20_采用ContentProvider对外共享数据.avi 所在项目:db & other 21_监听ContentProvider中数据的变化.avi 所在项目:db & other & Aapp 22_访问通信录中的联系人和添加联系人.avi 所在项目:contacts 23_网络通信之网络图片查看器.avi 所在项目:netimage & Web端应用:web 24_网络通信之网页源码查看器.avi 所在项目:HtmlViewer & Web端应用:web 25_网络通信之视频资讯客户端.avi 所在项目:news & Web端应用:web 26_采用JSON格式返回数据给资讯客户端.avi 所在项目:news & Web端应用:web 27_网络通信之通过GET和POST方式提交参数给web应用.avi 所在项目:newsmanage & Web端应用:web 28_网络通信之通过HTTP协议实现文件上传.avi 所在项目:newsmanage & Web端应用:web 29_发送xml数据和调用webservice.avi 所在项目:mobileAddressQuery & Web端应用:web 30_多线程下载原理.avi 所在项目:net 31_多线程断点下载器.avi 所在项目:MulThreadDownloader 32_文件断点上传器.avi 所在项目:videoUpload & javaSE应用:socket 33_为应用添加多个Activity与参数传递.avi 所在项目:MulActivity 34_Activity的启动模式.avi 所在项目:LaunchMode & openSingleInstance & singleInstance 35_Intent深入解剖.avi 所在项目:Intent 36_Activity生命周期.avi 所在项目:ActivityLife 37_采用广播接收者实现短信窃听器.avi 所在项目:SMSListener & Web端应用:web 38_采用广播接收者拦截外拔电话与其特性.avi 所在项目:SMSListener 39_采用Service实现电话窃听器.avi 所在项目:phonelistener 40_建立能与访问者进行相互通信的本地服务.avi 所在项目:studentquery 41_使用AIDL实现进程通信.avi 所在项目:remoteService & remoteServiceClient 42_服务的生命周期.avi 43_音乐播放器.avi 所在项目:audioplayer 44_在线视频播放器.avi 所在项目:videoplayer 45_拍照.avi 所在项目:takepicture 46_视频刻录.avi 所在项目:videoRecorder 47_手势识别.avi 所在项目:gesture & GestureBuilder 48_实现软件国际化.avi 所在项目:i18n 49_屏幕适配.avi 所在项目:ScreenAdapter 50_样式与主题.avi 所在项目:style 51_编码实现软件界面.avi 所在项目:codeUI 52_发送状态栏通知.avi 所在项目:Notification 53_采用网页设计软件界面.avi 所在项目:htmlUI 54_tween动画.avi 所在项目:tween 55_frame动画的实现.avi 所在项目:frameAnimation 56_activity切换动画与页面切换动画.avi 所在项目:animation 57_采用方向传感器实现指南针.avi 所在项目:sensor 58_拖拉功能与多点触摸.avi 所在项目:DragScale 59_各种图形的使用介绍.avi 所在项目:drawable 60_meta-data的使用.avi 所在项目:metadata 61_Widget.avi 所在项目:Widgets 62_自定义窗口标题.avi 所在项目:customtitle 63_PopupWindow.avi 所在项目:PopupWindow 64_ListView数据异步加载与AsyncTask.avi 所在项目:DataAsyncLoad 65_ListView数据的分批加载.avi 所在项目:datapageload 66_自定义标签页.avi 所在项目:tabhost

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值