7. 内容提供者(ContentProvider)
7.1. 什么是内容提供者
Ÿ 内容提供者是Android中的四大组件之一,可以将应用中的数据对外进行共享
Ÿ 内容提供者将数据的访问方式统一,不必针对不同数据类型采取不同的访问策略
Ÿ 内容提供者将数据封装,只暴露出我们希望提供给其他程序的数据
Ÿ 内容提供者中数据更改可被监听
7.2. 创建内容提供者
Ÿ 定义类继承ContentProvider,根据需要重写内部方法
Ÿ 在清单文件的<application>节点下进行配置,<provider>标签中需要指定name和authorities属性
name为类名,包名从程序Package开始,以“.”开始
authorities:是访问Provider时的路径,要唯一
Ÿ URI代表要操作的数据,由scheme、authorites、path三部分组成
content://cn.itcast.sqlite.provider/person
scheme:固定为content,代表访问内容提供者
authorites:<provider>节点中的authorites属性
path:程序定义的路径,可根据业务逻辑定义
7.3. 完成CRUD方法
Ÿ 当程序调用CRUD方法时会传入Uri
Ÿ 我们通过Uri判断调用者要操作的数据
可以使用工具类UriMatcher来判断Uri
addURI方法可以添加Uri
match方法可以匹配一个Uri判断其类型
Ÿ 根据业务逻辑操作数据
7.4. 访问内容提供者
Ÿ 通过Context获得ContentResolver对象
Ÿ 调用ContentResolver对象的方法即可访问内容提供者
7.5. 完成getType方法
Ÿ 如果返回数据是单条数据:vnd.android.cursor.item
Ÿ 如果返回数据是多条数据:vnd.android.cursor.dir
7.6. 监听内容提供者数据变化
Ÿ 在内容提供者中可以通知其他程序数据发生变化
通过Context的getContentResolver()方法获取ContentResolver
调用其notifyChange()方法发送数据修改通知
Ÿ 在其他程序中可以通过ContentObserver监听数据变化
通过Context的getContentResolver()方法获取ContentResolver
调用其registerContentObserver()方法指定对某个Uri注册ContentObserver
自定义ContentObserver,重写onChange()方法获取数据
7.7. GIT获取源代码
Ø 资源地址
Ÿ Git
http://code.google.com/p/msysgit/
Ÿ 源码
注意:
GIT1.7.7安装后不能卸载,可以用其他版本覆盖后再卸载。
使用GIT时不要使用中文目录,否则GIT GUI会报错无法启动。删除C盘中.gitconfig文件可以解决。
7.8. 监听短信
Ÿ Android系统提供了Provider对短信进行查询,当发出短信时也会发送更改通知
Ÿ 短信数据库在 com.android.providers.telephony
Ÿ 定义一个Observer监听"content://sms"
Ÿ 在onChange()方法中查询"content://sms"
Ÿ 需要权限android.permission.READ_SMS
7.9. 操作联系人
Ø 获取所有联系人
Ÿ Android系统中的联系人也是通过ContentProvider来对外提供数据的
Ÿ 数据库路径为:/data/data/com.android.providers.contacts/database/contacts2.db
Ÿ 我们需要关注的有3张表
raw_contacts:其中保存了联系人id
data:和raw_contacts是多对一的关系,保存了联系人的各项数据
mimetypes:为数据类型
Ÿ Provider的authorites为com.android.contacts
Ÿ 查询raw_contacts表的路径为:contacts
Ÿ 查询data表的路径为:contacts/#/data
这个路径为连接查询,直接查询“mimetype”字段就可以根据“mimetype_id”查询到mimetypes表中的数据
Ÿ 先查询raw_contacts得到每个联系人的id,在使用id从data表中查询对应数据,根据mimetype分类数据
Ø 通过电话号码获取联系人
Ÿ 系统内部提供了根据电话号码获取data表数据的功能,路径为:data/phones/filter/*
Ÿ 用电话号码替换“*”部分就可以查到所需数据,获取“display_name”可以获取到联系人显示名
Ø 添加联系人
Ÿ 先向raw_contacts表插入id,路径为:raw_contacts
Ÿ 得到id之后再向data表插入数据,路径为:data
Ø 使用事务添加联系人
Ÿ 在添加联系人得时候是分多次访问Provider,如果在过程中出现异常,会出现数据不完整的情况,这些操作应该放在一次事务中
Ÿ 使用ContentResolver的applyBatch(String authority,ArrayList<ContentProviderOperation> operations) 方法可以将多个操作在一个事务中执行
Ÿ 文档位置:
8. 网络通信
8.1. 获取文本数据
Ÿ 通过URL对象封装地址,打开一个HttpURLConnection
Ÿ 设置头信息之后获取响应码,如果成功返回200即可从HttpURLConnection中获取输入流读取数据
Ÿ 代码过长屏幕显示不全可以使用<ScrollView>进行显示
Ÿ 需要访问网络的权限
<uses-permission android:name="android.permission.INTERNET" />
8.2. 获取网络图片
Ÿ 通过BitmapFactory的decodeByteArray(byte[] data, int offset, int length)方法将数据转换为图片对象
8.3. 获取XML
Ÿ 使用URL封装路径,打开一个HttpURLConnection
Ÿ 设置头信息之后获取相应码,从输入流中获取数据
Ÿ 使用XmlPullPaser解析
8.4. 获取JSON
Ÿ 使用URL封装路径,打开一个HttpURLConnection
Ÿ 设置头信息之后获取相应码,从输入流中获取数据
Ÿ 将数据转为String,封装成JSONArray对象
Ÿ 遍历JSONArray对象,调用获取其中的JSONObject
Ÿ 再从JSONObject中获取每个字段的信息
8.5. 发送GET请求
Ÿ 拼接路径和参数,通过URL进行封装,打开一个HttpURLConnection,发送请求
Ÿ 如果参数是中文会出现乱码
Ÿ URL中包含的中文参数需要使用URLEncoder进行编码
Ÿ 服务器端如果是TOMCAT,其默认使用ISO8859-1编码,接收时需要处理编码问题
8.6. 发送POST请求
Ÿ 通过URL打开一个HttpURLConnection
Ÿ 头信息中除了超时时间和请求方式之外还必须设置Content-Type和Content-Length
Ÿ 从HttpURLConnection获得输出流输出参数数据
Ÿ 服务端可以使用request对象的setCharacterEncoding方法设置编码
8.7. 发送XML,访问WebService
Ø 发送XML
Ÿ 通过URL封装路径打开一个HttpURLConnection
Ÿ 设置请求方式,Content-Type和Content-Length
XML文件的Content-Type为:text/xml; charset=UTF-8
Ÿ 使用HttpURLConnection获取输出流输出数据
Ø WebService
Ÿ WebService是发布在网络上的API,可以通过发送XML调用,WebService返回结果也是XML数据
Ÿ WebService没有语言限制,只要可以发送XML数据和接收XML数据即可
Ÿ http://www.webxml.com.cn 网站上提供了一些WebService服务,我们可以对其进行调用
Ÿ http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?op=getMobileCodeInfo 中提供了电话归属地查询的使用说明
8.8. HTTP协议上传文件
Ÿ 搭建服务器,完成上传功能
Ÿ 使用浏览器上传,查看请求信息
Ø HttpURLConnection
Ÿ 通过URL封装路径打开一个HttpURLConnection
Ÿ 设置请求方式以及头字段:Content-Type、Content-Length、Host
Ÿ 拼接数据发送
Ø Socket
Ÿ 使用HttpURLConnection发送时内部有缓存机制,如果上传较大文件会导致内存溢出
Ÿ 我们可以使用Socket发送TCP请求,将上传数据分段发送
Ø HttpClient
public void upload(String name, String password, String path) throws Exception {
// 创建HttpClient对象
HttpClient client = new HttpClient();
// 设置超时事件
client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
// 创建一个Post请求, 指定路径
PostMethod postMethod = new PostMethod("http://192.168.1.102:8080/14.Web/LoginServlet");
// 封装每个表单项
Part[] parts = { new StringPart("name", name), new StringPart("password", password), new FilePart("file", new File(path)) };
// 给Post请求设置实体
postMethod.setRequestEntity(new MultipartRequestEntity(parts, postMethod.getParams()));
// 执行Post请求
client.executeMethod(postMethod);
// Post请求是释放资源
postMethod.releaseConnection();
}
8.9. 多线程断点续传下载器
Ÿ 在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度
Ÿ 手机端下载数据时难免会出现无信号断线、电量不足等情况,所以需要断点续传功能
Ÿ 根据下载数据长度计算每个线程下载的数据位置,程序中开启多个线程并发下载
在请求头中设置Range字段就可以获取指定位置的数据,例如:Range: bytes=100-200
Ÿ 在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,下次启动时从记录位置继续下载
Ø 多线程下载
Ÿ 进度条使用<Progress>进行配置
默认为圆形进度条,水平进度条需要配置style属性,?android:attr/progressBarStyleHorizontal
使用android.R.attr.progressBarStyleHorizontal作为样式
Ÿ 当点击下载按钮时开启多线程下载,下载过程中修改进度条进度
设置最大刻度:setMax()
设置当前进度:setProgress()
Ø 断点续传
Ÿ 断点续传需要在下载过程中记录每条线程的下载进度
Ÿ 每次下载开始之前先读取数据库,查询是否有未完成的记录,有就继续下载,没有则创建新记录插入数据库
Ÿ 在每次向文件中写入数据之后,在数据库中更新下载进度
Ÿ 下载完成之后删除数据库中下载记录
Ø Handler传输数据
Ÿ 主线程中创建的View只能在主线程中修改,其他线程只能通过和主线程通信,在主线程中改变View数据
Ÿ 我们使用Handler可以处理这种需求
主线程中创建Handler,重写handleMessage()方法
新线程中使用Handler发送消息,主线程即可收到消息,并且执行handleMessage()方法
Ø 动态生成新View
Ÿ 创建XML文件,将要生成的View配置好
Ÿ 获取系统服务LayoutInflater,用来生成新的View
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
Ÿ 使用inflate(int resource, ViewGroup root)方法生成新的View
Ÿ 调用当前页面中某个容器的addView,将新创建的View添加进来
9. 活动(Activity)
9.1. 创建Activity
Ø 定义Activity
Ÿ 定义类继承Activity
Ÿ 在AndroidManifest.xml的<application>节点中声明<activity>
Ø 显式意图创建方式
Ÿ 构造函数,代码少
new Intent(this, NewActivity.class);
Ÿ 类名形式,灵活,可扩展性强
intent.setClassName(this, "cn.itcast.activity.NewActivity");
Ÿ 包名类名形式,可启动其他程序中的Activity
intent.setClassName("cn.itcast.downloader", "cn.itcast.downloader.MainActivity");
Ø 创建Activity并传递数据
Ÿ 在意图对象中封装了一个Bundle对象,可以用来携带数据
Ÿ 在新Activity中可以获得意图对象以获取其中Bundle保存的数据
Ø 创建Activity获取返回数据
Ÿ 使用startActivityForResult(Intent intent, int requestCode) 方法打开Activity
Ÿ 重写onActivityResult(int requestCode, int resultCode, Intent data) 方法
Ÿ 新Activity中调用setResult(int resultCode, Intent data) 设置返回数据之后,关闭Activity就会调用onActivityResult方法
Ø 隐式意图创建Activity
Ÿ 显式意图是指在创建意图时指定了组件,而隐式意图则不指定组件,通过动作、类型、数据匹配对应的组件
Ÿ 在清单文件中定义<activity>时需要定义<intent-filter>才能被隐式意图启动
Ÿ <intent-filter>中至少配置一个<action>和一个<category>,否则无法被启动
Ÿ Intent对象中设置的action、category、data在<intent-filter>必须全部包含才能启动
Ÿ <intent-filter>中的<action>、<category>、<data>都可以配置多个,Intent对象中不用全部匹配,每样匹配一个即可启动
Ÿ 如果一个意图可以匹配多个Activity,Android系统会提示选择
9.2. 生命周期
Ÿ Acitivity三种状态
运行:activity在最前端运行
暂停:activity可见,但前端还有其他acti vity,被覆盖一部分,或者前端activity透明
停止:activity不可见,完全被覆盖
Ÿ 生命周期相关方法
onCreate:创建时调用,或者程序在暂停、停止状态下被杀死之后重新打开时也会调用
onStart:onCreate之后或者从停止状态恢复时调用
onResume:onStart之后或者从暂停状态恢复时调用,从停止状态恢复时由于调用onStart,也会调用onResume
onPause:进入暂停、停止状态,或者销毁时会调用
onStop:进入停止状态,或者销毁时会调用
onDestroy:销毁时调用
onRestart:从停止状态恢复时调用
Ÿ 保存信息相关方法
onSaveInstanceState:在Activity被动的摧毁或停止的时候调用,用于保存运行数据,可以将数据存在在Bundle中
onRestoreInstanceState:该方法在Activity被重新绘制的时候调用,例如改变屏幕方向,savedInstanceState为onSaveInstanceState保存的数据
9.3. 启动模式
Ÿ 在AndroidManifest.xml中的<activity>标签中可以配置android:launchMode属性,用来控制Actvity的启动模式
Ÿ 在Android系统中我们创建的Acitivity是以栈的形式呈现的
standard:每次调用startActivity()启动时都会创建一个新的Activity放在栈顶
singleTop:如果启动的Activity时,指定Activity不在栈顶就创建,如在栈顶,则不再创建
singleTask:如果启动的Activity不存在就创建,如果存在直接跳转到指定的Activity所在位置
singleInstance:如果启动的Activity不存在就创建,如果存在就将指定的Activity移动到栈顶
9.4. 内存管理
Ÿ Android系统在运行多个进程时,如果系统资源不足,会强制结束一些进程。优先选择哪个进程来结束是有优先级的。以下顺序靠上的优先结束
空:进程中所有Activity都已销毁
后台:进程中有一个停止状态的Activity
可见:进程中有一个暂停状态的Activity
前台:进程中正在运行一个Activity
10. 广播接收者(BroadcastReceiver)
10.1. 定义广播接收者
Ÿ 定义类继承BroadcastReceiver,重写onReceive方法
Ÿ 清单文件中声明<receiver>,需要在其中配置<intent-filter>指定接收广播的动作
Ÿ 当接收到匹配广播之后就会执行onReceive方法
Ÿ BroadcastReceiver除了在清单文件中声明,也可以在代码中声明,使用registerReceiver方法注册Receiver
10.2. 发送广播
Ø 无序广播
Ÿ 使用sendBroadcast方法发送
Ÿ 被所有广播接收者接收,无序,不可中断
Ÿ 广播时可设置接收者权限,仅当接收者含有权限才能接收
Ÿ 接收者的<receiver>也可设置发送方权限,只接收含有权限应用的广播
Ø 有序广播
Ÿ 使用sendOrderedBroadcast方法发送
Ÿ 接收者可以在<intent-filter>中定义android:priority定义优先级,数字越大优先级越高
Ÿ 被各个广播接收者逐个接收,中途可以中断或者添加数据
abortBroadcast()
getResultExtras(true).putString("data", "新增数据");
10.3. 监听短信接收
Ÿ Android系统在收到短信的时候会发送一条有序广播,我们如果定义一个接收者接收这个广播,就可以得到短信内容,也可以拦截短信
Ÿ 定义广播接收者接收广播android.provider.Telephony.SMS_RECEIVED
Ÿ 在onReceive方法内部调用Intent的getExtras()再调用get(String)获取其中pdus字段,得到一个Object[],其中每一个元素都是一个byte[]
Ÿ 通过SmsMessage类的createFromPdu方法创建SmsMessage对象
Ÿ 从SmsMessage对象中即可获取发送者号码、短信内容、发送时间等信息
Ÿ 需要接收短信权限:<uses-permission android:name="android.permission.RECEIVE_SMS"/>
Ÿ Android系统中收到短信的通知是一个有序通知,我们如需拦截垃圾短信,可以配置较高的priority,收到信息进行判断是否abortBroadcast()
10.4. 监听呼出电话
Ÿ 定义广播接收者接收 android.intent.action.NEW_OUTGOING_CALL
Ÿ 需要权限 <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
Ÿ 在onReceive方法中使用getResultData() 和 setResultData() 方法获取和设置电话号码
10.5. 生命周期
Ÿ 广播接收者的生命周期是非常短暂的,在接收到广播的时候创建,onReceive()方法结束之后销毁
Ÿ 广播接收者中不要做一些耗时的工作,否则会弹出Application No Response错误对话框
Ÿ 最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易被系统杀掉
Ÿ 耗时的较长的工作最好放在服务中完成
11. 服务(Service)
11.1. 基本概念
Ÿ Service是一种在后台运行,没有界面的组件,由其他组件调用开始。
Ÿ 创建Service,定义类继承Service,AndroidManifest.xml中定义<service>
Ÿ 开启Service,在其他组件中调用startService方法
Ÿ 停止Service,调用stopService方法
11.2. 电话录音
需要权限:android.permission.READ_PHONE_STATE
TelephonyManager manager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
manager.listen(new MyListener(), PhoneStateListener.LISTEN_CALL_STATE);
private final class MyListener extends PhoneStateListener {
private String num;
private MediaRecorder recorder;
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
num = incomingNumber;
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
try {
File file = new File(Environment.getExternalStorageDirectory(), num + "_" + System.currentTimeMillis() + ".3gp");
recorder = new MediaRecorder();
recorder.setAudioSource(AudioSource.MIC);
recorder.setOutputFormat(OutputFormat.THREE_GPP);
recorder.setAudioEncoder(AudioEncoder.AMR_NB);
recorder.setOutputFile(file.getAbsolutePath());
recorder.prepare();
recorder.start();
} catch (Exception e) {
e.printStackTrace();
}
break;
case TelephonyManager.CALL_STATE_IDLE:
if (recorder != null) {
recorder.stop();
recorder.release();
}
break;
}
}
}
11.3. 绑定本地服务
Ÿ 使用bindService绑定服务,传入一个自定义的ServiceConnection用来接收IBinder
Ÿ 定义一个业务接口,其中定义需要的使用的方法
Ÿ 服务中自定义一个IBinder继承Binder并实现业务接口,在onBind方法中返回
Ÿ 调用端将IBinder转为接口类型,调用接口中的方法即可调用到服务中的方法
11.4. 绑定远程服务
Ÿ 远程绑定服务时无法通过同一个接口来调用方法,这时就需要使用AIDL技术
Ÿ 将接口扩展名改为“.aidl”
Ÿ 去掉权限修饰符
Ÿ gen文件夹下会生成同名接口
Ÿ 将服务中自定义的IBinder类改为继承接口中的Stub
Ÿ ServiceConnection中返回的IBinder是代理对象,不能使用强转,改用Stub.asInterface()
11.5. AIDL使用自定义类型
Ÿ AIDL默认只能使用Java中基本数据类型和String、List、Map,List和Map中的元素类型也只能是这些类型。
Ÿ 如果需要使用其他类型数据,使用的类必须实现Parcelable接口以完成序列化和反序列化工作
重写 public void writeToParcel(Parcel dest, int flags)
定义 public static final Parcelable.Creator<Person> CREATOR
Ÿ 定义该类对应的AIDL
package 包名
parcelable 类名
Ÿ 在接口AIDL中导入该类,注意:即使是同一个包也需要导入
12. 多媒体
12.1. 音频播放器
12.2. 视频播放器
screenSV.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // 设置缓冲区数据
screenSV.getHolder().setKeepScreenOn(true); // 设置屏幕保持
screenSV.getHolder().addCallback(new MyCallback()); // 设置回调函数
player.reset();
player.setDisplay(screenSV.getHolder()); // 设置显式
player.setDataSource("/mnt/sdcard/1.mp4"); // 设置数据源
player.prepare(); // 准备
player.seekTo(position); // 跳转到指定位置
player.start();
12.3. 拍照
Ÿ 需要权限
<uses-permission android:name="android.permission.CAMERA" />
Ÿ 打开摄像头
Camera.open()
SDK2.3之后支持前置摄像头,open方法可以接收一个int参数,用来指定哪个摄像头
Ÿ 设置预览显示位置
setPreviewDisplay(SurfaceHolder holder)
注意SurfaceView不在前端显示的时候会被销毁,恢复之后会重绘
Ÿ 开始预览
startPreview()
将摄像头拍摄画面显示在SurfaceView中,在此之前可对摄像头进行参数配置
getParameters() 方法可以获取摄像头的相关参数Parameters,调用其内部方法即可进行配置
Ÿ 自动对焦
autoFocus(AutoFocusCallback cb)
自动对焦是一个异步操作,如果我们向等待自动对焦结束之后才开始拍照,需要传入一个回调对象,在其回调函数中调用拍照方法
Ÿ 拍照
takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg)
拍照也是异步操作,需要通过回调函数来得到拍照之后的数据
注意拍照之后摄像头不回自动回到预览状态,需要重写调用startPreview()方法
12.4. 录像
Ÿ 需要权限
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.CAMERA"/>
Ÿ 创建MediaRecorder
new MediaRecorder()
Ÿ 设置音频输入源
setAudioSource(int audio_source)
Ÿ 设置视频输入源
setVideoSource(int video_source)
Ÿ 设置输出格式
setOutputFormat(int output_format)
Ÿ 设置音频编码器
setAudioEncoder(int audio_encoder)
Ÿ 设置视频编码器
setVideoEncoder(int video_encoder)
Ÿ 设置预览显示位置
setPreviewDisplay(Surface sv)
Ÿ 设置输出文件
setOutputFile(String path)
Ÿ 准备录制
prepare()
Ÿ 开始录制
start()
开始录制之前需要结束摄像头的预览
Ÿ 结束录制释放资源
stop()
release()