当前位置:首页 >> 信息与通信 >>

Android—3G


2011

Android2.X 学习资料
作者:李顺(Eson)

仅供参考与交流 尊重版权?谢谢合作

微软用户 [选取日期]

微软用户 微软中国 2011-3-30

Android 笔记整理, 黎活明: 自学 Android 笔记整理,讲师传智播客 黎活明:<

br />一. Android 的概述 1. 2. 3. 4. 5. 3G 的概念:手机的上网带宽速度越来越快、运算能力、稳定的时代。 (1G—>3G) IT 发展的黄金时代:PC 时代、互联网时代、3G 物联网时代 Android 的基本概念:操作系统、移动设备、内核为 Linux、开源 Google 和手机联盟 Android 体系结构:内核级它就支持 Java 应用程序 Java 的核心内库 虚拟机: 虚拟机:SDK JVM 系统库: 系统库:SQLite 内核为: 内核为:Linux

二. Android 的基本开发 1. 2. 3. 4. 5. 开发环境:Android SDK(模拟器) 集成开发环境:IDE (Eclipse+ADT plug in) Android 虚拟设置:AVD(Android Virtual Devices) 一般小应用的 Android 开发的顺序:先设计界面——>设计 Activity——>业务层代码

做项目的 Android 开发顺序:业务层代码(Android Junit)——>先设计界面——>设计 Activity

三. Android Application 结构 1. 2. AndroidManifest.xml(Android 的配置文件) res(资源文件):drawables(图片资源:分辨率,有高、低、中三个分辨率) values(属性文件) layout(布局文件) 3. gen: R.java(目的:使访问资源变得更加容易,内部全部是完全静态的内部类)

小例子—电话拨号器和短信发送器 四. 小例子—电话拨号器和短信发送器 示例 1:电话拨号器

1.

因为应用要使用手机的电话服务, 中添加电话服务权限: 因为应用要使用手机的电话服务,所以要在清单文件的 AndroidManifest.xml 中添加电话服务权限: <uses<uses-permission android:name="android.permission.CALL_PHONE"/> "android.permission.CALL_PHONE"

2.

模拟器: 有两种方式启动 Androi 模拟器: 在 Eclipse 中设置 目录( 环境变量) 在 Dos 窗口中进入 android SDK 安装路径的 tools 目录(或设置 path 环境变量) 输入 emulator –data itcast 为用户数据存在文件,如果该文件不存在, 目录创建该文件) (注意:itcast 为用户数据存在文件,如果该文件不存在,默认在 tools 目录创建该文件) 注意:

3.

