• 1. 北京传智播客教育 www.itcast.cn3G应用开发之Android讲师:黎活明
  • 2. 北京传智播客教育 www.itcast.cn 什么是3G3G,全称为3rd Generation,中文含义就是指第三代数字通信。 所谓3G,是指将无线通信与国际互联网等多媒体通信结合的新一代移动通信系统。 3G只是一种通信技术标准,符合这个标准的技术有WCDMA、CDMA2000、TD-SCDMA三种制式。中国联通使用的是WCDMA(世界上大部分3G网络都采用的是该标准) ;中国电信使用的是CDMA2000 (日、韩和北美使用);中国移动使用的是具有自主知识产权的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) 等技术。
  • 3. 北京传智播客教育 www.itcast.cn 智能手机软件平台智能手机软件平台有: 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%
  • 4. 北京传智播客教育 www.itcast.cn 什么是AndroidAndroid一词的本义指“机器人”,Android是Google于07年11月5日宣布的基于Linux平台开源手机操作系统名称,该平台由操作系统、中间件、用户界面和应用软件组成。在国内,联想、戴尔、多普达、飞利浦、中兴、三星、摩托罗拉、等厂商已经推出基于Android平台的智能手机。 在3G应用开发领域,Android的发展前景不容小视,也将会成为未来主流的手机操作系统。
  • 5. 北京传智播客教育 www.itcast.cn 如何安装 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所在位置。
  • 6. 北京传智播客教育 www.itcast.cn 开发第一个Android应用打开Eclipse,新建项目(点击FileNewProject),在项目列表中展开Android目录,选择Android Project,如下图:
  • 7. 北京传智播客教育 www.itcast.cn 开发第一个Android应用
  • 8. 北京传智播客教育 www.itcast.cn 开发第一个Android应用点击”finish”即可完成项目的创建,创建后的项目已经是一个可运行的Android应用,我们可以通过下面方式运行此应用: 点击工具栏上手机形状的虚拟设备管理器(简称“AVD“),如下:
  • 9. 北京传智播客教育 www.itcast.cn 开发第一个Android应用在打开的虚拟设备管理器中创建一个虚拟手机:
  • 10. 北京传智播客教育 www.itcast.cn 开发第一个Android应用在项目上右键点击run as Android application,如下图:
  • 11. 北京传智播客教育 www.itcast.cn 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 项目环境信息,一般是不需要修改此文件
  • 12. 北京传智播客教育 www.itcast.cn 电话拔号器效果图:
  • 13. 北京传智播客教育 www.itcast.cn 电话拔号器因为应用要使用手机的电话服务,所以要在清单文件AndroidManifest.xml中添加电话服务权限: 略....
  • 14. 北京传智播客教育 www.itcast.cn 电话拔号器界面布局:
  • 15. 北京传智播客教育 www.itcast.cn 电话拔号器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); } }); } }
  • 16. 北京传智播客教育 www.itcast.cn 电话拔号器测试步骤: 1>在Eclipse中运行此应用 2>在Dos窗口中进入android SDK安装路径的tools目录,输入以下命令再开启一个Android模拟器: emulator -data itcast 注:itcast为用户数据存取文件,如果该文件不存在,默认在tools目录创建该文件 3>在电话扰号器中输入上图现显的电话号码
  • 17. 北京传智播客教育 www.itcast.cn “尚未注册网络”错误信息的解决办法打开Android模拟器时,出现无信号,拔打电话或发短信时,提示“尚未注册网络”错误信息的解决方案如下。 场景一:你的电脑没有连接上互联网,同时也没有在局域网。 解决办法:右键点击网上邻居,选择"属性",在网络连接窗口中右键点击"本地连接",选择"属性",设置TCP/IP属性如下: IP地址:192.168.1.100 子网掩码:255.255.255.0 默认网关:192.168.1.100 首选DNS服务器:192.168.1.100 场景二:你的电脑没有连接上互联网,但在局域网。 解决办法:右键点击网上邻居,选择"属性",在网络连接窗口中右键点击"本地连接",选择"属性",设置TCP/IP属性如下: IP地址:设置成你所在局域网的IP,如:192.168.1.100 子网掩码:设置成你所在局域网的掩码,如:255.255.255.0 默认网关:设置成你所在局域网的网关,一般网关的IP格式为:*.*.*.1,如:192.168.1.1 首选DNS服务器:设置成你所在局域网的路由器IP,一般路由器的IP格式为:*.*.*.1,如:192.168.1.1 最后一种解决方案是:让你的电脑连接上互联网。
  • 18. 北京传智播客教育 www.itcast.cn 短信发送器效果图:
  • 19. 北京传智播客教育 www.itcast.cn 短信发送器因为应用要使用手机的短信服务,所以要在清单文件AndroidManifest.xml中添加短信服务权限: 略....
  • 20. 北京传智播客教育 www.itcast.cn 短信发送器界面布局:
  • 21. 北京传智播客教育 www.itcast.cn 短信发送器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 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();
  • 22. 北京传智播客教育 www.itcast.cn 短信发送器测试步骤: 1>在Eclipse中运行此应用 2>在Dos窗口中进入android SDK安装路径的tools目录,输入以下命令再开启一个Android模拟器: emulator -data itcast 注:itcast为用户数据存取文件,如果该文件不存在,默认在tools目录创建该文件 3>在短信发送器的手机号中输入上图现显的电话号码 注:目前Android系统对中文短信尚未支持,所以发送中文短信会有乱码,这个问题日后会被解决的。
  • 23. 北京传智播客教育 www.itcast.cn 对应用进行单元测试第一步:首先在AndroidManifest.xml中加入下面红色代码: .... 上面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); } }
  • 24. 北京传智播客教育 www.itcast.cn 数据存储与访问很多时候我们的软件需要对处理后的数据进行存储或再次访问。Android为数据存储提供了多种方式,分别有如下几种: 文件 SharedPreferences SQLite数据库 内容提供者(Content provider) 网络
  • 25. 北京传智播客教育 www.itcast.cn 使用文件进行数据存储首先给大家介绍使用文件如何对数据进行存储,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//files目录,如: /data/data/cn.itcast.action/files/itcast.txt ,通过点击Eclipse菜单“Window”-“Show View”-“Other”,在对话窗口中展开android文件夹,选择下面的File Explorer视图,然后在File Explorer视图中展开/data/data//files目录就可以看到该文件。 openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为: Context.MODE_PRIVATE = 0 Context.MODE_APPEND = 32768 Context.MODE_WORLD_READABLE = 1 Context.MODE_WORLD_WRITEABLE = 2
  • 26. 北京传智播客教育 www.itcast.cn 使用文件进行数据存储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//files),其他程序无法访问。除非在创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE ,只有这样其他程序才能正确访问。
  • 27. 北京传智播客教育 www.itcast.cn 读取文件内容如果要打开存放在/data/data//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//cache目录 getFilesDir()方法用于获取/data/data//files目录
  • 28. 北京传智播客教育 www.itcast.cn 把文件存放在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的权限如下:
  • 29. 北京传智播客教育 www.itcast.cn 把文件存放在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();
  • 30. 北京传智播客教育 www.itcast.cn 使用SAX或者DOM或者pull解析XML文件在Android平台上可以使用Simple API for XML(SAX) 、 Document Object Model(DOM)和Android附带的pull解析器解析XML文件。 下面是本例子要解析的XML文件: 文件名称:itcast.xml 李明 30 李向梅 25 例子定义了一个javabean用于存放上面解析出来的xml内容, 这个javabean为Person,代码请见本页下面备注:
  • 31. 北京传智播客教育 www.itcast.cn 使用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)就可以获取内容。
  • 32. 北京传智播客教育 www.itcast.cn 使用SAX读取XML文件只要为SAX提供实现ContentHandler接口的类,那么该类就可以得到通知事件(实际上就是SAX调用了该类中的回调方法)。因为ContentHandler是一个接口,在使用的时候可能会有些不方便,因此,SAX还为其制定了一个Helper类:DefaultHandler,它实现了ContentHandler接口,但是其所有的方法体都为空,在实现的时候,你只需要继承这个类,然后重写相应的方法即可。使用SAX解析itcast.xml的代码如下: public static List 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的代码实现请看本页下面备注。
  • 33. 北京传智播客教育 www.itcast.cn 使用DOM读取XML文件除了可以使用 SAX解析XML文件,大家也可以使用熟悉的DOM来解析XML文件。 DOM解析XML文件时,会将XML文件的所有内容以对象树方式存放在内存中,然后允许您使用DOM API遍历XML树、检索所需的数据。使用DOM操作XML的代码看起来比较直观,并且,在某些方面比基于SAX的实现更加简单。但是,因为DOM需要将XML文件的所有内容以对象树方式存放在内存中,所以内存的消耗比较大,特别对于运行Android的移动设备来说,因为设备的资源比较宝贵,所以建议还是采用SAX来解析XML文件,当然,如果XML文件的内容比较小采用DOM是可行的。 代码请看本页下方备注
  • 34. 北京传智播客教育 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/
  • 35. 北京传智播客教育 www.itcast.cn 使用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();
  • 36. 北京传智播客教育 www.itcast.cn 使用SharedPreferences进行数据存储很多时候我们开发的软件需要向用户提供软件参数设置功能,例如我们常用的QQ,用户可以设置是否允许陌生人添加自己为好友。对于软件配置参数的保存,如果是window软件通常我们会采用ini文件进行保存,如果是j2se应用,我们会采用properties属性文件或者xml进行保存。如果是Android应用,我们最适合采用什么方式保存软件配置参数呢?Android平台给我们提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。使用SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data//shared_prefs目录下: SharedPreferences sharedPreferences = getSharedPreferences("itcast", Context.MODE_PRIVATE); Editor editor = sharedPreferences.edit();//获取编辑器 editor.putString("name", "传智播客"); editor.putInt("age", 4); editor.commit();//提交修改 生成的itcast.xml文件内容如下: 传智播客 因为SharedPreferences背后是使用xml文件保存数据,getSharedPreferences(name,mode)方法的第一个参数用于指定该文件的名称,名称不用带后缀,后缀会由Android自动加上。方法的第二个参数指定文件的操作模式,共有四种操作模式,这四种模式前面介绍使用文件方式保存数据时已经讲解过。如果希望SharedPreferences背后使用的xml文件能被其他应用读和写,可以指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。 另外Activity还提供了另一个getPreferences(mode)方法操作SharedPreferences,这个方法默认使用当前类不带包名的类名作为文件的名称。
  • 37. 北京传智播客教育 www.itcast.cn 访问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权限。如:有个为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//shared_prefs/itcast.xml”);//应替换成应用的包名
  • 38. 北京传智播客教育 www.itcast.cn 使用嵌入式关系型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
  • 39. 北京传智播客教育 www.itcast.cn 使用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()方法先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库。
  • 40. 北京传智播客教育 www.itcast.cn 使用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()方法在数据库版本每次发生变化时都会把用户手机上的数据库表删除,然后再重新创建。一般在实际项目中是不能这样做的,正确的做法是在更新数据库表结构时,还要考虑用户存放于数据库中的数据不会丢失。
  • 41. 北京传智播客教育 www.itcast.cn 使用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语句中占位符参数的值,参数值在数组中的顺序要和占位符的位置对应。
  • 42. 北京传智播客教育 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 ) 。
  • 43. 北京传智播客教育 www.itcast.cn 使用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。
  • 44. 北京传智播客教育 www.itcast.cn 使用SQLiteDatabase操作SQLite数据库delete()方法的使用: SQLiteDatabase db = databaseHelper.getWritableDatabase(); db.delete("person", "personid
  • 45. 北京传智播客教育 www.itcast.cn 使用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关键字后面的部分。
  • 46. 北京传智播客教育 www.itcast.cn 使用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()方法得到的都是同一实例。
  • 47. 北京传智播客教育 www.itcast.cn 使用事务操作SQLite数据库使用SQLiteDatabase的beginTransaction()方法可以开启一个事务,程序执行到endTransaction() 方法时会检查事务的标志是否为成功,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标志为成功则提交事务,如果没有调用setTransactionSuccessful() 方法则回滚事务。使用例子如下: SQLiteDatabase db = ....; db.beginTransaction();//开始事务 try { db.execSQL("insert into person(name, age) values(?,?)", new Object[]{"传智播客", 4}); db.execSQL("update person set name=? where personid=?", new Object[]{"传智", 1}); db.setTransactionSuccessful();//调用此方法会在执行到endTransaction() 时提交当前事务,如果不调用此方法会回滚事务 } finally { db.endTransaction();//由事务的标志决定是提交事务,还是回滚事务 } db.close(); 上面两条SQL语句在同一个事务中执行。
  • 48. 北京传智播客教育 www.itcast.cn 使用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使用对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider , ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,你可以把 ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名: 注意:一旦应用继承了ContentProvider类,后面我们就会把这个应用称为ContentProvider(内容提供者)。
  • 49. 北京传智播客教育 www.itcast.cn 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或网络等他存储方式,如下: 要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name 如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下: Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person")
  • 50. 北京传智播客教育 www.itcast.cn 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 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
  • 51. 北京传智播客教育 www.itcast.cn 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
  • 52. 北京传智播客教育 www.itcast.cn 使用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”。
  • 53. 北京传智播客教育 www.itcast.cn 使用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的记录。
  • 54. 北京传智播客教育 www.itcast.cn 使用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);
  • 55. 北京传智播客教育 www.itcast.cn从Internet获取数据利用HttpURLConnection对象,我们可以从网络中获取网页数据. URL url = new URL("http://www.sohu.com"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5* 1000);//设置连接超时 conn.setRequestMethod(“GET”);//以get方式发起请求 if (conn.getResponseCode() != 200) throw new RuntimeException("请求url失败"); InputStream is = conn.getInputStream();//得到网络返回的输入流 String result = readData(is, "GBK"); conn.disconnect(); //第一个参数为输入流,第二个参数为字符集编码 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); }
  • 56. 北京传智播客教育 www.itcast.cn从Internet获取数据利用HttpURLConnection对象,我们可以从网络中获取文件数据. URL url = new URL("http://photocdn.sohu.com/20100125/Img269812337.jpg"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5* 1000); conn.setRequestMethod("GET"); 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(); }
  • 57. 北京传智播客教育 www.itcast.cn多线程下载使用多线程下载文件可以更快完成文件的下载,多线程下载文件之所以快,是因为其抢占的服务器资源多。如:假设服务器同时最多服务100个用户,在服务器中一条线程对应一个用户,100条线程在计算机中并非并发执行,而是由CPU划分时间片轮流执行,如果A应用使用了99条线程下载文件,那么相当于占用了99个用户的资源,假设一秒内CPU分配给每条线程的平均执行时间是10ms,A应用在服务器中一秒内就得到了990ms的执行时间,而其他应用在一秒内只有10ms的执行时间。就如同一个水龙头,每秒出水量相等的情况下,放990毫秒的水 肯定比放10毫秒的水要多。 多线程下载的实现过程: 1>首先得到下载文件的长度,然后设置本地文件 的长度。 HttpURLConnection.getContentLength(); RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rw"); file.setLength(filesize);//设置本地文件的长度 2>根据文件长度和线程数计算每条线程下载的数据长度和下载位置。如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如上图所示。 3>使用Http的Range头字段指定每条线程从文件的什么位置开始下载,如:指定从文件的2M位置开始下载文件,代码如下: HttpURLConnection.setRequestProperty("Range", "bytes=2097152-"); 4>保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。 RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rw"); threadfile.seek(2097152);//从文件的什么位置开始写入数据
  • 58. 北京传智播客教育 www.itcast.cn向Internet发送请求参数利用HttpURLConnection对象,我们可以向网络发送请求参数. String requestUrl = "http://localhost:8080/itcast/contanctmanage.do"; Map requestParams = new HashMap(); requestParams.put("age", "12"); requestParams.put("name", "中国"); StringBuilder params = new StringBuilder(); for(Map.Entry 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); outStream.flush(); if( conn.getResponseCode() == 200 ){ String result = readAsString(conn.getInputStream(), "UTF-8"); outStream.close(); System.out.println(result); }
  • 59. 北京传智播客教育 www.itcast.cn向Internet发送xml数据利用HttpURLConnection对象,我们可以向网络发送xml数据. StringBuilder xml = new StringBuilder(); xml.append(""); xml.append(""); xml.append("中国"); xml.append(""); 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(5* 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"); outStream.close();
  • 60. 北京传智播客教育 www.itcast.cn 为应用添加新的Activity第一步:新建一个继承Activity的类,如:NewActivity public class NewActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //这里可以使用setContentView(R.layout.xxx)显示某个视图.... } } 第二步:需要在功能清单AndroidManifest.xml文件中添加进上面Activity配置代码(红色部分): ..... ... android:name属性值的前面加了一个点表示NewActivity是当前包cn.itcast.action下的类,如果类在应用的当前包下,可以省略点符号,如果类在应用的子包下必须加点,如:NewActivity类在cn.itcast.action.user包下可以这样写:
  • 61. 北京传智播客教育 www.itcast.cn 打开新的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)); }}); } }
  • 62. 北京传智播客教育 www.itcast.cn 打开新的Activity,并传递若干个参数给它第二种:打开新的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"); } }
  • 63. 北京传智播客教育 www.itcast.cn Bundle类的作用Bundle类用作携带数据,它类似于Map,用于存放key-value名值对形式的值。相对于Map,它提供了各种常用类型的putXxx()/getXxx()方法,如:putString()/getString()和putInt()/getInt(),putXxx()用于往Bundle对象放入数据,getXxx()方法用于从Bundle对象里获取数据。Bundle的内部实际上是使用了HashMap类型的变量来存放putXxx()方法放入的值: public final class Bundle implements Parcelable, Cloneable { ...... Map mMap; public Bundle() { mMap = new HashMap(); ...... } 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()方法会把转换后的值返回。
  • 64. 北京传智播客教育 www.itcast.cn 为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; }
  • 65. 北京传智播客教育 www.itcast.cn 得到新打开Activity 关闭后返回的数据如果你想在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返回的数据。
  • 66. 北京传智播客教育 www.itcast.cn 得到新打开Activity 关闭后返回的数据使用startActivityForResult(Intent intent, int requestCode)方法打开新的Activity,新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; }
  • 67. 北京传智播客教育 www.itcast.cn 请求码的作用使用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的请求,作相应业务处理 } } }
  • 68. 北京传智播客教育 www.itcast.cn 结果码的作用在一个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的返回数据 } } }
  • 69. 北京传智播客教育 www.itcast.cn Intent(意图)Android基本的设计理念是鼓励减少组件间的耦合,因此Android提供了Intent (意图) ,Intent提供了一种通用的消息系统,它允许在你的应用程序与其它的应用程序间传递Intent来执行动作和产生事件。使用Intent可以激活Android应用的三个核心组件:活动、服务和广播接收器。 Intent可以划分成显式意图和隐式意图。 显式意图:调用Intent.setComponent()或Intent.setClass()方法明确指定了组件名的Intent为显式意图,显式意图明确指定了Intent应该传递给哪个组件。 隐式意图:没有明确指定组件名的Intent为隐式意图。 Android系统会根据隐式意图中设置的动作(action)、类别(category)、数据(URI和数据类型)找到最合适的组件来处理这个意图。查找规则请见ppt下方备注。
  • 70. 北京传智播客教育 www.itcast.cnActivity生命周期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()
  • 71. 北京传智播客教育 www.itcast.cnActivity生命周期这七个方法定义了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下方备注栏。
  • 72. 北京传智播客教育 www.itcast.cnActivity生命周期
  • 73. 北京传智播客教育 www.itcast.cn广播接收者--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文件中的节点里进行订阅:
  • 74. 北京传智播客教育 www.itcast.cn使用广播接收者窃听短信如果你想窃听别人接收到的短信,达到你不可告人的目的,那么本节内容可以实现你的需求。 当系统收到短信时,会发出一个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文件中的节点里对接收到短信的广播Intent进行订阅: 在AndroidManifest.xml文件中添加以下权限:
  • 75. 北京传智播客教育 www.itcast.cn广播接收者除了短信到来广播Intent,Android还有很多广播Intent,如:开机启动、电池电量变化、时间已经改变等广播Intent。 接收电池电量变化广播Intent ,在AndroidManifest.xml文件中的节点里订阅此Intent: 接收开机启动广播Intent,在AndroidManifest.xml文件中的节点里订阅此Intent: 并且要进行权限声明:
  • 76. 北京传智播客教育 www.itcast.cn广播接收者通常一个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); } }
  • 77. 北京传智播客教育 www.itcast.cn服务--ServiceAndroid中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的程序。服务的开发比较简单,如下: 第一步:继承Service类 public class SMSService extends Service { } 第二步:在AndroidManifest.xml文件中的节点里对服务进行配置: 服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。 如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。 如果打算采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()-->onDestroy()方法。
  • 78. 北京传智播客教育 www.itcast.cn服务--Service服务常用生命周期回调方法如下: onCreate() 该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。 onDestroy()该方法在服务被终止时调用。 与采用Context.startService()方法启动服务有关的生命周期方法 onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽管不会多次创建服务,但onStart() 方法会被多次调用。 与采用Context.bindService()方法启动服务有关的生命周期方法 onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。 onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。
  • 79. 北京传智播客教育 www.itcast.cn采用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); }}); } }
  • 80. 北京传智播客教育 www.itcast.cn采用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);//解除绑定 }}); } }
  • 81. 北京传智播客教育 www.itcast.cn电话窃听器要实现电话窃听,需要监听电话的状态,方法如下: /* 取得电话服务 */ 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中添加权限:
  • 82. 北京传智播客教育 www.itcast.cn音频采集你可以使用手机进行现场录音,实现步骤如下: 第一步:在功能清单文件AndroidManifest.xml中添加音频刻录权限: 第二步:编写音频刻录代码: 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(); //刻录完成一定要释放资源
  • 83. 北京传智播客教育 www.itcast.cn音乐播放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; } });
  • 84. 北京传智播客教育 www.itcast.cn音视频采集第一步:在功能清单文件AndroidManifest.xml中添加音频刻录和照相机权限: 第二步:编写音频刻录代码: 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(); //刻录完成一定要释放资源
  • 85. 北京传智播客教育 www.itcast.cnAndroid的状态栏通知(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);//第一个参数为图标,第二个参数为标题,第三个为通知时间 notification.defaults = Notification.DEFAULT_SOUND;//发出默认声音 Intent openintent = new Intent(this, OtherActivity.class); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, openintent, 0);//当点击消息时就会向系统发送openintent意图 notification.setLatestEventInfo(this, “标题”, “我是内容", contentIntent); mNotificationManager.notify(0, notification);
  • 86. 北京传智播客教育 www.itcast.cn对话框通知(Dialog Notification)当你的应用需要显示一个进度条或需要用户对信息进行确认时,可以使用对话框来完成。 下面代码将打开一个如右图所示的对话框: new AlertDialog.Builder(context) .setTitle("java培训") .setCancelable(false) //设置不能通过“后退”按钮关闭对话框 .setMessage("浏览传智播客网站?") .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); } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }) .show();//显示对话框 上面代码采用的是一个链式调用,像setTitle()、setMessage()这些方法,他们的返回值都是当前对话框对象。
  • 87. 北京传智播客教育 www.itcast.cn创建带单选项列表的对话框下面代码将打开一个如右上图所示的选项列表对话框: final String[] items = {"java", ".net", "php"}; new AlertDialog.Builder(SenderNotificationActivity.this).setTitle("选择语言") .setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show(); } }).show();//显示对话框 下面代码将打开一个如右下图所示的带单选框的列表对话框: final String[] items = {"java", ".net", "php"}; new AlertDialog.Builder(SenderNotificationActivity.this).setTitle("选择语言") .setSingleChoiceItems(items, 1, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show(); dialog.cancel(); } }).show();//显示对话框 setSingleChoiceItems()的第二个参数是设置默认选项, 选项索引从0开始,-1代表不选择任何选项。
  • 88. 北京传智播客教育 www.itcast.cn创建带多选项列表的对话框下面代码将打开一个如右下图所示的多选项列表对话框: final String[] items = {"java", ".net", "php"}; new AlertDialog.Builder(SenderNotificationActivity.this).setCancelable(false) .setTitle("选择语言") .setMultiChoiceItems(items, new boolean[]{false,true,false}, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { if(isChecked){ Toast.makeText(getApplicationContext(), items[which], Toast.LENGTH_SHORT).show(); } } }) .setPositiveButton("确认", new DialogInterface.OnClickListener(){ public void onClick(DialogInterface dialoginterface, int i){ dialoginterface.dismiss(); } }) .show();//显示对话框
  • 89. 北京传智播客教育 www.itcast.cn进度对话框(ProgressDialog)效果图: 使用代码ProgressDialog.show(ProgressDialogActivity.this, "请稍等", "数据正在加载中...", true);创建并显示一个进度对话框。 调用setProgressStyle()方法设置进度对话框风格。有两种风格: ProgressDialog.STYLE_SPINNER 旋体进度条风格 (为默认风格) ProgressDialog.STYLE_HORIZONTAL 横向进度条风格
  • 90. 北京传智播客教育 www.itcast.cn单选框(RadioButton)效果图: 要完成单选框显示,我们需要使用到RadioGroup和RadioButton(单选框),RadioGroup用于对单选框进行分组,相同组内的单选框只有一个单选框能被选中。(例子代码请见下方备注栏) RadioGroup.check(R.id.dotNet);将id名为dotNet的单选框设置成选中状态。 (RadioButton) findViewById(radioGroup.getCheckedRadioButtonId());//获取被选中的单选框。 RadioButton.getText();//获取单选框的值 调用setOnCheckedChangeListener()方法,处理单选框被选择事件,把RadioGroup.OnCheckedChangeListener实例作为参数传入
  • 91. 北京传智播客教育 www.itcast.cn多选框(CheckBox)效果图: 每个多选框都是独立的,可以通过迭代所有多选框,然后根据其状态是否被选中再获取其值。 CheckBox.setChecked(true);//设置成选中状态。 CheckBox.getText();//获取多选框的值 调用setOnCheckedChangeListener()方法,处理多选框被选择事件,把CompoundButton.OnCheckedChangeListener实例作为参数传入
  • 92. 北京传智播客教育 www.itcast.cn下拉列表框(Spinner)效果图: Spinner.getItemAtPosition(Spinner.getSelectedItemPosition());获取下拉列表框的值 调用setOnItemSelectedListener()方法,处理下拉列表框被选择事件,把AdapterView.OnItemSelectedListener实例作为参数传入
  • 93. 北京传智播客教育 www.itcast.cn下拉列表框—采用javabean作为Adapter元素效果图: 很多时候显示在下拉列表框的值并不是希望得到的值,如果要做一个联系人下拉列表框,列表框列出的是联系人的姓名,因为姓名有可能相同,所以我们希望得到的值应该为该联系人的id,要实现这种需求我们需要自定义Adapter,当然自定义Adapter需要我们编写一小段代码,如果我们不想编写Adapter,又能实现我们的需求,那是最好不过的了。通过观察ArrayAdapter中getView(int position, View convertView, ViewGroup parent)的内部代码发现,如果为ArrayAdapter指定的实际泛型参数类型没有实现CharSequence(字符串)接口,将会调用该类型对象的toString()向下拉列表框输出显示值。利用这个特点我们可以重写javaBean的toString()向下拉列表框提供显示值。
  • 94. 北京传智播客教育 www.itcast.cn下拉列表框--自定义选项界面样式效果图: Spinner.getItemAtPosition(Spinner.getSelectedItemPosition());获取下拉列表框的值 调用setOnItemSelectedListener()方法,处理下拉列表框被选择事件,把AdapterView.OnItemSelectedListener实例作为参数传入
  • 95. 北京传智播客教育 www.itcast.cn拖动条(SeekBar)效果图: SeekBar.getProgress()获取拖动条当前值 调用setOnSeekBarChangeListener()方法,处理拖动条值变化事件,把SeekBar.OnSeekBarChangeListener实例作为参数传入
  • 96. 北京传智播客教育 www.itcast.cn菜单(Menu)效果图: 重写Activity的onCreateOptionsMenu(Menu menu)方法,该方法用于创建选项菜单,在用户按下手机的“Menu”按钮时就会显示创建好的菜单,在onCreateOptionsMenu(Menu menu)方法内部可以调用Menu.add()方法实现菜单的添加。 重写Activity的onMenuItemSelected()方法,该方法用于处理菜单被选择事件
  • 97. 北京传智播客教育 www.itcast.cn创建进度条在布局xml文件中添加进度条代码: 在代码中操作进度条: ProgressBar.setMax(100);//设置总长度为100 ProgressBar.setProgress(0);//设置已经开启长度为0,假设设置为50,进度条将进行到一半
  • 98. 北京传智播客教育 www.itcast.cnandroid样式和主题(style&theme) android中的样式和CSS样式作用相似,都是用于为界面元素定义显示风格,它是一个包含一个或者多个view控件属性的集合。如:需要定义字体的颜色和大小。 在CSS中是这样定义的: 可以像这样使用上面的css样式:
    传智播客
    在Android中可以这样定义样式: 在res/values/styles.xml文件中添加以下内容 在layout文件中可以像下面这样使用上面的android样式:
  • 99. 北京传智播客教育 www.itcast.cnandroid样式和主题(style&theme)
  • 100. 北京传智播客教育 www.itcast.cnandroid样式和主题(style&theme) android中主题也是用于为应用定义显示风格,它的定义和样式的定义相同,如下: 上面“?android:windowNoTitle”中的问号用于引用在当前主题中定义过的资源的值。下面代码显示在AndroidManifest.xml中如何为应用设置上面定义的主题: ...... 除了可以在AndroidManifest.xml中设置主题,同样也可以在代码中设置主题,如下: setTheme(R.style.itcastTheme); 尽管在定义上,样式和主题基本相同,但是它们使用的地方不同。样式用在单独的View,如:EditText、TextView等;主题通过AndroidManifest.xml中的用在整个应用或者某个 Activity,主题对整个应用或某个Activity进行全局性影响。如果一个应用使用了主题,同时应用下的view也使用了样式,那么当主题与样式属性发生冲突时,样式的优先级高于主题。 另外android系统也定义了一些主题,例如:,该主题可以让Activity看起来像一个对话框,如果需要查阅这些主题,可以在文档的referenceandroid-->R.style 中查看。
  • 101. 北京传智播客教育 www.itcast.cnAndroid中的显示单位 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。
  • 102. 北京传智播客教育 www.itcast.cn使用html作为UI因为android软件开发分工目前还没有细化,程序员往往需要负责软件界面的开发,尽管软件的界面图片已经由美工设计好了,但如果使用layout技术把软件做成如图片所示的界面确实很困难,而且也比较耗时。Android通过WebView实现了JS代码与Java代码互相通信的功能,使的android软件的界面开发也可以采用HTML网页技术,这样,广大网页美工可以参与进android软件的界面开发工作,从而把程序员从中解脱出来。
  • 103. 北京传智播客教育 www.itcast.cn 安装外部程序首先需要AndroidManifest.xml中加入安装程序权限: 第二步把安装程序添加进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);