[color=brown][size=large]一.Activity[/size][/color]
http://xiechengfa.iteye.com/category/146425 博客
首先看一下Android api中所提供的Activity生命周期图(不明白的,可以看完整篇文章,在回头看一下这个图,你会明白的):
[img]http://dl2.iteye.com/upload/attachment/0087/1500/146d078e-fdfe-3371-b889-a8f961f2ea55.png[/img]
Activity其实是继承了ApplicationContext这个类,我们可以重写以下方法,如下代码:
为了便于大家更好的理解,我简单的写了一个Demo,不明白Activity周期的朋友们,可以亲手实践一下,大家按照我的步骤来。
第一步:新建一个Android工程,我这里命名为ActivityDemo.
第二步:修改ActivityDemo.java(我这里重新写了以上的七种方法,主要用Log打印),代码如下:
第三步:运行上述工程,效果图如下(没什么特别的):
[img]http://dl2.iteye.com/upload/attachment/0087/1502/329d2066-c014-3fd5-87b0-5a93e57ea01e.jpg[/img]
我们打开应用时先后执行了onCreate()->onStart()->onResume三个方法,看一下LogCat视窗如下:
[img]http://dl2.iteye.com/upload/attachment/0087/1504/476a4c4c-93e5-3734-ade2-3f55d63fe3e1.jpg[/img]
当我们打开应用程序时,比如浏览器,我正在浏览NBA新闻,看到一半时,我突然想听歌,这时候我们会选择按HOME键,然后去打开音乐应用程序,而当我们按HOME的时候,Activity先后执行了onPause()->onStop()这两个方法,这时候应用程序并没有销毁。如下图所示:
[img]http://dl2.iteye.com/upload/attachment/0087/1506/a6da0ded-70fb-3887-bd3a-a5694e991463.jpg[/img]
而当我们再次启动ActivityDemo应用程序时,则先后分别执行了onRestart()->onStart()->onResume()三个方法,如下图所示:
[img]http://dl2.iteye.com/upload/attachment/0087/1508/92216c78-11b6-3616-aad5-9ac20965b2ae.jpg[/img]
这里我们会引出一个问题,当我们按HOME键,然后再进入ActivityDemo应用时,我们的应用的状态应该是和按HOME键之前的状态是一样的,同样为了方便理解,在这里我将ActivityDemo的代码作一些修改,就是增加一个EditText。
第四步:修改main.xml布局文件(增加了一个EditText),代码如下:
第五步:然后其他不变,运行ActivityDemo程序,在EditText里输入如"Frankie"字符串(如下图:)
[img]http://dl2.iteye.com/upload/attachment/0087/1510/a45f28bf-7dc9-35e2-9d9c-c2d04620e9a3.jpg[/img]
这时候,大家可以按一下HOME键,然后再次启动ActivityDemo应用程序,这时候EditText里并没有我们输入的"Frankie"字样,如下图:
[img]http://dl2.iteye.com/upload/attachment/0087/1512/134d45c2-9979-368d-a30e-e7b0cbacd081.jpg[/img]
这显然不能称得一个合格的应用程序,所以我们需要在Activity几个方法里自己实现,如下第六步所示:
第六步修改ActivityDemo.java代码如下:
第七步:重新运行ActivityDemo程序,重复第五步操作,当我们按HOME键时,再次启动应用程序时,EditText里有上次输入的"Frankie"字样,如下图如示:
[img]http://dl2.iteye.com/upload/attachment/0087/1516/e8d92584-390b-39f7-b25e-d1606a9cd2d3.jpg[/img]
OK,大功基本告成,这时候大家可以在回上面看一下Activity生命周期图,我想大家应该完全了解了Activity的生命周期了.
[color=brown][size=large]二.Service[/size][/color]
一.基础知识
服务一般分为两种:
1:本地服务, Local Service 用于应用程序内部。在Service可以调用Context.startService()启动,调用Context.stopService()结束。在内部可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。无论调用了多少次startService(),都只需调用一次stopService()来停止。
2:远程服务, Remote Service 用于android系统内部的应用程序之间。可以定义接口并把接口暴露出来,以便其他应用进行操作。客户端建立到服务对象的连接,并通过那个连接来调用服务。调用Context.bindService()方法建立连接,并启动,以调用 Context.unbindService()关闭连接。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。
提供给可被其他应用复用,比如定义一个天气预报服务,提供与其他应用调用即可。
那么先来看Service的生命周期吧:如图:
[img]http://dl2.iteye.com/upload/attachment/0087/1518/dfcc06cd-3b4e-3dcc-887b-be956091b3fd.png[/img]
context.startService() ->onCreate()- >onStart()->Service running--调用context.stopService() ->onDestroy()
context.bindService()->onCreate()->onBind()->Service running--调用>onUnbind() -> onDestroy()
从上诉可以知道分别对应本地的,以及远程的,也对应不同的方式启动这个服务。
二.实战
我们可以定义一个本地服务继承Service,然后在这个服务里播放媒体播放器或者记录地理位置变化。通常有时候我们的Service要与Activity交互,那么可以可以定义一个内部类,返回这个Service,当然我们要考虑到如果是以绑定方式启动服务,那么内部类可以定义为继承Binder,然后返回本地服务,具体代码如下。
我们可以从上面知道
//定义内容类继承Binder
public class LocalBinder extends Binder{
//返回本地服务
LocalService getService(){
return LocalService.this;
}
}
可以返回这个服务,然后activity可以通过服务调用服务的方法了。
那么如何启动服务呢?从上面基础知识中,我们知道有2种方法,如下:
//启动服务
第2种绑定方式:
在绑定服务的时候,需要一个服务连接对象,ServiceConnection,服务一旦连接,就会调用onServiceConnected方法,我们可以在这个方法里面返回我们的本地服务对象,具体看代码;而在服务断开时候会调用onServiceDisconnected方法,我们可以清理一些服务资源。
[size=large][color=brown]三、ContentProvider[/color][/size]的概念
ContentProvider:为存储和获取数据提供统一的接口。可以在不同的应用程序之间共享数据。Android已经为常见的一些数据提供了默认的ContentProvider
1、ContentProvider使用表的形式来组织数据
无论数据的来源是什么,ContentProvider都会认为是一种表,然后把数据组织成表格
2、ContentProvider提供的方法
query:查询
insert:插入
update:更新
delete:删除
getType:得到数据类型
onCreate:创建数据时调用的回调函数
3、每个ContentProvider都有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。Android所提供的ContentProvider都存放在android.provider包当中
二、ContentProvider的内部原理
自定义一个ContentProvider,来实现内部原理
步骤:
1、定义一个CONTENT_URI常量(里面的字符串必须是唯一)
Public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentprovider");
如果有子表,URI为:
Public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentProvider/users");
2、定义一个类,继承ContentProvider
Public class MyContentProvider extends ContentProvider
3、实现ContentProvider的所有方法(query、insert、update、delete、getType、onCreate)
4、在AndroidMinifest.xml中进行声明
**为ContentProvider提供一个常量类MyContentProviderMetaData.java
[color=brown][size=large]四.BroadcastReceiver[/size] [/color]
用接收短信举例:
第一种方式 :
实现
public class MyBroadcastReceiver extends BroadcastReceiver {
// action 名称
String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED" ;
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals( SMS_RECEIVED )) {
// 相关处理 : 地域变换、电量不足、来电来信;
}
}
}
系统注册:在 AndroidManifest.xml 中注册
< receiver android:name = ".MyBroadcastReceiver" >
< intent-filter android:priority = "1000" >
< action android:name = " android.provider.Telephony.SMS_RECEIVED" />
</ intent-filter >
</ receiver > 当然了需要权限 :
< uses-permission android:name = "android.permission.RECEIVE_SMS" />
< uses-permission android:name = "android.permission.SEND_SMS" />
第二种方式:
// 广播接收者 - 广播的接收
private BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 相关处理,如收短信,监听电量变化信息
}
};
代码中注册:
IntentFilter intentFilter = new IntentFilter( "android.provider.Telephony.SMS_RECEIVED " );
registerReceiver( mBatteryInfoReceiver , intentFilter);
生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报错 。
每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 , 该对象即被销毁 . 当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应 . 所以在
BroadcastReceiver 里不能做一些比较耗时的操作 , 否侧会弹出 ANR(Application No
Response) 的对话框 . 。(如图):
怎么用好 BroadcastReceiver ?
如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由 Service 来完成 . 这里不能使用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短 , 子线程可能还没有结束
BroadcastReceiver 就先结束了 .BroadcastReceiver 一旦结束 , 此时 BroadcastReceiver 的
所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 ). 如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的 .
广播类型及广播的收发
广播类型
普通广播 (Normal broadcasts)
发送一个广播,所以监听该广播的广播接收者都可以监听到改广播。
异步广播 , 当处理完之后的Intent ,依然存在,这时候registerReceiver(BroadcastReceiver, IntentFilter) 还能收到他的值,直到你把它去掉 , 不能将处理结果传给下一个接收者 , 无法终止广播 .
有序广播 (Ordered broadcasts)
按照接收者的优先级顺序接收广播 , 优先级别在 intent-filter 中的 priority 中声明 ,-1000 到
1000 之间 , 值越大 , 优先级越高 . 可以终止广播意图的继续传播 . 接收者可以篡改内容 .
广播的收发
该组件接收被广播的 intent,Context 可以通过 sendBroadcast() 和 sendOrderedBroadcast()
方法实现广播的发送 .
首先在需要发送信息的地方 ,把要发送的信息和用于过滤的信息 ( 如 Action 、 Category) 装入一个 Intent 对象 ,然后通过调用 Context.sendBroadcast() 、 sendOrderBroadcast() 或 sendStickyBroadcast() 方法,把 Intent 对象以广播方式发送出去。
使用 sendBroadcast() 或 sendStickyBroadcast() 方法发出去的 Intent ,所有满足条件的 BroadcastReceiver 都会随机地执行其 onReceive() 方法
普通广播的发送和接收:
sendBroadcast(intent);
Intent intent = new Intent( "cn.lenovo.yangguangf " );
sendBroadcast(intent);
priority :这个是 AndroidManifest.xml 中 intent-filter 的参数。
< receiver android:name = ".MyBroadcastReceiver" >
< intent-filter android:priority = "1000" >
< action android:name = "cn.lenovo.yangguangfu" />
</ intent-filter >
</ receiver >
sendOrderedBroadcast(intent, receiverPermission);
1 ,他决定该广播的级别,级别数值是在 -1000 到 1000 之间 , 值越大 , 优先级越高;
2 ,同级别接收是先后是随机的;级别低的收到广播;
3 ,在 android 系统中只要监听该广播的接收者,都能够收到 sendBroadcast(intent) 发出的广播 ;
3 ,不能截断广播的继续传播,
4 ,实验现象,在这个方法发来的广播中,代码注册方式中,收到的广播的先后和注明优先级最高的他们的先后是随机。如果都没有优先级,代码注册收到为最先。
http://xiechengfa.iteye.com/category/146425 博客
首先看一下Android api中所提供的Activity生命周期图(不明白的,可以看完整篇文章,在回头看一下这个图,你会明白的):
[img]http://dl2.iteye.com/upload/attachment/0087/1500/146d078e-fdfe-3371-b889-a8f961f2ea55.png[/img]
Activity其实是继承了ApplicationContext这个类,我们可以重写以下方法,如下代码:
public class Activity extends ApplicationContext {
protected void onCreate(Bundle savedInstanceState);
protected void onStart();
protected void onRestart();
protected void onResume();
protected void onPause();
protected void onStop();
protected void onDestroy();
}
为了便于大家更好的理解,我简单的写了一个Demo,不明白Activity周期的朋友们,可以亲手实践一下,大家按照我的步骤来。
第一步:新建一个Android工程,我这里命名为ActivityDemo.
第二步:修改ActivityDemo.java(我这里重新写了以上的七种方法,主要用Log打印),代码如下:
package com.tutor.activitydemo;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class ActivityDemo extends Activity {
private static final String TAG = "ActivityDemo";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.e(TAG, "start onCreate~~~");
}
@Override
protected void onStart() {
super.onStart();
Log.e(TAG, "start onStart~~~");
}
@Override
protected void onRestart() {
super.onRestart();
Log.e(TAG, "start onRestart~~~");
}
@Override
protected void onResume() {
super.onResume();
Log.e(TAG, "start onResume~~~");
}
@Override
protected void onPause() {
super.onPause();
Log.e(TAG, "start onPause~~~");
}
@Override
protected void onStop() {
super.onStop();
Log.e(TAG, "start onStop~~~");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(TAG, "start onDestroy~~~");
}
}
第三步:运行上述工程,效果图如下(没什么特别的):
[img]http://dl2.iteye.com/upload/attachment/0087/1502/329d2066-c014-3fd5-87b0-5a93e57ea01e.jpg[/img]
我们打开应用时先后执行了onCreate()->onStart()->onResume三个方法,看一下LogCat视窗如下:
[img]http://dl2.iteye.com/upload/attachment/0087/1504/476a4c4c-93e5-3734-ade2-3f55d63fe3e1.jpg[/img]
当我们打开应用程序时,比如浏览器,我正在浏览NBA新闻,看到一半时,我突然想听歌,这时候我们会选择按HOME键,然后去打开音乐应用程序,而当我们按HOME的时候,Activity先后执行了onPause()->onStop()这两个方法,这时候应用程序并没有销毁。如下图所示:
[img]http://dl2.iteye.com/upload/attachment/0087/1506/a6da0ded-70fb-3887-bd3a-a5694e991463.jpg[/img]
而当我们再次启动ActivityDemo应用程序时,则先后分别执行了onRestart()->onStart()->onResume()三个方法,如下图所示:
[img]http://dl2.iteye.com/upload/attachment/0087/1508/92216c78-11b6-3616-aad5-9ac20965b2ae.jpg[/img]
这里我们会引出一个问题,当我们按HOME键,然后再进入ActivityDemo应用时,我们的应用的状态应该是和按HOME键之前的状态是一样的,同样为了方便理解,在这里我将ActivityDemo的代码作一些修改,就是增加一个EditText。
第四步:修改main.xml布局文件(增加了一个EditText),代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<EditText
android:id="@+id/editText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
第五步:然后其他不变,运行ActivityDemo程序,在EditText里输入如"Frankie"字符串(如下图:)
[img]http://dl2.iteye.com/upload/attachment/0087/1510/a45f28bf-7dc9-35e2-9d9c-c2d04620e9a3.jpg[/img]
这时候,大家可以按一下HOME键,然后再次启动ActivityDemo应用程序,这时候EditText里并没有我们输入的"Frankie"字样,如下图:
[img]http://dl2.iteye.com/upload/attachment/0087/1512/134d45c2-9979-368d-a30e-e7b0cbacd081.jpg[/img]
这显然不能称得一个合格的应用程序,所以我们需要在Activity几个方法里自己实现,如下第六步所示:
第六步修改ActivityDemo.java代码如下:
package com.tutor.activitydemo;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.EditText;
public class ActivityDemo extends Activity {
private static final String TAG = "ActivityDemo";
private EditText mEditText;
//定义一个String 类型用来存取我们EditText输入的值
private String mString;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mEditText = (EditText)findViewById(R.id.editText);
Log.e(TAG, "start onCreate~~~");
}
@Override
protected void onStart() {
super.onStart();
Log.e(TAG, "start onStart~~~");
}
//当按HOME键时,然后再次启动应用时,我们要恢复先前状态
@Override
protected void onRestart() {
super.onRestart();
mEditText.setText(mString);
Log.e(TAG, "start onRestart~~~");
}
@Override
protected void onResume() {
super.onResume();
Log.e(TAG, "start onResume~~~");
}
//当我们按HOME键时,我在onPause方法里,将输入的值赋给mString
@Override
protected void onPause() {
super.onPause();
mString = mEditText.getText().toString();
Log.e(TAG, "start onPause~~~");
}
@Override
protected void onStop() {
super.onStop();
Log.e(TAG, "start onStop~~~");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(TAG, "start onDestroy~~~");
}
}
第七步:重新运行ActivityDemo程序,重复第五步操作,当我们按HOME键时,再次启动应用程序时,EditText里有上次输入的"Frankie"字样,如下图如示:
[img]http://dl2.iteye.com/upload/attachment/0087/1516/e8d92584-390b-39f7-b25e-d1606a9cd2d3.jpg[/img]
OK,大功基本告成,这时候大家可以在回上面看一下Activity生命周期图,我想大家应该完全了解了Activity的生命周期了.
[color=brown][size=large]二.Service[/size][/color]
一.基础知识
服务一般分为两种:
1:本地服务, Local Service 用于应用程序内部。在Service可以调用Context.startService()启动,调用Context.stopService()结束。在内部可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。无论调用了多少次startService(),都只需调用一次stopService()来停止。
2:远程服务, Remote Service 用于android系统内部的应用程序之间。可以定义接口并把接口暴露出来,以便其他应用进行操作。客户端建立到服务对象的连接,并通过那个连接来调用服务。调用Context.bindService()方法建立连接,并启动,以调用 Context.unbindService()关闭连接。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。
提供给可被其他应用复用,比如定义一个天气预报服务,提供与其他应用调用即可。
那么先来看Service的生命周期吧:如图:
[img]http://dl2.iteye.com/upload/attachment/0087/1518/dfcc06cd-3b4e-3dcc-887b-be956091b3fd.png[/img]
context.startService() ->onCreate()- >onStart()->Service running--调用context.stopService() ->onDestroy()
context.bindService()->onCreate()->onBind()->Service running--调用>onUnbind() -> onDestroy()
从上诉可以知道分别对应本地的,以及远程的,也对应不同的方式启动这个服务。
二.实战
我们可以定义一个本地服务继承Service,然后在这个服务里播放媒体播放器或者记录地理位置变化。通常有时候我们的Service要与Activity交互,那么可以可以定义一个内部类,返回这个Service,当然我们要考虑到如果是以绑定方式启动服务,那么内部类可以定义为继承Binder,然后返回本地服务,具体代码如下。
package com.dongzi;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class LocalService extends Service {
private static final String TAG = "LocalService";
private IBinder binder=new LocalService.LocalBinder();
@Override
public IBinder onBind(Intent intent) {
return binder;
}
MediaPlayer mediaPlayer=null;
@Override
public void onCreate() {
Log.i(TAG, "onCreate");
//这里可以启动媒体播放器
// if(mediaPlayer==null)
// mediaPlayer=MediaPlayer.create(this, uri);
super.onCreate();
}
@Override
public void onStart(Intent intent, int startId) {
Log.i(TAG, "onStart");
super.onStart(intent, startId);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand");
return START_STICKY;
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
super.onDestroy();
}
//定义内容类继承Binder
public class LocalBinder extends Binder{
//返回本地服务
LocalService getService(){
return LocalService.this;
}
}
}
我们可以从上面知道
//定义内容类继承Binder
public class LocalBinder extends Binder{
//返回本地服务
LocalService getService(){
return LocalService.this;
}
}
可以返回这个服务,然后activity可以通过服务调用服务的方法了。
那么如何启动服务呢?从上面基础知识中,我们知道有2种方法,如下:
//启动服务
private void startCustomService(){
Intent intent=new Intent(this,LocalService.class);
startService(intent);
}
第2种绑定方式:
LocalService localService=null;
//用bindService方法启动服务
private void BinderService(){
Intent intent=new Intent(this,LocalService.class);
bindService(intent, new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName componentName, IBinder binder) {
//调用bindService方法启动服务时候,如果服务需要与activity交互,
//则通过onBind方法返回IBinder并返回当前本地服务
localService=((LocalService.LocalBinder)binder).getService();
//这里可以提示用户,或者调用服务的某些方法
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
localService=null;
//这里可以提示用户
}
}, Context.BIND_AUTO_CREATE);
}
在绑定服务的时候,需要一个服务连接对象,ServiceConnection,服务一旦连接,就会调用onServiceConnected方法,我们可以在这个方法里面返回我们的本地服务对象,具体看代码;而在服务断开时候会调用onServiceDisconnected方法,我们可以清理一些服务资源。
[size=large][color=brown]三、ContentProvider[/color][/size]的概念
ContentProvider:为存储和获取数据提供统一的接口。可以在不同的应用程序之间共享数据。Android已经为常见的一些数据提供了默认的ContentProvider
1、ContentProvider使用表的形式来组织数据
无论数据的来源是什么,ContentProvider都会认为是一种表,然后把数据组织成表格
2、ContentProvider提供的方法
query:查询
insert:插入
update:更新
delete:删除
getType:得到数据类型
onCreate:创建数据时调用的回调函数
3、每个ContentProvider都有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。Android所提供的ContentProvider都存放在android.provider包当中
二、ContentProvider的内部原理
自定义一个ContentProvider,来实现内部原理
步骤:
1、定义一个CONTENT_URI常量(里面的字符串必须是唯一)
Public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentprovider");
如果有子表,URI为:
Public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentProvider/users");
2、定义一个类,继承ContentProvider
Public class MyContentProvider extends ContentProvider
3、实现ContentProvider的所有方法(query、insert、update、delete、getType、onCreate)
package com.WangWeiDa.cp;
import java.util.HashMap;
import com.WangWeiDa.cp.MyContentProviderMetaData.UserTableMetaData;
import com.WangWeiDa.data.DatabaseHelp;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
public class MyContentProvider extends ContentProvider {
//访问表的所有列
public static final int INCOMING_USER_COLLECTION = 1;
//访问单独的列
public static final int INCOMING_USER_SINGLE = 2;
//操作URI的类
public static final UriMatcher uriMatcher;
//为UriMatcher添加自定义的URI
static{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user",
INCOMING_USER_COLLECTION);
uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user/#",
INCOMING_USER_SINGLE);
}
private DatabaseHelp dh;
//为数据库表字段起别名
public static HashMap userProjectionMap;
static
{
userProjectionMap = new HashMap();
userProjectionMap.put(UserTableMetaData._ID,UserTableMetaData._ID);
userProjectionMap.put(UserTableMetaData.USER_NAME, UserTableMetaData.USER_NAME);
}
/**
* 删除表数据
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
System.out.println("delete");
//得到一个可写的数据库
SQLiteDatabase db = dh.getWritableDatabase();
//执行删除,得到删除的行数
int count = db.delete(UserTableMetaData.TABLE_NAME, selection, selectionArgs);
return count;
}
/**
* 数据库访问类型
*/
@Override
public String getType(Uri uri) {
System.out.println("getType");
//根据用户请求,得到数据类型
switch (uriMatcher.match(uri)) {
case INCOMING_USER_COLLECTION:
return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE;
case INCOMING_USER_SINGLE:
return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITEM;
default:
throw new IllegalArgumentException("UnKnown URI"+uri);
}
}
/**
* 插入数据
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
//得到一个可写的数据库
SQLiteDatabase db = dh.getWritableDatabase();
//向指定的表插入数据,得到返回的Id
long rowId = db.insert(UserTableMetaData.TABLE_NAME, null, values);
if(rowId > 0){//判断插入是否执行成功
//如果添加成功,利用新添加的Id和
Uri insertedUserUri = ContentUris.withAppendedId(UserTableMetaData.CONTENT_URI, rowId);
//通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(insertedUserUri, null);
return insertedUserUri;
}
return uri;
}
/**
* 创建ContentProvider时调用的回调函数
*/
@Override
public boolean onCreate() {
System.out.println("onCreate");
//得到数据库帮助类
dh = new DatabaseHelp(getContext(),MyContentProviderMetaData.DATABASE_NAME);
return false;
}
/**
* 查询数据库
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
//创建一个执行查询的Sqlite
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
//判断用户请求,查询所有还是单个
switch(uriMatcher.match(uri)){
case INCOMING_USER_COLLECTION:
//设置要查询的表名
qb.setTables(UserTableMetaData.TABLE_NAME);
//设置表字段的别名
qb.setProjectionMap(userProjectionMap);
break;
case INCOMING_USER_SINGLE:
qb.setTables(UserTableMetaData.TABLE_NAME);
qb.setProjectionMap(userProjectionMap);
//追加条件,getPathSegments()得到用户请求的Uri地址截取的数组,get(1)得到去掉地址中/以后的第二个元素
qb.appendWhere(UserTableMetaData._ID + "=" + uri.getPathSegments().get(1));
break;
}
//设置排序
String orderBy;
if(TextUtils.isEmpty(sortOrder)){
orderBy = UserTableMetaData.DEFAULT_SORT_ORDER;
}
else{
orderBy = sortOrder;
}
//得到一个可读的数据库
SQLiteDatabase db = dh.getReadableDatabase();
//执行查询,把输入传入
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
//设置监听
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
/**
* 更新数据库
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
System.out.println("update");
//得到一个可写的数据库
SQLiteDatabase db = dh.getWritableDatabase();
//执行更新语句,得到更新的条数
int count = db.update(UserTableMetaData.TABLE_NAME, values, selection, selectionArgs);
return count;
}
}
4、在AndroidMinifest.xml中进行声明
android:name=".cp.MyContentProvider"
android:authorities="com.WangWeiDa.cp.MyContentProvider"
/>
**为ContentProvider提供一个常量类MyContentProviderMetaData.java
package com.WangWeiDa.cp;
import android.net.Uri;
import android.provider.BaseColumns;
public class MyContentProviderMetaData {
//URI的指定,此处的字符串必须和声明的authorities一致
public static final String AUTHORITIES = "com.wangweida.cp.MyContentProvider";
//数据库名称
public static final String DATABASE_NAME = "myContentProvider.db";
//数据库的版本
public static final int DATABASE_VERSION = 1;
//表名
public static final String USERS_TABLE_NAME = "user";
public static final class UserTableMetaData implements BaseColumns{
//表名
public static final String TABLE_NAME = "user";
//访问该ContentProvider的URI
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/user");
//该ContentProvider所返回的数据类型的定义
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.myprovider.user";
public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.myprovider.user";
//列名
public static final String USER_NAME = "name";
//默认的排序方法
public static final String DEFAULT_SORT_ORDER = "_id desc";
}
}
[color=brown][size=large]四.BroadcastReceiver[/size] [/color]
用接收短信举例:
第一种方式 :
实现
public class MyBroadcastReceiver extends BroadcastReceiver {
// action 名称
String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED" ;
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals( SMS_RECEIVED )) {
// 相关处理 : 地域变换、电量不足、来电来信;
}
}
}
系统注册:在 AndroidManifest.xml 中注册
< receiver android:name = ".MyBroadcastReceiver" >
< intent-filter android:priority = "1000" >
< action android:name = " android.provider.Telephony.SMS_RECEIVED" />
</ intent-filter >
</ receiver > 当然了需要权限 :
< uses-permission android:name = "android.permission.RECEIVE_SMS" />
< uses-permission android:name = "android.permission.SEND_SMS" />
第二种方式:
// 广播接收者 - 广播的接收
private BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 相关处理,如收短信,监听电量变化信息
}
};
代码中注册:
IntentFilter intentFilter = new IntentFilter( "android.provider.Telephony.SMS_RECEIVED " );
registerReceiver( mBatteryInfoReceiver , intentFilter);
生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报错 。
每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 , 该对象即被销毁 . 当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应 . 所以在
BroadcastReceiver 里不能做一些比较耗时的操作 , 否侧会弹出 ANR(Application No
Response) 的对话框 . 。(如图):
怎么用好 BroadcastReceiver ?
如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由 Service 来完成 . 这里不能使用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短 , 子线程可能还没有结束
BroadcastReceiver 就先结束了 .BroadcastReceiver 一旦结束 , 此时 BroadcastReceiver 的
所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 ). 如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的 .
广播类型及广播的收发
广播类型
普通广播 (Normal broadcasts)
发送一个广播,所以监听该广播的广播接收者都可以监听到改广播。
异步广播 , 当处理完之后的Intent ,依然存在,这时候registerReceiver(BroadcastReceiver, IntentFilter) 还能收到他的值,直到你把它去掉 , 不能将处理结果传给下一个接收者 , 无法终止广播 .
有序广播 (Ordered broadcasts)
按照接收者的优先级顺序接收广播 , 优先级别在 intent-filter 中的 priority 中声明 ,-1000 到
1000 之间 , 值越大 , 优先级越高 . 可以终止广播意图的继续传播 . 接收者可以篡改内容 .
广播的收发
该组件接收被广播的 intent,Context 可以通过 sendBroadcast() 和 sendOrderedBroadcast()
方法实现广播的发送 .
首先在需要发送信息的地方 ,把要发送的信息和用于过滤的信息 ( 如 Action 、 Category) 装入一个 Intent 对象 ,然后通过调用 Context.sendBroadcast() 、 sendOrderBroadcast() 或 sendStickyBroadcast() 方法,把 Intent 对象以广播方式发送出去。
使用 sendBroadcast() 或 sendStickyBroadcast() 方法发出去的 Intent ,所有满足条件的 BroadcastReceiver 都会随机地执行其 onReceive() 方法
普通广播的发送和接收:
sendBroadcast(intent);
Intent intent = new Intent( "cn.lenovo.yangguangf " );
sendBroadcast(intent);
priority :这个是 AndroidManifest.xml 中 intent-filter 的参数。
< receiver android:name = ".MyBroadcastReceiver" >
< intent-filter android:priority = "1000" >
< action android:name = "cn.lenovo.yangguangfu" />
</ intent-filter >
</ receiver >
sendOrderedBroadcast(intent, receiverPermission);
1 ,他决定该广播的级别,级别数值是在 -1000 到 1000 之间 , 值越大 , 优先级越高;
2 ,同级别接收是先后是随机的;级别低的收到广播;
3 ,在 android 系统中只要监听该广播的接收者,都能够收到 sendBroadcast(intent) 发出的广播 ;
3 ,不能截断广播的继续传播,
4 ,实验现象,在这个方法发来的广播中,代码注册方式中,收到的广播的先后和注明优先级最高的他们的先后是随机。如果都没有优先级,代码注册收到为最先。