代码为,为按钮增加一个单击事件: 核心的 Activity 代码为,为按钮增加一个单击事件: myButton.setOnClickListener(new OnClickListener() { new @Override public void onClick(View v) { String mobile=mobileText.getText().toString();

//第一个参数打电话,第二个参数是电话号码 Intent intent=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+mobile)); new startActivity(intent); } }); 4. 输入手机号就可以打电话, 模拟器之间进行测试 输入手机号就可以打电话,可以另外开启一个模拟器进行测试 5554 与 5556 模拟器之间进行测试

示例 2:短信发送器

1. 2. 3.

如果用户输入的内容比较多,需要拆成多个字符串, 如果用户输入的内容比较多,需要拆成多个字符串,默认的接受时 70 个字符 得到短信发送器,拆分短信内容,发送短信 得到短信发送器, 拆分短信内容, smsManager.sendTextMessage( 第一个参数接受短信的手机号,第二个短信中心的服务号码, smsManager.sendTextMessage(…)第一个参数接受短信的手机号,第二个短信中心的服务号码,第 三个短信内容,第四个短信发送是否成功意图, 三个短信内容,第四个短信发送是否成功意图,第五个短信接受是否成功的意图

4.

因为应用要使用手机的短信服务, 中添加短信服务: 因为应用要使用手机的短信服务,所以要在清单文件 AndroidManifest.xml 中添加短信服务: <usesandroid:name= <uses-permission android:name="android.permission.SEND_SMS"/>

5.

核心代码: 核心代码:按钮的单击事件 myButton.setOnClickListener(new OnClickListener() { new @Override public void onClick(View v) { String mobile=mobileText.getText().toString(); String context=contextText.getText().toString(); SmsManager smsManager=SmsManager.getDefault();//得到短信发送器 得到短信发送器 if(context.length()>70){ //拆分短信内容 if 拆分短信内容 List<String> contexts=smsManager.divideMessage(context); for (String sms:contexts) { smsManager.sendTextMessage(mobile, null sms, null null null, null, null); } }else else{ else smsManager.sendTextMessage(mobile, null context, null null null, null, null); } Toast.makeText(SMSMessage.this "发送成功", Toast.LENGTH_LONG).show(); this, this } }); //要注意在多个布局文件中要指定布局文件 //要注意在多个布局文件中要指定布局文件

五. 日志输出和单元测试 1. 一般的日志应该可以掌握,但是要注意的一点: 一般的日志应该可以掌握,但是要注意的一点: 定义标签: String 定义标签:public static final String TAG; ) log.i(TAG, "当前的 Activity")

2. 3. 4.

System.out.println();标签是 可以使用 System.out.println();标签是 system.out 之后, 当我们编写业务 bean 之后,一定要对业务 bean 进行单元测试 直接抛出异常就可以了, JunitRun——>LogTest(实例化 ——>testCase() ——>LogTest(实例化) 直接抛出异常就可以了,,因为 JunitRun——>LogTest(实例化)——>testCase() JunitRun{ Run(){ Try{ testCase() }catch(){

//打印在 //打印在 DDMS 的 LogCat } } } 示例 3:单元测试 A. AndroidManifest.xml 中加入代码: 首先在 AndroidManifest.xml 中加入代码:

<application android:icon="@drawable/love" android:label="@string/app_name"> <uses-library android:name="android.test.runner"/> …. </application> <instrumentation android:name="android.test.instrumentationTestRunner" android:targetPackage="com.lishun.activity" //此处一定要与应用的package相同 android:label="TestForMyApp"/> B. 编写单元测试代码(选择测试的方法,右键点击“RunAS”——>Abndroid JunitTest) 编写单元测试代码(选择测试的方法,右键点击“RunAS”——>Abndroid JunitTest) public class LogTest extends AndroidTestCase { private static final String TAG="LogTest"; public public void testSave() throws Exception{ Log.i(TAG,"result="+888); } } 六. 数据存储与访问 1. 很多时候我们的软件需要对处理后的数据进行存储或再次访问, 为数据的存储提供了多种方式, 很多时候我们的软件需要对处理后的数据进行存储或再次访问,android 为数据的存储提供了多种方式, 有如下几种: 有如下几种: 文件: 文件、 文件:text 文件、xml 文件等 SharedPreferences: 共享参数)专门用于保存类似于软件参数设置的内容, (共享参数 SharedPreferences: 共享参数)专门用于保存类似于软件参数设置的内容,用这个方便一些 ( 数据库: SQLite 数据库:嵌入式数据库 内容提供者( provider : vider) :作用对外共享数据的使用 内容提供者(Content provider) 作用对外共享数据的使用 网络: 网络:通过网络去保存数据 2. 3. 的模式: 数据库保存使用 MVC 的模式:业务层代码(Android Junit)——>先设计界面——>设计 Activity 如何对数据进行存储, openFileOutput()方法可以用于把数据输出到文件中 方法可以用于把数据输出到文件中, 如何对数据进行存储,Activity 提供了 openFileOutput()方法可以用于把数据输出到文件中,具体的实 的环境中保存数据到文件是一样的: 现过程与在 J2EE 的环境中保存数据到文件是一样的: FileOutputStream outStream=this.getContext().openFileOutput(文件名 操作模式); this.getContext().openFileOutput(文件名, FileOutputStream outStream=this.getContext().openFileOutput(文件名, 操作模式); A. 第一个参数指定文件名称, 能包含路线的分隔符“ 第一个参数指定文件名称,不能包含路线的分隔符“/“,如果文件不存在 Android 会自动创建 创建的路径在/data/data/<package 目录, 创建的路径在/data/data/<package name>/files 目录,在 File Explorer 视图可以看到 B. 第二个参数指定操作模式,有四种模式,分别为: 第二个参数指定操作模式,有四种模式,分别为: Context.MODE_PRIVATE=0:为默认的操作,代表文件是私有数据,代表只能够被本身创 Context.MODE_PRIVATE=0:为默认的操作,代表文件是私有数据,代表只能够被本身创 只能够被本身 建的对象使用,在改模式下, 建的对象使用,在改模式下,写入的内容会覆盖原有的内容 Context.MODE_APPEND=32768:如何文件不存在,会创建该文件;如何存在, Context.MODE_APPEND=32768:如何文件不存在,会创建该文件;如何存在,就追加文 //直接抛出异常就可以了,上面说明了原因 直接抛出异常就可以了, 直接抛出异常就可以了 //引入测试库

件,这个模式也是私有的,只能被创建的对象使用 这个模式也是私有的, Context.MODE_WORLD_READABLE=1:代表你创建出的文件, Context.MODE_WORLD_READABLE=1:代表你创建出的文件,可以被其它的应用读取 =1 Context.MODE_WORLD_WRITEABLE=2:代表你创建出的文件, Context.MODE_WORLD_WRITEABLE=2:代表你创建出的文件,可以被其它的应用写 示例 4:对文件进行读写读取操作

A.

文件中: 先对文件进行如图所示的布局设置在 main.xml 文件中:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content" > //内部有两个文本框(文件名称)和编辑框两个控件,用相对布局绑定在一起

<TextView android:id="@+id/filenameLable" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/filename" /> <EditText android:id="@+id/filename" android:layout_width="160px" android:layout_height="wrap_content" android:layout_toRightOf="@id/filenameLable" //在文本框右边 android:layout_alignTop="@id/filenameLable" //和文本框的顶部对齐 android:layout_marginLeft="10px" //设置文本框和编辑框的距离间隔 android:text="lishun.txt" /> </RelativeLayout> <TextView android:id="@+id/filenameLable" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/content" /> <EditText android:id="@+id/content" android:layout_width="fill_parent" android:layout_height="wrap_content" //设置编辑框的默认提示字符

android:minLines="3"/>

//设置编辑框显示的行数

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button"/> <Button android:id="@+id/Readbutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/button" android:layout_alignTop="@id/button" android:text="@string/Readcontent"/> </RelativeLayout> <TextView android:id="@+id/result" android:layout_width="wrap_content" android:layout_height="wrap_content" /> B. 字符串属性设置的 string.xml:

<resources> <string name="hello">Hello World, Activity01!</string> <string name="app_name">数据保存</string> <string name="filename">文件名称</string> <string name="content">文件内容</string> <string name="Readcontent">读取文件内容</string> <string name="button">保存文件</string> <string name="success">保存成功</string> <string name="error">保存失败</string> </resources> C. 现在编写业务层的代码 FileService:

public class FileService { //保存文件数据 public static void save(OutputStream outStream,String content) throws Exception{ outStream.write(content.getBytes()); outStream.close(); } //读取文件数据 public static String read(InputStream inStream) throws Exception{ ByteArrayOutputStream outStream=new ByteArrayOutputStream(); new byte[] byte buffer=new byte new byte[1024];

int len=-1; while((len=inStream.read(buffer))!=-1){ while outStream.write(buffer, 0, len); } byte[] byte data=outStream.toByteArray(); outStream.close(); inStream.close(); return new String(data); } } D. Activity01(部分的实例化布局控件的代码省略 在对按钮增加单击事件) 例化布局控件的代码省略, 最后编写 Activity 代码 Activity01(部分的实例化布局控件的代码省略,在对按钮增加单击事件)

//可以将两个按钮的事件监听器写在一起,定义成属性成员 private View.OnClickListener listener=new OnClickListener() { new @Override public void onClick(View v) { Button button=(Button)v; String filename=filenameText.getText().toString(); switch (button.getId()) { case R.id. Savebutton: //若单击的是保存按钮,会产生的事件

int resId=R.string.success; String content=contentText.getText().toString(); try { this.openFileOutput( OutputStream outStream = Activity01.this this filename, Context.MODE_PRIVATE); FileService.save(outStream, content); } catch (Exception e) { Log.i(TAG, e.toString()); resId=R.string.error; } Toast.makeText(Activity01.this resId, Toast.LENGTH_LONG).show(); this, this break; break case R.id.Readbutton: try { InputStream inStream = Activity01.this this.openFileInput(filename); this String text= FileService.read(inStream); reslutView.setText(text); } catch (Exception e) { Log.i(TAG, e.toString()); Toast.makeText(Activity01.this "读取失败", Toast.LENGTH_LONG).show(); this, this } break; break } } }; //若单击的是读取文件按钮,会产生的事件

注意:对其他三种操作模式的操作: 注意:对其他三种操作模式的操作: OutputStream outStream = Activity01.this this.openFileOutput( this filename, Context.MODE_PRIVATE); 如果我们要打开存放在/data/data/com.lishun/files/目录下的应用私有的文件,可以使用Activity提供的 openFileInput()方法 FileInputStreaminStream=this.getContext().openFileInput(“lishun.txt”); Log.i(“FileTest”,readStream(inStream)); 或者直接使用文件的绝对路径: //读取其他应用文件的代码: Public void testRead() throws Exception{ File file=new File("/data/data/com.lishun/files/lishun.txt"); InputStream inStream=new FileInputStream(file); String result=read(inStream); Log.i(TAG,result); } //写入其他应用文件的代码: Public void testRead() throws Exception{ File file=new File("/data/data/com.lishun/files/lishun.txt"); FileOutputStream outStream =new FileOutputStream(file); Save(outStream, "******写入文件的内容********") } 修改操作模式为: (1) 修改操作模式为:Context.MODE_APPEND 可以进行追加文件的操作 (2) Context.MODE_WORLD_READABLE 和 Context.MODE_WORLD_WRITEABLE 用法一样 若我们既想读又想写,那么我们可以在第二个参数设置为: 操作模式是可以相加的) (操作模式是可以相加的 (3) 若我们既想读又想写,那么我们可以在第二个参数设置为: 操作模式是可以相加的) ( Context.MODE_WORLD_READABLE+Context.MODE_WORLD_WRITEABLE Context.MODE_WORLD_READABLE+Context.MODE_WORLD_WRITEABLE getCacheDir()和 getFilesDir()方法 方法: (4) Activity 还提供了 getCacheDir()和 getFilesDir()方法: getCacheDir()方法用与获取 getCacheDir()方法用与获取/data/data/com.lishun/cache 目录 方法用与获取 getFilesDir()方法用与获取 getFilesDir()方法用与获取/data/data/com.lishun/files/ 方法用与获取 七. SDcard 数据访问和 SAX 解析 XML 文件 1. openFileOutput()方法保存文件 文件是放在手机空间上的,一般的手机的存储空间不 方法保存文件, 使用 Activity 的 openFileOutput()方法保存文件,文件是放在手机空间上的,一般的手机的存储空间不 是很大,存放一些小文件还行,如何要存放像视频这样的大文件是不行的。对应视频这样的大文件, 是很大,存放一些小文件还行,如何要存放像视频这样的大文件是不行的。对应视频这样的大文件,我们可 SDcard。 是干什么的? 以把它存放在 SDcard。SDcard 是干什么的?你可以把它看做事移动硬盘或者 U 盘 2. SDCard, SDcard,只是镜像文件) 。创 在模拟器中使用 SDCard,你需要先创建一张 SDcard 卡(当然不是真的 SDcard,只是镜像文件) 创 。 里面随同模拟器一起创建, 命令创建,如下: 建 SDcard 可以在 Eclipse 里面随同模拟器一起创建,也可以用 DOS 命令创建,如下: 目录, 在 DOS 窗口中进入 android SDK 安装路径下的 tools 目录,输入以下命令创建一张容量为 2G 的 SDCard,文件的后缀建议使用.img。 SDCard,文件的后缀建议使用.img。 建议使用.img D:\AndrodTool\ mksdcard 2048M D:\AndrodTool\sdcard.img 3. SDCard, 在程序中访问 SDCard,你需要申请访问 SDCard 的权限 的权限如下: 在 AndroidManifest.xml 中加入访问 SDCard 的权限如下: 中创建和删除文件的权限: 在 SDcard 中创建和删除文件的权限:
<uses<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

// readStream是一个工具类

写入数据的权限: 往 SDcard 写入数据的权限: <usesandroid:name= <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 4. 目录下的, 我们保存的文件一般是保存在默认的手机文件: 目录下的 我们保存的文件一般是保存在默认的手机文件:/data/data/com.lishun/files/目录下的,若想使我们的

下面,我们需要做的修改为: 程序将文件保存在 SDcard 下面,我们需要做的修改为: A. 的路径,在路径下创建文件: 有两种方式) (有两种方式 得到 SDcard 的路径,在路径下创建文件: 有两种方式) ( File("/sdcard sdcard"+filename); File file=new File("/sdcard"+filename); file=new File file=new File(Environment.getExternalStorageDirectory(),filename); getExternalStorageDirectory B. 是否存在,判断的条件: 一定要判断 SDcard 是否存在,判断的条件:

if(Environment. )){… if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){…} C. 的权限, 的文件是可以被任何程序访问的, 必须要申请访问 SDCard 的权限,存放到 SDcard 的文件是可以被任何程序访问的,它是移动

八. 使用 SAX 或者 DOM 或者 pull 读取 xml 文件 (一) 使用 SAX 解析 xml 文件

解析器, 等移动设备。 SAX 是一个解析速度快并别占用内存少的 xml 解析器, 非常实用于 Android 等移动设备。 SAX 解析 xml 文件采用的是事件驱动,也就是说它不需要解析完整的文档, 内容顺序解析文档的过程中, 文件采用的是事件驱动,也就是说它不需要解析完整的文档,按内容顺序解析文档的过程中,SAX 会判断 语法中的某个部分,如果符合就会触发事件。所谓事件, 当前读到的字符是否合法 xml 语法中的某个部分,如果符合就会触发事件。所谓事件,其实就是一些回调 (callback) 方法, 这些方法 事件) (事件) 定义在 ContentHandler 接口。 接口。 函数 callback) 方法, 下面是一些 ContentHandler 接口常用的方法: 接口常用的方法: startDocument():当遇到文档开通的时候,调用这个方法,可以在其中做一些预处理的工作。 startDocument():当遇到文档开通的时候,调用这个方法,可以在其中做一些预处理的工作。 Document() endDocument():和上面的方法对应,当文档结束的时候调用这个方法, endDocument():和上面的方法对应,当文档结束的时候调用这个方法,可以在其中做一些善后的 Document() 这个方法 操作 qName,Attributes atts) startElement(String namespaceURI,String localName,String qName,Attributes atts) 当读到一个开始标签的时候,会触发这个方法。 就是命名空间, 当读到一个开始标签的时候,会触发这个方法。NamespaceURI 就是命名空间,localName 是 不带命名空间的前缀的标签名, 是带命名空间前缀的标签名。 不带命名空间的前缀的标签名,qName 是带命名空间前缀的标签名。通过 atts 可以得到所有属性名和相 应的值。 中一个重要的特点就是它的流式处理,当遇到一个标签的时候, 应的值。要注意的是 SAX 中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会记录以 前所碰到的标签,也就是说 startElement()方法中 所有你所知道的信息,就是标签名字和属性, 方法中, 前所碰到的标签,也就是说,在 startElement()方法中,所有你所知道的信息,就是标签名字和属性, 至于标签的嵌套结构,上层标签的名称,是否有子原属等等其它与结构相关的信息都是不得而知的, 至于标签的嵌套结构,上层标签的名称,是否有子原属等等其它与结构相关的信息都是不得而知的,都需 要你的程序来完成。 来的那么方便。 要你的程序来完成。这使得 SAX 在编程处理上没有 DOM 来的那么方便。 endElement(String endElement(String uri,String localName,String name) 这个方法和上面的方法相对应,在遇到结束标签的时候, 这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法 Characters(char[] ch,int start,int length) 这个方法用来处理在 文件中读到的内容,第一个参数用于存放文件的内容, 这个方法用来处理在 xml 文件中读到的内容,第一个参数用于存放文件的内容,后面两个参数是读到 的字符串在这个数字中的起始位置和长度, String(ch,start,length)就可以获取内容 的字符串在这个数字中的起始位置和长度,使用 new String(ch,start,length)就可以获取内容 xml(SAX)、 Model(DOM)和 1. 在 Android 平台上可以使用 Simple API for xml(SAX)、Document Object Model(DOM)和 Android 附带的 pull 解析器解析 示例 5:解析 xml 文件 <persons> <persons> <person id="23"> <name>李明</name> <name>李明</name> 李明 <age>30</age> <age>30</age> 30</ </person> <person id="20"> <name>李向梅</name> <name>李向梅</name> 李向梅 <age>25 <age>25</age> </person> </persons>

2. 先 设 计 业 务 处 理 类 SAXPersonService , 业 务 处 理 类 Person(id,name,age) 还 有 相 应 的 toString()方法省略给出: toString()方法省略给出: 方法省略给出 public class SAXPersonService { //在业务层我们最好抛出 public static List<Person> readXml(InputStream inStream) throws Exception{ SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxParser = spf.newSAXParser();//创建解析器 //设置解析器的相关特性,http://xml.org/sax/features/namespaces/true //表示开启命名空间特性 //saxParser.setProperty("http://xml.org/sax/features/namespaces/true", true); XMLContentHandler handler = new XMLContentHandler();//这个类要自定义 saxParser.parse(inStream, handler); inStream.close(); return handler.getPersons(); } } 接口的类 的类, 3. 上述的 XMLContentHandler:只要为 SAX 提供实现 ContentHandler 接口的类,那么该类就可 只要为 以得到通知事件( 调用了该类的回调方法) ;因为 是一个接口, 以得到通知事件(实际上就是 SAX 调用了该类的回调方法) 因为 ContentHandler 是一个接口,在使用的时 ; 候可能会有些不方便, DefaultHandler,它实现了这个接口, 候可能会有些不方便,因此 SAX 还制定了一个 Helper 类:DefaultHandler,它实现了这个接口,但是所有 的方法体都为空,在实现的时候,你只需要继承这个类,然后重载相应的方法即可。 的方法体都为空,在实现的时候,你只需要继承这个类,然后重载相应的方法即可。 public class XMLContentHandler extends DefaultHandler { private List<Person> persons;//用于存放解析到的person private Person person; private String perTag; public List<Person> getPersons() { return persons; } public void setPersons(List<Person> persons) { this.persons = persons; this } @Override public void startDocument() throws SAXException { persons=new ArrayList<Person>(); //解析文档完成初始化 new } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if("person".equals(localName)){ if person=new Person(); new person.setId(new Integer(attributes.getValue(0))); new } perTag=localName; //记录当前的标签 } @Override

public void endElement(String uri, String localName, String qName) throws SAXException { if("person".equals(localName) && person!=null null){ if null persons.add(person); person=null null; null } perTag=null //记录当前的标签,一定要将前面的标签变量置为空 null; null } @Override char[] public void characters(char ch, int start, int length) char throws SAXException { if(person!=null null){ if null String data=new String(ch,start,length);//获取目标数据 new if("name".equals(perTag)){ if person.setName(data); }else if else if("age".equals(perTag)){ person.setAge(new Short(data)); new } } } } 4. 的处理类( lishun.xml 文件) 手机布局文件就省略了: 最后给出 xmlActivity 的处理类(读取 lishun.xml 文件) 手机布局文件就省略了: ,

public class xmlActivity extends Activity { private static final String TAG="xmlActivity"; null; private TextView resultView=null null @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super setContentView(R.layout.main); resultView=(TextView)findViewById(R.id.reault); InputStream inStream= xmlActivity.class class.getClassLoader().getResourceAsStream("lishun.xml"); class try { List<Person> persons = SAXPersonService.readXml(inStream); StringBuilder sb = new StringBuilder(); for (Person person : persons) { sb.append(person.toString()).append("\n"); } resultView.setText(sb.toString()); } catch (Exception e) { Log.e(TAG, e.toString()); Toast.makeText(this "xml解析失败", Toast.LENGTH_LONG); this, this } }

5.

最后显示的结果: 最后显示的结果:

(二)使用 DOM 解析 xml 文件 文件所有的内容读入到内存中, API 使用 DOM 解析 xml 时,会将 xml 文件所有的内容读入到内存中,然后使用 DOM API 遍历 xml 树、检 索所需的数据。 的代码比较直观, 文件较小的时候, 的实现更加的简单。 索所需的数据。使用 DOM 操作的 xml 的代码比较直观,当 xml 文件较小的时候,比 SAX 的实现更加的简单。 是将所有的内容读入到内存中,所有内存的消耗比较大, 的设置来说, 但是因为 DOM 是将所有的内容读入到内存中,所有内存的消耗比较大,特别对于 Android 的设置来说,内存 资源是很宝贵的, 文件, 资源是很宝贵的,所有建议还是使用 SAX 来解析 xml 文件,当然 xml 文件的内容较小的时候采用 DOM 是可 行的。 行的。 1.业务处理类 DomPersonService: 程序应该结果一样) 1.业务处理类 Dom 解析的代码 DomPersonService: 程序应该结果一样) ( public class DomPersonService { public static List<Person> readXml(InputStream inStream) throws Exception{ List<Person> persons=new ArrayList<Person>(); new DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); DocumentBuilder builder=factory.newDocumentBuilder();//建立文档的解析器 Document document=builder.parse(inStream);//用输入流的方式去解析,在内存中以树型存储 Element root=document.getDocumentElement();//由文档区得到第一个根元素 //找到所有的person的节点,返回节点列表 NodeList nodes=root.getElementsByTagName("person"); for(int for int i=0;i<nodes.getLength();i++){ Element personElement=(Element)nodes.item(i); Person person=new Person(); new person.setId(new Integer(personElement.getAttribute("id")));//根据名称返回属性值 new NodeList childNodes=personElement.getChildNodes();//在person内部取得所有子节点 for(int for int y=0;y<childNodes.getLength();y++){ Node childNode=(Node)childNodes.item(y); //不能强转成Element类型,只能转成Node //判断节点类型为ELEMENT_NODE类型 if(childNode.getNodeType()==Node.ELEMENT_NODE){ if Element childElement=(Element)childNode; //把节点强制转型为元素类型 if("name".equals(childElement.getNodeName())){ //通过名字判断 if person.setName(childElement.getFirstChild().getNodeValue());//取值 }else if else if("age".equals(childElement.getNodeName())){ person.setAge(new Short(childElement.getFirstChild().getNodeValue())); new } } //Element和Text统称为节点

} persons.add(person); } return persons; } } (三)使用 Pull 解析 xml 文件 解析器是一个开源的, 解析器相似。 Pull 解析器是一个开源的,Android 内置的 Pull 解析器的运行方式与 SAX 解析器相似。它提供了类似 的事件, parser.next()可以进行下一个元素并触发相应的事件 可以进行下一个元素并触发相应的事件。 的事件,如:开始元素和结束元素事件,使用 parser.next()可以进行下一个元素并触发相应的事件。事件将作 开始元素和结束元素事件, 为 数 值 代 码 被 传 送 , 因 此 可 以 使 用 一 个 switch 对 感 兴 趣 的 事 件 进 行 处 理 。 当 元 素 开 始 解 析 时 , 调 用 parser.nextText()方法可以获取下一个 parser.nextText()方法可以获取下一个 Text 类型元素的值 方法可以获 1. PullPersonService (程序应该结果一样) PersonService: (程序应该结果一样 业务处理类 Pull 解析的代码 PullPersonService: 程序应该结果一样)

public class PullPersonService { public static List<Person> readXml(InputStream inStream) throws Exception{ List<Person> persons = null null; XmlPullParser parser=Xml.newPullParser();//Android提供的一个工具类,得到一个Pull解析器 parser.setInput(inStream, "UTF-8");//用输入流,字符为UTF-8的方式去解析 int eventCode=parser.getEventType();//触发一个事件,返回一个数字 //对事件进行迭代 null; Person person=null null while(eventCode!=XmlPullParser.END_DOCUMENT){//如果事件不是结束文档事件 while switch (eventCode) { case XmlPullParser.START_DOCUMENT://文档开始事件 persons=new ArrayList<Person>(); new break; break case XmlPullParser.START_TAG://开始元素事件 if("person".equals(parser.getName())){ if person=new Person(); new person.setId(new Integer(parser.getAttributeValue(0))); new }else if else if((person!=null null)){ null if("name".equals(parser.getName())){ if //如果当前元素的下一个元素是文本类型的元素,就将文本的值返回出来 person.setName(parser.nextText()); }else if else if("age".equals(parser.getName())){ person.setAge(new Short(parser.nextText())); new } } break; break case XmlPullParser.END_TAG://结束元素 //判断最后一个结束标签</person> if("person".equals(parser.getName()) && person!=null null){ if null persons.add(person); person=null null;//把标签变量置空 null }

break; break } eventCode=parser.next(); } return return persons; } } 解析器将内容输出到 文件, 内容: (四)怎么样使用 Pull 解析器将内容输出到 xml 文件,写 xml 内容: 怎么样使用 PullPersonService 增加一个方法: 1. 业务处理类 Pull 解析的代码 PullPersonService 增加一个方法: //第一个参数是数据,第二个是输出方向,我们可以通过输出流或者写入器 public static void writeXml(List<Person> persons,Writer writer) throws Exception{ XmlSerializer serializer=Xml.newSerializer();//创建序列化的类对象 serializer.setOutput(writer);//可以接受写入器或者输入流 //作用是输出<?xml version="1.0" encoding="UTF-8"?> serializer.startDocument("UTF-8", true true); serializer.startTag(null "persons");//如果标签没有命名空间前缀,可以输入null null, null for(Person person:persons){ for null, serializer.startTag(null "person"); null serializer.attribute(null "id", String.valueOf(person.getId())); null, null null, serializer.startTag(null "name"); null serializer.text(person.getName()); System.out.println("person.getName()=="+person.getName()); null, serializer.endTag(null "name"); null serializer.startTag(null "age"); null, null serializer.text(String.valueOf(person.getAge())); serializer.endTag(null "age"); null, null serializer.endTag(null "person"); null, null } serializer.endTag(null "persons");//结束标签 null, null serializer.endDocument(); writer.flush(); writer.close(); } 2. 文件: 向 SDcard 卡写入 xml 文件: FileOutputStream outStream = xmlActivity.this this.openFileOutput("person.xml", this Context.MODE_WORLD_READABLE+Context.MODE_WORLD_WRITEABLE); OutputStreamWriter writer = new OutputStreamWriter(outStream); List<Person> persons = new ArrayList<Person>(); persons.add(new Person(1, "李顺", (short 22)); short) new short persons.add(new Person(2, "李威", (short 21)); new short) short persons.add(new Person(3, "彭明", (short 22)); new short) short PullPersonService.writeXml(persons, writer); //结束文档 //取得下一个事件

