Android 应用开发
注意!由于文章图片是通过typora一键上传图片实现,该功能还存在bug,容易导致图片顺序混乱,文章(1 of 2)开头提供了原版文章的 pdf 资源下载,推荐下载 pdf 后观看,或评论区联系我获取其他方式的下载链接。
文章目录
第6章 Intent
1 Intent简介
解决的问题:如何启动其他的Activity并实现它们之间的单/双向通信
Intent简介
-
Intent是一种组件之间消息传递机制,它是一个动作的完整描述:包含了动作产生组件、接收组件和传递的数据信息。
-
Intent主要用途:启动Activity、Service,在Android系统上发布Broadcast消息。
2 使用Intent启动Activity【显示与隐式】
显示启动
-
概念:在Intent中指明要启动的Activity类。
-
示例:
隐式启动
-
说明:
-
无需指明具体启动哪一个Activity,而由Android系统根据Intent的动作和数据来决定启动哪一个Activity。
-
例如:希望启动一个浏览器,却不知道具体应该启动哪一 个Activity,此时则可以使用Intent的隐式启动,由Android 系统决定启动哪一个Activity来接收这个Intent。
-
隐式启动的可以是Android系统内置的Activity,也可是程序本身的Activity,还可是第三方应用程序的Activity。
-
-
隐式启动示例1:启动浏览器打开一个网址
-
隐式启动示例2:打开播放器播放音乐
-
播放音乐例子在7.0报错问题的解决:
-
报错:FileUriExposedException: …exposed beyond app throughIntent.getData()
-
原因:按照Android N的要求,如果 file://格式的Uri的Intent离开应 用将导致 FileUriExposedException 异常。若要在应用间共享文件, 应发送content://格式的Uri,并授予Uri临时访问权限,实现此方法的 是FileProvider类。
-
解决方法:不推荐
-
-
常见的Intent动作
-
-
隐式启动示例3:程序拨打电话例子
-
在AndroidManifest.xml添加权限:
<uses-permission android:name="android.permission.CALL_PHONE" />
-
核心程序代码:
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:18908643860")); startActivity(intent);
-
权限申请 (6.0以后):代码见后
-
添加v4依赖(在安卓开发中,添加 v4 依赖是为了使用 Android Support Library 中的类和方法。Android Support Library 是一组库,可帮助您在旧版 Android 平台上使用最新 API 功能。v4 库是其中的一个库,它包含了许多有用的类和方法,例如 Fragment、ViewPager、NotificationCompat 等等。),此处方法我觉得不会考
-
3 Activity间数据传递
总体图
单向传递数据
-
A 利用Bundle给 B 传递数据:★★★
-
关于Bundle
-
Bundle类是一个存储和管理key-value值对的类,多应用于Activity之间相互传递值。
-
用法示例:
-
Bundle类的一些方法:
clear():清除此Bundle映射中的所有保存的数据。
clone():克隆当前Bundle
containsKey(String key):返回指定key的值
getString(String key):返回指定key的字符
isEmpty():如果这个捆绑映射为空,则返回true
putString(String key, String value):插入一个给定key的字符串值
remove(String key):移除指定key的值
-
-
Intent类的putExtras()、getExtras()方法
- putExtras(Bundle):往Intent中添加一个Bundle对象
- getExtras():从Intent中取出Bundle对象
-
单向传递数据示例1 – 传递普通数据
-
单向传递数据示例2 – 传递对象数据
-
针对前例,如何用一个user对象传递数据:
-
关键点在于Bundle如何传递对象数据。
-
Bundle可以传递对象,但前提是这个对象需要序列化。
什么是序列化:序列化是一种用来处理对象流的机制, 以解决如网络传播、磁盘读写等对对象流读写操作时所引发的问题。
-
Bundle的putSerializable()方法,可以存储已经序列化的对象数据(仍然是Key-Value形式);
-
接收数据时Bundle用getSerializable()方法,获得数据需要强制转化一下原来的对象类型。
-
具体过程如下:
(1)先将User类序列化(直接实现Serializable接口即可)
(2)发送端:RegisterActivity主要代码
(3)接收端:WelcomeActivity主要代码
-
双向传递数据
-
总图
-
A ↔ B 工作原理
- B 返回数据给 A 的核心代码框架 ★★★
-
双向传递数据示例1:A_Activity输入两个数,B_Activity求和并返回值
-
A_Activity主要代码1
-
B_Activity主要代码
-
A_Activity主要代码2 – 关键事件onActivityResult
-
-
双向传递数据示例2 – 将DatePicker控件单独作为一个activity(自学)
-
MainActivity主要代码1
-
MainActivity主要代码2
-
CalendarActivity主要代码1
-
CalendarActivity主要代码2
-
第7章 Android广播机制
1 什么是广播
概述
- 在Android中,广播(Broadcast)是一种广泛运用在应用程序之间传输信息的机制。
- 广播消息实质就是将一个Intent对象用sendBroadcast方法 发送出去 (详见7.3节自定义广播)。
- 两种类型广播:
- 系统广播:如系统启动完成了、拨打电话了、收到短信了、 手机没电了等系统发送的消息
- 自定义广播:将自定义的消息广播给应用程序
BroadcastReceiver
-
BroadcastReceiver是对发送出来的Broadcast进行过滤并响应的一类组件,是Android系统的四大组件之一。
-
BroadcastReceiver一般要在AndroidManifest.xml中注册
2 系统广播
概述
- 系统广播就是由Android系统发出的广播。
- 如系统启动完成了、系统关闭了、拨打电话了、收到短信 了、手机没电了、新安装了一个应用程序、在耳机口上插 入耳机、设备内存不足、屏幕关闭等等。
系统广播示例1:手机关机时播放关机音乐
-
准备工作:新建一个新Module(名为TestBroadcast)
-
在res文件夹下新建raw文件夹
-
将音乐文件复制到raw文件夹中
-
-
创建广播接收器BroadcastReceiver
-
生成的MyReceiver.java代码框架:
-
-
处理广播:在onReceive添加处理代码:
-
注册接收器:(静态注册)
注册:告诉系统这个BroadcastReceiver要接收哪种广播消息(Intent-filter)
在AndroidManifest.xml文件中添加注册信息(红色部分)
-
权限设置:有的广播需要权限支持,本例不需要。
如需添加权限,则在AndroidManifest.xml中添加,例如:
-
测试运行: 先运行一下MainActivity,将app安装到手机中(告诉系统有BroadcastReceiver);然后再锁屏,就有音乐放了。
-
部分系统广播
示例2:监听拨打电话(去电)
-
给Receiver添加接收拨打电话的广播
-
给app添加处理拨打电话的权限
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
-
补充:AS 6.0以后的模拟器
-
修改Receiver代码
3 自定义广播
主要步骤
-
发送广播消息
在广播发送端(如某个APP的Activity),把信息装入一个Intent对象(含一个自定义的消息标识串,值任意,能保证唯一性即可), 然后sendBroadcast(intent)把 Intent 广播出去。
-
接收广播
接收端创建Broadcast Receiver
-
静态注册:在AndroidManifest.xml中注册Receiver,添加intent-filter
-
动态注册:代码实现注册
-
-
接收端处理广播消息:onReceive()方法
-
关于消息广播标识串:
-
消息广播标识串,自定义值,具有唯一性即可
-
这个标识在后面有3个地方要用:
- 发送广播时:用于创建Intent对象,Intent intent = new Intent(“广播标识串”);
- manifest中注册时用:作为BroadcastReceiver的intent-filter的action name值
- 处理消息时:在onReceive()方法中作为消息类型的判断用
-
通常也可以把标识串定义为符号常量,如:
final String BROADCAST_ACTION_NAME = “如 wust.zz.mybroadcast";
-
自定义广播示例(静态注册)
-
总图
-
发送端app:MainActivity主要代码
-
接收端app:MyReceiver主要代码
-
MyReceiver配置 (静态注册)
AndroidManifest.xml文件:红色部分
-
补充:Android8.0自定义广播无法接收问题解决
-
方法1:使用动态广播代替静态广播
-
方法2:保留原来的静态广播,但intent但加入Component参数:
-
附录:短信黑名单示例
概述
-
监听SMS短信信息(SMS,Short Messaging Service)
- 如果对方发送的短信包含“@echo”串,就自动回发“denyyou!”信息,并将该号码列为黑名单
-
BroadcastReceiver采用动态注册
MainActivity界面(activity_main.xml)
<ToggleButton
android:id="@+id/toggleButton1" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="74dp"
android:layout_marginTop="44dp"
android:text="监控SMS"
android:textOff="SMS监控已关闭"
android:textOn="SMS监控已开启" />
SMSReceiver代码
获取短信说明
AndroidManifest.xml添加SMS权限
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
MainActivity主要代码
第8章 Service
1 什么是Service
-
Service是Android系统四大组件之一,它是一种长生命周期的,没有可视化界面,运行于后台的一种服务程序。
2 Service类型
Started启动的
-
Started启动的:常用于应用程序内部
-
Started形式是指当一个应用组件(如Activity)通过startService()方法 开启的服务。一旦开启,该服务就可以无限期地在后台运行,哪怕开启它的组件被销毁。
-
基本特点:
-
在应用程序中定义service组件。
-
服务通过调用startService(intent)启动,stopService(intent)结束。
-
在服务内部可以调用stopSelf() 来自己停止。
-
通常,开启的服务执行一个单独的操作且并不向调用者返回一个结果。
-
无论调用了多少次startService(),只需调用一次stopService()来停止。
-
Bound绑定的
- Bound绑定的:用于应用程序之间
- Bound形式是指一个应用组件通过调用 bindService() 方法与服务绑定。调用 unbindService()关闭连接。
- Bound形式的服务一旦启用,调用者就与服务绑定在一起,调用者一旦退出,服务也就终止。
- 多个组件可以同时绑定到一个服务,但当全部绑定解除后,服务就被销毁
Service生命周期
3 本地服务示例
概述
-
示例图
-
主要步骤:
- 准备工作
- 创建MusicService
- 主程序
- 功能改进
准备工作
-
在res文件夹下新建raw文件夹
-
将音乐文件复制到raw文件夹中
创建MusicService
-
MusicService代码框架
-
MusicService代码
主程序(主要代码)
功能改进(自学)
-
增加暂停、重播、结束(停止服务)功能
-
思路:通过在Intent传递参数值给Service
- 假设参数值:
- 1 – 播放
- 2 – 暂停
- 3 – 重播
- 0 – 结束(内部结束服务)
- -1 – 退出(外部结束服务和Activity)
- 假设参数值:
-
主界面activity_main.xml
-
MainActivity主要代码
-
MusicService主要代码
4 广播交互
-
在Activity中点击Button后启动Service
public void onClick(View v) { Intent intent = new Intent(this, CounterService.class); intent.putExtra("counter", counter); //counter用来计数 startService(intent); }
-
CounterService.java
public class CounterService extends Service { int counter; @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub counter = intent.getIntExtra("counter", 0); new Timer().schedule(new TimerTask() { @Override public void run() { // TODO Auto-generated method stub Intent counterIntent = new Intent(); counterIntent.putExtra("counter", counter); counterIntent.setAction("com.example.counter.COUNTER_ACTION"); sendBroadcast(counterIntent); counter++; } } return START_STICKY; } }
-
在Service的onStartCommand()中启动一个定时器,每隔1秒钟counter计数加1,通过广播发送将counter发送出去,在Activity中的CounterReceiver收到广播后取出counter,将counter通过handler 发送出去
// 广播接收器,定义在Activity中 class CounterReceiver extends BroadcastReceiver { Handler handler; CounterReceiver(Handler handler){ this. handler = handler; } @Override public void onReceive(Context context, Intent intent) { counter = intent.getIntExtra("counter", 0); handler.sendEmptyMessage(counter); } } //运行程序,点击按钮开始计时
-
在Activity中通过Handler设置到Button上,Button要设计为Activity的全局量, 并在onCreat中初始化:
CounterReceiver receiver; Button start; Handler handler = new Handler(){ @Override public void handleMessage(Message msg){ start. setText(String.valueOf (msg.what)) super.handleMessage(msg); } }
-
在Activity的onCreat中
Receiver = new CounterReceiver(handler) start = (Button)findViewById(R.id.start)
-
示例图:
-
在程序运行过程遇到一个问题,在这里说明一下
- 广播类是在Activity里定义的,是Activity的内部类,这个内部类在使用静态注册的时候,会发生程序运行崩溃,原因是内部广播类如果使用静态注册,必须是静态内部类。
- 但是如果是静态内部类,只能访问外部类的静态成员变量,所以内部广播类推荐使用动态注册方式。
- 而且这类广播一般只在程序内部使用,没有必须在进程结束以后继续接收广播。
-
通过广播实现Activity和Service的交互简单容易实现。
- 缺点是发送不广播受系统制约,系统会优先发送系统级的广播,自定义的广播接收器可能会有延迟。
- 在广播里也不能有耗时操作,否则会导致程序无响应。
-
Activity中动态注册receiver ,如放在onCreat中:
IntentFilter filter = new IntentFilter(); filter.addAction("com.example.counter.COUNTER_ACTION "); BroadcastReceiver receiver = new MyReceiver(); registerReceiver( receiver , filter);
-
此外Activity销毁的时候,注销(关闭)广播:
unregisterReceiver(receiver);
-
进一步学习:有关远程服务的创建和调用
参考:
- http://www.apkbus.com/android-131250-1-1.html
- http://liangruijun.blog.51cto.com/3061169/653344/
第9章 1+简单数据存储和访问(简单)(SharedPreference)
0 简单数据存储
- 应用程序一般允许用户自己定义配置信息,如界面背景颜色、字体大小和字体颜色等。
- 使用SharedPreferences保存用户的自定义配置信息,并在程序启动时自动加载这些自定义的配置信息。
1 简单存储
概述
- 用户在界面上的输入的信息,在Activity关闭时进行保存。当应用程序重新开启时,保存信息将被读取出来,并重新呈现在用户界面上
SharedPreferences
-
SharedPreferences是一种轻量级的数据保存方式。
- 通过SharedPreferences可以将NVP(Name/Value Pair,名称/值对)保存在Android的文件系统中,而且SharedPreferences完全屏蔽的对文件系统的操作过程。
- 开发人员仅是通过调用SharedPreferences对NVP进行保存和读取。
-
SharedPreferences不仅能够保存数据,还能够实现不同应用程序间的数据共享
- SharedPreferences支持三种访问模式
- 私有(MODE_PRIVATE):仅有创建程序有权限对其进行读取或写入。
- 全局读(MODE_WORLD_READABLE):不仅创建程序可以对其进行读取或写入,其他应用程序也读取操作的权限,但没有写入操作的权限。
- 全局写(MODE_WORLD_WRITEABLE):创建程序和其他程序都可以对其进行写入操作,但没有读取的权限。
- SharedPreferences支持三种访问模式
-
在使用SharedPreferences前,先定义SharedPreferences的访问模式。
-
下面的代码将访问模式定义为私有模式:
public static int MODE = MODE_PRIVATE;
-
有的时候需要将SharedPreferences的访问模式设定为即可以全局读,也可以全局写,这样就需要将两种模式写成下面的方式:
public static int MODE = Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE;
-
-
定义SharedPreferences的名称
-
这个名称与在Android文件系统中保存的文件同名。因此,只要具有相同的SharedPreferences名称的NVP内容,都会保存在同一个文件中
public static final String PREFERENCE_NAME = "SaveSetting";
-
为了可以使用SharedPreferences,需要将访问模式和SharedPreferences名称作为参数,传递到getSharedPreferences()函数,并获取到SharedPreferences对象
SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCE_NAME, MODE);
-
-
修改SharedPreferences
-
在获取到SharedPreferences对象后,则可以通过SharedPreferences.Editor类对SharedPreferences进行修改,最后调用commit()函数保存修改内容。
-
SharedPreferences广泛支持各种基本数据类型,包括整型、布尔型、浮点型和长型等等。
SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString("Name", "Tom"); editor.putInt("Age", 20); editor.putFloat("Height", 1.67); //这里PPT没有float的内容,猜测是这样 editor.commit();
-
-
读取SharedPreferences
-
如果需要从已经保存的SharedPreferences中读取数据,同样是调用getSharedPreferences()函数,并在函数的第1个参数中指明需要访问的SharedPreferences名称,最后通过get<Type>()函数获取保存在SharedPreferences中的NVP。
SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCE_NAME, MODE); String name = sharedPreferences.getString("Name","Default Name"); int age = sharedPreferences.getInt("Age", 20); float height = sharedPreferences.getFloat("Height",);
- get<Type>()函数的第1个参数是NVP的名称。
- 第2个参数是在无法获取到数值的时候使用的缺省值。
-
示例
通过SimplePreferenceDemo示例介绍具体说明SharedPreferences的文件保存位置和保存格式。
-
下图是SimplePreferenceDemo示例的用户界面
用户在界面上的输入的信息,将通过SharedPreferences在Activity关闭时进行保存。当应用程序重新开启时,保存在SharedPreferences的信息将被读取出来,并重新呈现在用户界面上。
-
文件信息
-
SimplePreferenceDemo示例运行后,通过FileExplorer查看/data/data下的数据,Android为每个应用程序建立了与包同名的目录,用来保存应用程序产生的数据,这些数据包括文件、SharedPreferences文件和数据库等。
-
SharedPreferences文件就保存在/data/data/<package name>/shared_prefs目录下。
-
在本示例中,shared_prefs目录下生成了一个名为SaveSetting.xml的文件:
-
这个文件就是保存SharedPreferences的文件,文件大小为170字节,在Linux下的权限为“-rw-rw-rw”
在Linux系统中,文件权限分别描述了创建者、同组用户和其他用户对文件的操作限制。
- x表示可执行,r表示可读,w表示可写,d表示目录,-表示普通文件。因此,“-rw-rw-rw”表示SaveSetting.xml可以被创建者、同组用户和其他用户进行读取和写入操作,但不可执行。
- 产生这样的文件权限与程序人员设定的SharedPreferences的访问模式有关,“-rw-rw-rw”的权限是“全局读+全局写”的结果。
- 如果将SharedPreferences的访问模式设置为私有,则文件权限将成为“-rw-rw —”,表示仅有创建者和同组用户具有读写文件的权限。
-
-
SaveSetting.xml文件是以XML格式保存的信息,内容如图如下
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <float name="Height" value="1.81" /> <string name="Name">Tom</string> <int name="Age" value="20" /> </map>
-
-
具体功能与相关代码
-
SimplePreferenceDemo示例在onStart()函数中调用loadSharedPreferences()函数,读取保存在SharedPreferences中的姓名、年龄和身高信息,并显示在用户界面上。
-
当Activity关闭时,在onStop()函数调用saveSharedPreferences(),保存界面上的信息
-
SimplePreferenceDemo.java的完整代码:
package edu.hrbeu.SimplePreferenceDemo; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.widget.EditText; public class SimplePreferenceDemo extends Activity { private EditText nameText; private EditText ageText; private EditText heightText; public static final String PREFERENCE_NAME = "SaveSetting"; public static int MODE = Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); nameText = (EditText)findViewById(R.id.name); ageText = (EditText)findViewById(R.id.age); heightText = (EditText)findViewById(R.id.height); } @Override public void onStart(){ super.onStart(); loadSharedPreferences(); } @Override public void onStop(){ super.onStop(); saveSharedPreferences(); } private void loadSharedPreferences(){ SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCE_NAME, MODE); String name = sharedPreferences.getString("Name","Tom"); int age = sharedPreferences.getInt("Age", 20); float height = sharedPreferences.getFloat("Height",); nameText.setText(name); ageText.setText(String.valueOf(age)); heightText.setText(String.valueOf(height)); } private void saveSharedPreferences(){ SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCE_NAME, MODE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString("Name", nameText.getText().toString()); editor.putInt("Age", Integer.parseInt(ageText.getText().toString())); editor.putFloat("Height", Float.parseFloat(heightText.getText().toString())); editor.commit(); } }
-
-
示例SharePreferenceDemo将说明如何读取其他应用程序保存的SharedPreferences数据。
-
下图是SharePreferenceDemo示例的用户界面。
-
示例将读取SimplePreferenceDemo示例保存的信息,并在程序启动时显示在用户界面上。
-
-
SharePreferenceDemo示例的核心代码
-
-
访问其他应用程序的SharedPreferences必须满足三个条件:
- 共享者需要将SharedPreferences的访问模式设置为全局读或全局写。
- 访问者需要知道共享者的包名称和SharedPreferences的名称,以通过Context获得SharedPreferences对象。
- 访问者需要确切知道每个数据的名称和数据类型,用以正确读取数据。
2 文件存储
概述
- Android使用的是基于Linux的文件系统。
- 程序开发人员可以建立和访问程序自身的私有文件。
- 也可以访问保存在资源目录中的原始文件和XML文件。
- 还可以在SD卡等外部存储设备中保存文件。
内部存储
-
在内部存储器上进行文件写入和读取
-
Android系统允许应用程序创建仅能够自身访问的私有文件,文件保存在设备的内部存储器上,在Linux系统下的/data/data/<package name>/files目录中。
- Android系统不仅支持标准Java的IO类和方法,还提供了能够简化读写流式文件过程的函数。
- 主要介绍的两个函数:
- openFileOutput()
- openFileInput()
-
openFileOutput()函数
-
openFileOutput()函数为写入数据做准备而打开的应用程序私文件,如果指定的文件不存在,则创建一个新的文件。
-
openFileOutput()函数的语法格式如下:
public FileOutputStream openFileOutput(String name, int mode)
- 第1个参数是文件名称,这个参数不可以包含描述路径的斜杠。
- 第2个参数是操作模式。
-
函数的返回值是FileOutputStream类型。
-
Android系统支持四种文件操作模式:
-
使用openFileOutput()函数建立新文件的示例代码如下:
String FILE_NAME = "fileDemo.txt"; //定义了建立文件的名称fileDemo.txt FileOutputStream fos = openFileOutput(FILE_NAME,Context.MODE_PRIVATE) //使用openFileOutput()函数以私有模式建立文件 String text = “Some data”; fos.write(text.getBytes()); //调用write()函数将数据写入文件 fos.flush(); //调用flush()函数将所有剩余的数据写入文件 fos.close(); //调用close()函数关闭FileOutputStream
- 为了提高文件系统的性能,一般调用write()函数时,如果写入的数据量较小,系统会把数据保存在数据缓冲区中,等数据量累积到一定程度时再一次性的写入文件中。
- 由上可知,在调用close()函数关闭文件前,务必要调用flush()函数,将缓冲区内所有的数据写入文件。
-
-
openFileInput()函数
-
openFileInput()函数为读取数据做准备而打开应用程序私文件。
-
openFileInput()函数的语法格式如下:
public FileInputStream openFileInput (String name)
- 第1个参数也是文件名称,同样不允许包含描述路径的斜杠。
-
使用openFileInput ()函数打开已有文件的示例代码如下:
String FILE_NAME = "fileDemo.txt"; FileInputStream fis = openFileInput(FILE_NAME); byte[] readBytes = new byte[fis.available()]; while(fis.read(readBytes) != -1){ }
- 上面的两部分代码在实际使用过程中会遇到错误提示,因为文件操作可能会遇到各种问题而最终导致操作失败,因此代码应该使用try/catch捕获可能产生的异常。
-
-
InternalFileDemo示例用来演示在内部存储器上进行文件写入和读取。
-
InternalFileDemo示例用户界面如图:
-
InternalFileDemo示例的核心代码:
OnClickListener writeButtonListener = new OnClickListener() { @Override public void onClick(View v) { FileOutputStream fos = null; try { if (appendBox.isChecked()){ fos = openFileOutput(FILE_NAME,Context.MODE_APPEND); } else { fos = openFileOutput(FILE_NAME,Context.MODE_PRIVATE); } String text = entryText.getText().toString(); fos.write(text.getBytes()); labelView.setText("文件写入成功,写入长度:"+text.length()); entryText.setText(""); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ if (fos != null){ try { fos.flush(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }; OnClickListener readButtonListener = new OnClickListener() { @Override public void onClick(View v) { displayView.setText(""); FileInputStream fis = null; try { fis = openFileInput(FILE_NAME); if (fis.available() == 0){ return; } byte[] readBytes = new byte[fis.available()]; while(fis.read(readBytes) != -1){ } String text = new String(readBytes); displayView.setText(text); labelView.setText("文件读取成功,文件长度:"+text.length()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } };
-
程序运行后,在/data/data/edu.hrbeu.InternalFileDemo/files/目录下,找到了新建立的fileDemo.txt文件。
-
fileDemo.txt文件:
- fileDemo.txt从文件权限上进行分析,“-rw-rw—”表明文件仅允许文件创建者和同组用户读写,其他用户无权使用。
- 文件的大小为9个字节,保存的数据为“Some data”
-
-
外部存储
-
Android的外部存储设备指的是SD卡(Secure Digital Memory Card),是一种广泛使用于数码设备上的记忆卡。不是所有的Android手机都有SD卡,但Android系统提供了对SD卡的便捷的访问方法。
-
SD卡适用于保存大尺寸的文件或者是一些无需设置访问权限的文件,可以保存录制的大容量的视频文件和音频文件等。
- SD卡使用的是FAT(File Allocation Table)的文件系统,不支持访问模式和权限控制,但可以通过Linux文件系统的文件访问权限的控制保证文件的私密性。
- Android模拟器支持SD卡,但模拟器中没有缺省的SD卡,开发人员须在模拟器中手工添加SD卡的映像文件。
-
使用<Android SDK>/tools目录下的mksdcard工具创建SD卡映像文件,命令如下:
mksdcard -l SDCARD 256M E:\android\sdcard_file
- 第1个参数-1表示后面的字符串是SD卡的标签,这个新建立的SD卡的标签是SDCARD。
- 第2个参数256M表示SD卡的容量是256兆。
- 最后一个参数表示SD卡映像文件的保存位置,上面的命令将映像保存在E:\android目录下sdcard_file文件中。在CMD中执行该命令后,则可在所指定的目录中找到生产的SD卡映像文件。
-
如果希望Android模拟器启动时能够自动加载指定的SD卡,还需要在模拟器的“运行设置”(Run Configurations)中添加SD卡加载命令。
-
SD卡加载命令中只要指明映像文件位置即可。
-
SD卡加载命令:
-
-
测试SD卡映像是否正确加载
-
在模拟器启动后,使用FileExplorer向SD卡中随意上传一个文件,如果文件上传成功,则表明SD卡映像已经成功加载。
-
向SD卡中成功上传了一个测试文件test.txt,文件显示在/sdcard目录下
-
-
编程访问SD卡
- 首先需要检测系统的/sdcard目录是否可用。
- 如果不可用,则说明设备中的SD卡已经被移除,在Android模拟器则表明SD卡映像没有被正确加载。
- 如果可用,则直接通过使用标准的Java.io.File类进行访问。
-
将数据保存在SD卡
- 通过“生产随机数列”按钮生产10个随机小数。
- 通过“写入SD卡”按钮将生产的数据保存在SD卡的目录下。
- SDcardFileDemo示例说明了如何将数据保存在SD卡。
-
SDcardFileDemo示例
-
用户界面
-
SDcardFileDemo示例运行后,在每次点击“写入SD卡”按钮后,都会在SD卡中生产一个新文件,文件名各不相同。
-
SD卡中生产的文件:
-
-
SDcardFileDemo示例的核心代码
-
资源文件
-
程序开发人员可以将程序开发阶段已经准备好的原始格式文件和XML文件分别存放在/res/raw和/res/xml目录下,供应用程序在运行时进行访问。
- 原始格式文件可以是任何格式的文件,例如视频格式文件、音频格式文件、图像文件和数据文件等等,在应用程序编译和打包时,/res/raw目录下的所有文件都会保留原有格式不变。
- /res/xml目录下的XML文件,一般用来保存格式化的数据,在应用程序编译和打包时会将XML文件转换为高效的二进制格式,应用程序运行时会以特殊的方式进行访问。
-
ResourceFileDemo示例
-
ResourceFileDemo示例演示了如何在程序运行时访问资源文件。
- 当用户点击“读取原始文件”按钮时,程序将读取/res/raw/raw_file.txt文件,并将内容显示在界面上。
-
当用户点击“读取XML文件”按钮时,程序将读取/res/xml/people.xml文件,并将内容显示在界面上。
-
读取原始格式文件,首先需要调用getResource()函数获得资源对象,然后通过调用资源对象的openRawResource()函数,以二进制流的形式打开指定的原始格式文件。在读取文件结束后,调用close()函数关闭文件流。
-
ResourceFileDemo示例中关于读取原始格式文件的核心代码如下:
Resources resources = this.getResources(); InputStream inputStream = null; try { inputStream = resources.openRawResource(R.raw.raw_file); byte[] reader = new byte[inputStream.available()]; while (inputStream.read(reader) != -1) { } displayView.setText(new String(reader,"utf-8")); } catch (IOException e) { Log.e("ResourceFileDemo", e.getMessage(), e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { } } }
- 代码第8行的new String(reader,“utf-8”),表示以UTF-8的编码方式,从字节数组中实例化一个字符串。
-
程序开发人员需要确定/res/raw/raw_file.txt文件使用的是UTF-8编码方式,否则程序运行时会产生乱码。确认的方法:
-
右击raw_file.txt文件。
-
选择“Properties”打开raw_file.txt文件的属性设置框。
-
在Resource栏下的Text file encoding中,选择“Other:UTF-8”。
-
-
-
/res/xml目录下的XML文件会转换为一种高效的二进制格式
-
如何在程序运行时读取/res/xml目录下的XML文件
- 首先在/res/xml目录下创建一个名为people.xml的文件。
- XML文件定义了多个<person>元素,每个<person>元素都包含三个属性name、age和height,分别表示姓名、年龄和身高。
-
/res/xml/people.xml文件代码如下:
<people> <person name="李某某" age="21" height="1.81" /> <person name="王某某" age="25" height="1.76" /> <person name="张某某" age="20" height="1.69" /> </people>
-
-
读取XML格式文件,首先通过调用资源对象的getXml()函数,获取到XML解析器XmlPullParser。XmlPullParser是Android平台标准的XML解析器,这项技术来自一个开源的XML解析API项目XMLPULL。
-
ResourceFileDemo示例中关于读取XML文件的核心代码如下:
-
XmlPullParser的XML事件类型
-
-
第9章 2++数据存储和访问(SQLite示例)
1 ListView显示数据库数据(ShowActivity)
ListView 2个关键点
-
自定义layout布局;
-
使用SimpleCursorAdapter适配器填充:(用于显示数据库数据)
该适配器允许将一个Cursor(查询结果集)的数据列绑定到ListView自定 义布局中的TextView 或 ImageView组件上(详见后面代码)
-
代码详解:
-
ListView自定义layout布局,新建一个名为listview.xml布局:
-
使用SimpleCursorAdapter填充ListView,SimpleCursorAdapter基本方法:
-
ShowAcitvity的OnCreate()主要代码:
-
2 添加记录功能(InsertActivity)
-
ShowActivity“添加”按钮
-
InsertActivity“保存”按钮
-
ShowActivity接收回传信息(下面代码与onCreate并列)
3 给ListView添加ContextMenu菜单:删除+编辑(UpdateActivity)
主要步骤
-
第1步:新建菜单资源
-
在res下新建menu目录
-
在menu目录下新建一个manage.xml文件,代码:
-
-
第2步:创建上下文菜单(下面代码放在ShowActivity中,与onCreate并列)
-
第3步:添加上下文菜单选中项方法(下面代码放在ShowActivity中,与onCreateContextMenu并列)
-
第4步:将上下文菜单注册到ListView上(完)(下面代码放在ShowActivity中onCreate中)
-
后续删除、修改操作分析:
-
两个操作的关键点:
- 如何得到选中行的id值以及其他字段值(name、age)。
- 删除:根据id值作为删除记录的条件。
- 修改:将id值及其他字段的值传到UpdateActivity显示和修改。
-
关键点代码:
-
具体代码:
-
删除操作 delete(MenuItem item) 方法
-
修改操作
UpdateActivity:oncreat()代码
-
ShowActivity接收回传信息
-
-