SharedPreferences 共享参数) Preferences( 九. 使用 SharedPreferences(共享参数)进行数据存储

1.

省略,给出两个按钮的单击事件的代码: 属性匿名内部类) (属性匿名内部类 布局文件 main.xml 和 string.xml 省略,给出两个按钮的单击事件的代码: 属性匿名内部类) (

private View.OnClickListener listener=new OnClickListener() { new @Override public void onClick(View v) { Button button=(Button)v; //使用SharedPreferences对数据存储,在第一个参数不要加后缀.xml,不指定名称的话,默认的名称为类名称 this.getSharedPreferences("soft", SharedPreferences pres=PreferencesActivity.this this Context.MODE_PRIVATE); switch (button.getId()) { case R.id.setButton: //保存按钮 String name=nameText.getText().toString(); String age=ageText.getText().toString(); Editor editor=pres.edit();//获得编辑器 editor.putString("name", name); editor.putString("age", age); editor.commit(); //类似事务的做法,为了保存一起成功和失败*一定要加此句代码 Toast.makeText(PreferencesActivity.this "保存成功", Toast.LENGTH_LONG).show(); this, this break; break

case R.id.ReadButton: //读取按钮 String nameValue=pres.getString("name", "");//此方法的作用如name的值为空,就显示""内容 String ageValue=pres.getString("age", ""); showText.setText("姓名:"+nameValue+",年龄:"+ageValue); break; break } }; }; 2. 文件,我们从另外一个应用访问到它的方法: 若当前 PreferencesActivity 应用生成的 softxml 文件,我们从另外一个应用访问到它的方法:

首先我们从另一个应用(OtherActivity)必须构建这个能访问 A. 首先我们从另一个应用(OtherActivity)必须构建这个能访问 PreferencesActivity 的上下文
Context context= ,Context.CONTEXT_IGNORE_SECURITY); this.createPackageContext("com.lishun",Context.CONTEXT_IGNORE_SECURITY); ,Context.CONTEXT_IGNORE_SECURITY this.createPackageContext(

B. SharedPreferences pres= this.getSharedPreferences("soft", Context.MODE_PRIVATE); PreferencesActivity.this this 十. 使用嵌入式关系型 SQLite 数据库存储数据

(一)使用 SQLiteDatabase 操作 SQLite 数据库 CRUD

(二)使用 SQLiteOpenHelper 对数据库进行版本控制

(三)用户安装你的软件到手机里面去的时候,应该怎么做? 用户安装你的软件到手机里面去的时候,应该怎么做? 1. 2. 3. 4. 在用户的手机上创建数据库表结构 在用户的手机上创建数据库表结构 必须在用户第一次使用你的软件, 必须在用户第一次使用你的软件,自动生成数据库表结构 升级数据库 的数据库应用是单用户的,可以不关数据库, android 的数据库应用是单用户的,可以不关数据库,一个连接就可以了

操作: 有两种方式操作数据库—— ——SQL 提供的方法) 示例 6:SQLite 对数据库的 CRUD 操作:(有两种方式操作数据库——SQL 语句和 SQLite 提供的方法) DataBaseOpenHelper, A. 首先得到一个数据库操作对象 DataBaseOpenHelper,必须继承指定的类 public class DataBaseOpenHelper extends SQLiteOpenHelper { private static final String DBNAME="lishun"; //数据库名称 private static final int version=1; //父类没有无参的构造器,只有参数的构造器 public DataBaseOpenHelper(Context context) { super(context, DBNAME, null version); null, super //第三个是CursorFactory指定在执行查询时获得一个游标示例的工厂类,设置为null,代表系统默认的工厂类 } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE 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 onCreate(db); } } EXISTS person"); //数据库版本

第一种操作数据库的方式: B. 第一种操作数据库的方式:使用 SQL 语句完成 CRUD public class PersonService { private DataBaseOpenHelper dbOpenHelper; public PersonService(Context context) { dbOpenHelper=new DataBaseOpenHelper(context); new } public void save(Person person){ //保存操作

SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); new data.execSQL("insert into person(name,age) values(?,?)",new Object[]{person.getName(),person.getAge()}); } public void update(Person person){ //更新操作

SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); data.execSQL("update person(name,age) set name=?,age=? where personid=?", new Object[]{person.getName(),person.getAge(),person.getPersonid()}); } public Person find(Integer id){ //查找操作

SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); Cursor cursor=data.rawQuery("select * from person where personid=?", new String[]{String.valueOf(id)}); if(cursor.moveToNext()){ if return new Person(cursor.getInt(0),cursor.getString(1),cursor.getShort(2)); } null; return null } public void delete(Integer...idss){ if(idss.length>0){ if StringBuilder sb=new StringBuilder(); new for(Integer id:idss){ for sb.append('?').append(','); } sb.deleteCharAt(sb.length()-1); SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); data.execSQL("delete form person where personid in("+sb+")", (Object[])idss); } } public List<Person> getScrollData(int startResult,int maxResult){ //分页的操作 int int List<Person> persons=new ArrayList<Person>(); new SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); Cursor cursor=data.rawQuery("select * from person limit ?,?", new String[]{String.valueOf(startResult),String.valueOf(maxResult)}); while(cursor.moveToNext()){ while persons.add(new Person(cursor.getInt(0),cursor.getString(1),cursor.getShort(2))); new //删除操作,可变参数

} return persons; } public long getCount(){ //总记录数操作

SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); Cursor cursor=data.rawQuery("select * from person ",null null); null if(cursor.moveToNext()){ if return cursor.getLong(0); } return 0; } } 第二种方式操作数据库: C. 第二种方式操作数据库:SQLite 提供的方法 public class OtherPersonService { private DataBaseOpenHelper dbOpenHelper; public OtherPersonService(Context context) { dbOpenHelper=new DataBaseOpenHelper(context); new } public void save(Person person){ //保存操作

SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); ContentValues values=new ContentValues(); new values.put("name", person.getName()); values.put("age", person.getAge()); data.insert("person", "name", values);//第三个参数必须添加记录,处理主键之外, 其他字段为null } public void update(Person person){ //更新操作

SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); ContentValues values=new ContentValues(); new values.put("name", person.getName()); values.put("age", person.getAge()); data.update("person", values, "personid", new String[]{String.valueOf(person.getPersonid())}); } public Person find(Integer id){ //查找操作

SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); Cursor cursor=data.query("person", new String[]{"personid","name","age"}, "personid=?", new String[]{String.valueOf(id)}, null null null null, null, null); if(cursor.moveToNext()){ if return new Person(cursor.getInt(0),cursor.getString(1),cursor.getShort(2)); } null; return null } public void delete(Integer...idss){ if(idss.length>0){ if //删除操作

StringBuilder sb=new StringBuilder(); new String[] strIds=new String[idss.length]; new for(int for int i=0;i<idss.length;i++){ sb.append('?').append(','); strIds[i]=String.valueOf(idss[i]); } sb.deleteCharAt(sb.length()-1); SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); data.delete("person", "personid in("+sb+")", strIds); } } public List<Person> getScrollData(int startResult,int maxResult){ int int List<Person> persons=new ArrayList<Person>(); new SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); Cursor cursor=data.query("person", new String[]{"personid","name","age"}, null, null, null, null, null null null null "personid desc",startResult+","+ maxResult); while(cursor.moveToNext()){ while persons.add(new Person(cursor.getInt(0),cursor.getString(1),cursor.getShort(2))); new } return persons; } public long getCount(){ //获得总记录操作 //分页操作

SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); Cursor cursor=data.query("person", new String[]{"count(*)"},null null null null null null, null null, null, null, null); if(cursor.moveToNext()){ if return cursor.getLong(0); } return 0; } 对事物的处理,下面以保存操作为例: (四)SQLite 对事物的处理,下面以保存操作为例: public void save(Person person){ SQLiteDatabase data=dbOpenHelper.getWritableDatabase(); data.beginTransaction(); try { //开启事物

//如果SQL语句有错,是提交不上去的 data.execSQL("insert into person(name,age) values(?,?)",new Object[] {李顺, }); 22 new data.setTransactionSuccessful(); //没有commit提交方法,只有设置事务成功标志

} catch (Exception e) { } data.endTransaction(); } 十一. 控件显示列表: 十一. ListView 控件显示列表: 1. 两个布局文件: personitem.xml, 布局文件是整个的布局文件, 两个布局文件:main.xml 和 personitem.xml,第一个 main.xml 布局文件是整个的布局文件, 第二个 personitem.xml 布局文件是对列表条目的布局 //结束事物

2.

Adapter: 对 ListView 操作我们需要用到适配器 Adapter: 对此我们两种适配器模式: SimpleCursorAdapter, 对此我们两种适配器模式:SimpleAdapter 和 SimpleCursorAdapter, A.对 的用法: A.对 SimpleAdapter 的用法:其中 data 是一个 List<HashMap<String,String>>

SimpleAdapter adapter=new SimpleAdapter(DBActivity.this new this,data,R.layout.personitem, this new String[]{"personid","name","age"},new int new int[]{R.id.personid,R.id.name,R.id.age}); B.对 的用法: 列表, B.对 SimpleCursorAdapter 的用法:而这个不是 list 列表,而是一个游标 Cursor SimpleCursorAdapter adapter1=new SimpleCursorAdapter(DBActivity.this this,R.layout.personitem, new this int[]{R.id.personid,R.id.name,R.id.age}); Cursor new String[]{"personid","name","age"}, new int C.最后将适配器注册到ListView里面:listView.setAdapter(adapter); 最后将适配器注册到ListView里面: 最后将适配器注册到ListView里面 3. 的用法, 是不正确的, 改为_id 才行, 上述的 SimpleCursorAdapter 的用法, 是不正确的, 一定要将 persionid 改为_id 才行, 这是 Android 规定的, 详细的介绍还是看视频。 在我写的另一本 Android 笔记里面对 ListView 也有详细的介绍和示例; 也有详细的介绍和示例; 规定的, 详细的介绍还是看视频。 对此不再重复演示了。 对此不再重复演示了。 4. 里面的条目的点击事件: 还有点要强调的是对 ListView 里面的条目的点击事件: listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { new @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { // 相应的代码省略··· 相应的代码省略··· } }); 十二. 内容提供者( provider) 十二. 内容提供者(Content provider)

1. 2. 3.

它的作用是对外共享数据的 要访问某个内容提供者, 要访问某个内容提供者,一定要知道它的域名 的介绍: URI 的介绍:

的记录进行修改: 如:需要对 person 表中 id 为 1 的记录进行修改: 的写法: URI 的写法:content://com.lishun.provider.personprovider/person/1 字段记录进行修改: 如:需要对 person 表中 id 为 1 的 name 字段记录进行修改: 的写法:content://com.lishun.provider.personprovider/person/1/name URI 的写法:content://com.lishun.provider.personprovider/person/1/name 4. 类的使用介绍 (匹配采用的是一种事件形式) 介绍: (匹配采用的是一种事件形式 UriMatcher 类的使用介绍: 匹配采用的是一种事件形式) 的时候,怎么区分它是: 那么获得了用户输入的 URI 的时候,怎么区分它是: content://com.lishun.provider.personprovider/person 的这种模式 还是: 还是:content://com.lishun.provider.personprovider/person/1 的这种模式 类的使用,注意任何类都不匹配的时候, NO_MATCH=这时我们就需要 UriMatcher 类的使用,注意任何类都不匹配的时候,它就返回 NO_MATCH=-1

5.

ContentUri 工具类的使用: ContentUri 工具类的使用: 部分,它有两个比较实用的方法: ContentUri 类获取 Uri 路径后面的 ID 部分,它有两个比较实用的方法: withAppendedId(uri,id)用于为路径加上 部分: withAppendedId(uri,id)用于为路径加上 ID 部分: AppendedId(uri,id) uri=Uri.parse( content://com.lishun.provider.personprovider/person" =Uri.parse(" Uri uri=Uri.parse("content://com.lishun.provider.personprovider/person") Uri resultUri=ContentUris.withAppendedId(uri,10); //生成的 content://com.lishun.provider.personprovider/person/10 //生成的 Uri 为: content://com.lishun.provider.personprovider/person/10 parseId(uri)方法用于为路径中获取 部分: parseId(uri)方法用于为路径中获取 ID 部分:

Uri uri=Uri.parse(content://com.lishun.provider.personprovider/person/10) //获取结果为 Long personId=ContentUris.parseId(uri); //获取结果为 10

十三. 十三. Activity 之间的调用与参数传递 之间的调用方式: 1.Activity 之间的调用方式: 第一种: intent=new (oneActivity.this,OtherActivity.class); 第一种:Intent intent=new Intent (oneActivity.this,OtherActivity.class); 第二种: 第二种:Intent intent=new Intent(); Intent.setClass(oneActivity.this,OtherActivity.class); 第三种: 第三种:Intent intent=new Intent(); Intent.setComponent(new Component(oneActivity.this,OtherActivity.class)); 之间的参数传递: 2.Activity 之间的参数传递: 中设置参数: 在 oneActivity 中设置参数: Intent.putExtra(“id”,10); ntent.putExtra(“id” //传对象 传对象, Intent.putExtra(“student”,student); //传对象,student 必须实现可序列化接口 ntent.putExtra(“student” 中得到参数: 在 OtherActivity 中得到参数: Intent intent=this.getIntent(); Intent.getIntExtra(“id”); ntent.getIntExtra(“id”

关闭后返回的数据: 得到新打开的 Activity 关闭后返回的数据:

请求码的作用: 请求码的作用:

附加数据的两种写法: 为 Intent 附加数据的两种写法:

十四. 十四. Intent 的使用 十五. 的生命周期(面试重点) 十五. Activity 的生命周期(面试重点) 十六. 与互联网应用的双向沟通(重点) 十六. 与互联网应用的双向沟通(重点)


相关文章:
3G-Android
3. 实训项目名称基于 Android 平台的 3G 应用开发集合 4. 项目简介通过开发一些 3g 应用案例 应用案例,来了解 3g 行业,并掌握基于 android 平台的 3g 应用开发...
3G Android
招生简章 华清远见 3G Android 系统开发就业培训班将通过 5 个阶段的强化、集中学习,全方位打造手机开发通用型人 才,让你有更多的机会全面接触 3G 手机开发领域的...
安卓3G
/*** * * * 安卓 3G * * * ***/ 烧写安卓 3G 开发平台: nand scrub setenv serverip 192.168.13.40; setenv ipaddr 192.168.13.200; saveenv tftp...
《Android 3G 移动应用开发》期末复习题
Android 3G 移动应用开发》期末复习题 2012-2013-1 学期 1. Android 系统内核基于什么操作系统? 2. Google 发布的基于 Linux 平台的开放源代码移动操作名称是...
基于Android手机3G网络远程可移动监控系统
基于Android 手机 3G 网络远程可移动监控系统 【摘要】论述了基于 ARM9 硬件平台,linux2.6 操作系统,android 手机设 计了一个远程操作家居视频监控设备的系统。...
华清远见3G Android系统开发就业培训班将通过5个阶段的强化
华清远见 3G Android 系统开发就业培训班将通过 5 个阶段的强化、集中学习,全方位打造手机开 发通用型人才,让你有更多的机会全面接触 3G 手机开发领域的各种知识...
基于Android系统的3G网
(42) IV 信息科学与技术学院学士学位论文 1 绪论 1.1 系统概述此次开发目的为实现基于 Android 系统的 3G 网络功能,即利用 3G 网络实现打 接电话、收发短信和...
Android3G教学科研平台实验手册
136 3 KIOTB-AND01 Android 3G 教学科研平台 北京京胜世纪科技有限公司 4 KIOTB-AND01 Android 3G 教学科研平台 北京京胜世纪科技有限公司 前言 本文档为北京...
Android 3G开发平台创新实验室建设方案
Android 3G开发平台创新实验室建设方案_计算机软件及应用_IT/计算机_专业资料。南京爱思电子有限公司 www.nj-ices.com Android 3G 移动智能终端、物联网创新实 验室...
基于Android手机平台的3G航空订票系统
基于Android手机平台的3G航空订票系统_计算机软件及应用_IT/计算机_专业资料。四川大学锦城学院本科毕业论文 基于 Android 手机平台的 3G 航空订票系统 基于 Android 手...
更多相关标签:
3g android培训 | android 判断2g 3g 4g | android ntfs 3g | android获取3g ip地址 | android vold ntfs 3g | android 3g | android 3g模块移植 | android 2g 3g 4g |