Android开发——Android四大组件

Activity

技巧:当你想查找系统已有的某界面对应的清单文件中等信息时,你可以先打开模拟器, 打开你要查找应用的界面。然后再Logcat中查找相关信息,可能是 info级别的log,可以找到【action-name】的值。例如: Log信息为【ActivityManager(861): Starting: Intent { dat=content://mms-sms/conversations/2 cmp=com.android.mms/.ui.ComposeMessageActivity } from pid 1237】,这条Log清楚标明了短信页面Activity的action-name值。 然后你就可以通过这个类名到源码的清单文件中去查找【data】【category】信息,通过这个类名找到界面的源码所属java类

1.页面跳转

1.1 配置文件中配置的参数解释

	
	
	

	
	
	
		
		
		
	

1.2 隐式意图跳转

意义:开启一个界面,通过指定的一组动作或者数据,主要用来开启别的应用的界面

//隐式意图	开启一个界面
//创建一个意图对象
Intent intent = new Intent();
		
//设置意图的动作,就是Manifest文件中对应要开启Activity的action标签【必须】
intent.setAction("com.heima.result");
		
//对应的Data标签,清单文件中没有【:】,
intent.setData(Uri.parse("heima:"+"nihao"));
		
//对应的catagory标签【必须】
intent.addCategory("android.intent.category.DEFAULT");
startActivity(intent);

1.3 显示意图

意义:一般通过指定的类和包名开启界面,主要用来开启自己应用中的其他界面

//显示意图
Intent intent = new Intent(this,ResultActivity.class);
//intent.setClassName(this, "com.heima.clacperson.ResultActivity");较麻烦

//开启新界面
startActivity(intent);

1.4 小知识



2 Activity类

int BIND_AUTO_CREATE 从Context类继承的字段,作用是作为开启连接服务的连接类型值

Public Methods
void setContentView(int layoutResID)
设置界面中显示的内容通过布局Id
void setContentView(View view)
设置界面中显示的内容通过对象
View findViewById(int id)
通过控件Id查找相应的控件
void startActivity(Intent intent)
开启一个界面
Intent getIntent()
获取开启此界面的意图对象
void startActivityForResult(Intent intent, int requestCode)
开启一个新界面,通过requestCode请求码来标识这个界面, 一般去某界面只是为了获取一个值时用此方
void onActivityResult(int requestCode, int resultCode, Intent data)
这是一个受保护的方法,可以获取某界面的返回值,requestCode:标识不同界面;data:封装了传回本界面的数据
void finish()
关闭当前界面,等同于手机返回键
void finishActivity(int requestCode)
关闭当前界面,等同于手机返回键,不过可以同时关闭requestCode值相同的一类界面
void onCreate(Bundle savedInstanceState)
生命周期方法, 受保护的方法,在activity第一次创建时被调用
void onStart()
生命周期方法,受保护的方法,当activity变成可见时被调用
void onResume()
生命周期方法, 受保护的方法,当activity上的按钮可以被点击时被调用
void onPause()
生命周期方法, 受保护的方法,当activity上的按钮不可以被点击时调用,当有透明应用覆盖时你不能点击按钮却能看见按钮
void onStop()
生命周期方法, 受保护的方法,当avtivity不再可见时被调用
void onDestroy()
生命周期方法, 受保护的方法,当activity被销毁时调用
void onRestart()
生命周期方法,受保护的方法,当activity通过点击手机的Home键而达到不可见的状态,也就是Stop状态,这时不是activity不会被destory。当再次开启软件,这时此方法会被调用,并且接下来回到start方法的时期那里去
int getTaskId()
获取当前任务栈的id,任务栈会给每一个应用分配一个任务栈id
void onBackPressed()
当手机的返回按键被点击时调用
void sendBroadcast(Intent intent)
发送一个无序广播,是从ContextWrapper继承的方法
abstract void sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
发送一个有序广播,是从ContextWrapper继承的方法,参数:意图对象;接受时所需的权限;最终接受者,一定最后运行,不管中间有没有接受者中断广播;处理器;初始码;初始数据;初始化值,经常是null
final void setResult(int resultCode)
设置一个结果码,来标记是从哪个界面的返回的,此结果码被 startActivityForResult方法接受
abstract ComponentName startService(Intent service)
从context类继承的方法,开启一个服务
abstract boolean stopService(Intent service)
从context类继承的方法,关闭一个服务
boolean bindService(Intent service, ServiceConnection conn, int flags)
从context类继承的方法。conn:是开启服务后对服务状态进行监视的连接器,可用这个连接器获取服务中的IBinder对象,从而调用服务对象中的方法;flags:绑定的类型,值在该类成员字段处
void unbindService(ServiceConnection conn)
从context类继承的方法,接触绑定服务。 当调用了多次bindService方法时,会绑定多个不同连接器对象,这是再次调用解绑方法会报异常,因为他不知道解绑哪个连接了。conn:传入已绑定服务的连接器对象
abstract Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
从context类继承的方法,动态注册广播的方法
abstract void unregisterReceiver(BroadcastReceiver receiver)
从context类继承的方法,解绑注册的广播,当Activity中注册了广播时,必须在onDestroy方法中添加这个方法。否则报错
abstract ContentResolver getContentResolver()
从context类继承的方法,获取内容解析者
final boolean requestWindowFeature(int featureId)
通过这个方法,然后传入参数Window.FEATURE_NO_TITLE可清除标题栏
void overridePendingTransition(int enterAnim, int exitAnim)
设置进入下一个Activity,退出本Activity的动画效果, 必须在startActivity(Intent)之后执行
boolean onTouchEvent(MotionEvent event)
Activity触摸监听事件的方法
boolean onKeyDown(int keyCode, KeyEvent event)
当键盘按键被点击时触发方法,参数1:系统会把被点击按钮的key值传入此方法,所以你可以通过与KeyEvnent中的全局常量值判断来使用。如
KeyEvent.KEYCODE_MENU

3.生命周期

特殊:

(1)手机横竖屏切换时就是界面完全关闭再重启的过程。会从新走一遍Activity的生命周期。解决方法: 值可以是【portrait】——竖屏,【landscpe】——横屏。这样设置你横竖屏生命周期方法不会从新执行一遍。不过此属性只在4.0以上系统能用

Android开发——Android四大组件_第1张图片

4.任务栈概念和Activity的四种启动模式

4.1 任务栈

栈:先进后出;队列:先进先出

(1)定义:打开一个界面就会有一个Activity进栈,每按一次返回(关闭)都会清除栈结构顶层的Activity,显示出下一层的

(2)意义:这时用来维护Activity中打开和关闭关系的,也就是维护用户的操作体验

4.2 四种启动模式

在清单文件activity节点下可以配置如下属性 ,四种模式如下解释

(1)standard:默认的启动模式

(2)singleTop:单一顶部模式,如果任务栈栈顶存在这个要开启的Activity,不重新创建Activity,复用过去的。保证栈顶如果存在,则不重复创建。应用场景:浏览器的书签页面就是使用了这个启动模式,用户添加书签时不会因为多次点击更添加了大量重复的书签

(3)singleTask:单一任务栈,在当前任务栈里面只能有一个实例存在。当开启此类型的Activity时,会去检查任务栈里面是否有实例已经存在,如果有实例就复用这个已经存在的实例,并且把这个Activity之上的所有Activity全部清空。这样就可以保证任务栈里面只有一个这个的实例存在。应用场景:游戏实玩界面、浏览器界面(BrowserActivity)就是用的这种启动模式

(4)singleInstance:这种启动模式非常特殊,此类型的Activity会运行自定新开辟的一个任务栈之中,并且这个任务栈之中里面只有一个实例存在。如果你想保证一个Activity在真个手机操作系统里面只有一个实例存在,请使用这种启动模式。应用场景:(InCallScreen)来电话的界面,有道词典,当你在进行任何其他应用,这种模式的Activity会被立刻启动

Intent连接的桥梁

1.Intent类

Public Constructors

Intent
(Context packageContext, Class cls)
通过本类上下文和跳转页面的字节码文件对象进行跳转页面的初始化操作
Constants
String ACTION_CALL 打电话Activity界面的Action值
int FLAG_ACTIVITY_NEW_TASK 设置一个标着,通过setFlags方法传递给目标Activity,表示这个Activity自己维护自己的任务栈,如果任务栈中没有当前任务,就会创建一个任务放入任务栈。文档中有多个Flag值都是让目标Activity执行一些特殊操作的,可以去看一下
Public Methods
Intent putExtras(Intent src)
存储另一个Intent类型的对象
Intent putExtras(Bundle extras)
存储Bundle类型的对象
Intent putExtra(String name, Parcelable[] value)
Add extended data to the intent.
Intent putExtra(String name, Serializable value)
Add extended data to the intent.
Intent putExtra(String name, String value)
除Object类外其他常用类型都可以存储,键值对形式
Bundle getExtras()
获取Bundle类型传递过来的数据
String getStringExtra(String name)
获取字符串类型的数据,通过键获取值
Intent setAction(String action)
设置Action值,来指明 跳转的界面发出的广播的事件、建立的服务等等
Intent setData(Uri data)
设置传递数据并且按照对方定义的规则书写
Intent addCategory(String category)
设置分类值,值为对方定义好的
String getAction()
获取Action值,可以判断是否是自己想要的界面、广播、服务
Intent setType(String type)
设置传递数据的类型,当对应Intent-filter节点中配置了mimeType时你需要调用这个方法设置成相应的值
Intent setFlags(int flags)
告诉接受该Intent的Activity一些标识,通过这些标识这个Activity会修改一定的模式

2.常用隐式意图的跳转

2.1 App安装界面

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");//这是设置文件和类型,file是一个File对象
startActivityForResult(intent, 0);

2.2 App卸载界面

Intent intent = new Intent(this,Intent.ACTION_VIEW);
intent.addCategory(Intent.GATEGORY_DEFAULT);
intent.setData(Uri.parse("package:"+getPackageName());
startActivity(intent);

2.3 App分享的界面

//因为每一个相关应用都有一个Activity界面,action值是ACTION_SEND,你可以通过这个分享功能打开这个Activity
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "开启此Intent将会弹出所有可以接受文本信息应用供你选择,呼叫本地系统可以分享文字的app");
startActivity(intent);

2.4 跳转到桌面

Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);

BroadcastReceiver广播接收者

解释:类似于一个电台,电台可以发送不同的广播事件,类如外拨电话的广播事件,你只要进行了外拨电话这项操作,就会触发这个事件,然后电台会发送一条广播,我们作为这种事件的监听者,也是BroadcastReceiver接受者处理这个事件

特殊:

(1)当你注册了一个广播接受者到系统时,无论当前广播接受者所在线程是否正在运行(所在进程就算被杀死),你只要触发了接受者注册的事件,这个接受者所在线程会被立即启动

(2)在4.0系统中,在设置中对每个应用多了一个【force  stop】的操作,强行停止应用,相当于冻结。这时广播接受者不会被触发

1.BroadcastReceiver抽象类

概述:定义广播接收者要继承该类,同时要配置这个广播接收者,通过静态或者动态的方法

Public Methods
abstract void onReceive(Context context, Intent intent)
当接收到存储广播信息的Intent之后调用,定义广播接收者不需要复写该方法
final String getResultData()
获取当前广播接受的数据。就是广播传递过来的字符串
final void setResultData(String data)
设置广播中的的数据
final void abortBroadcast()
终止广播,不过只能用在有序广播中

1.1 去电广播接受者

public class OutGoingCallReceive extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		//获取到自己要获取的号码
		String data = getResultData();
		
		//获取到我自己设置的ip号码
		SharedPreferences sp = context.getSharedPreferences("config", Context.MODE_PRIVATE);
		String ipNum = sp.getString("number", "");
		
		
		//生成一个新的号
		String newNum = ipNum + data;
		
		//改变拨打的号码
		if(data.startsWith("0")){
			setResultData(newNum);
		}
	}
}

1.2  接受短信广播接受者

public class SmsReceiver extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		
		Object objects[] = (Object[]) intent.getExtras().get("pdus");
		
		for (Object object : objects) {
			SmsMessage sms = SmsMessage.createFromPdu((byte[])object);
			
			String mesData = sms.getMessageBody();//获取短信内容
			String mesSender = sms.getOriginatingAddress();//获取发信人
			
			System.out.println("短信内容:"+mesData+"\t短信发送者:"+mesSender);
		}
	}
}

2.静态配置广播接收者



	
		
		
	

3.动态配置广播接收者

3.1 实现步骤

//注册广播通过代码
receiver = new ScreenReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.SCREEN_OFF");
filter.addAction("android.intent.action.SCREEN_ON");
registerReceiver(receiver, filter);

还需要解除注册

@Override
public void onDestroy() {
	super.onDestroy();
	unregisterReceiver(receiver);
	receiver = null;
}

3.2 动态注册广播的优点

(1)需要时注册,不需要时解除注册

(2)静态注册会因为每次触发事件而重新解析注册注册一遍,相对消耗资源,动态注册只会注册一遍,不用重复注册多遍

3.3 服务中注册广播的好处

(1)Activity中注册广播的局限性:.在Activity中注册广播,如果你退出Activity时,不在onDestory()方法中解除注册的广播,会抛出异常。而你解除了广播,动态广播就不能一直服务下去

(2)在服务中注册广播,由于服务进程不容易被杀死,服务可以长期在后台运行,这样注册的广播也得以长期为你服务

4.系统广播常用事件

(1)外拨电话的事件:android.intent.action.NEW_OUTGOING_CALL

(2)发送短信的事件:android.provider.Telephone.SMS_REERVED

(3)SD卡的状态事件:

     <1>.挂载:android.intent.action.MEDIA_MOUNTED

     <2>.卸载:android.intent.action.MEDIA_UNMOUNTED

     <3>移除:android.intent.action.MEDIA_REMOVED

注意:SD卡的事件静态注册时必须在intent-filter节点下添加 否则广播事件不生效

(4)手机重启事件:android.intent.action.BOOT_COMPLETED

(5)应用事件:

      <1>.应用被卸载事件:android.intent.action.PACKAGE_REMOVED

      <2>.应用被安装事件:android.intent.action.PACKAGE_ADDED

(6)屏幕事件

      <1>.屏幕锁屏事件:android.intent.action.SCREEN_OFF

      <2>.屏幕解屏事件:android.intent.action.SCREEN_ON

(7)电量事件

<1>.电量过低时:Intent.ACTION_BATTERY_LOW

<2>.电池电量充满时:Intent.ACTION_BATTERY_OKAY

(8)手机插入时:Intent.ACTION_HEADSET_PLUG

(9)网络是否断开时:Intent.ACTION_HEADSET_PLUG

Intent官方文档

注意:对于屏幕事件不能使用静态注册广播的形式注册,谷歌禁止。因为该事件是一个非常频繁的操作,每当事件一激活就会注册一遍广播,非常的消耗资源。需要使用动态的方式注册广播

5.自定义广播

5.1 无序广播

实现方法:通过Activity子类对象的sendBroadReceiver(intent)发送一个无序广播,intent对象中封装了事件名,intent对象设置事件名通过setActiion方法

5.2 有序广播

实现方法:通过Activity子类对象的sendOrderedBroadcast()方法发送一个有序广播,intent对象中封装了事件名,intent对象设置事件名通过setAction方法。然后接受者通过在Intent-filter节点下配置属性然后接受者们就会按照权限值大小的顺序接受广播。

Intent intent = new Intent();
intent.setAction("com.heima.sendReceiver");
intent.putExtra("content", "第一次作为自定义广播发送者");
		
//发送一个无序广播
sendBroadcast(intent);

Intent intent = new Intent();
intent.setAction("com.heima.sendrice");
/**
 * intent 意图,封装数据和标识
* receiverPermission 接受的权限,不需要权限则为null
* resultReceiver 是最终的接受者
* scheduler handler 处理器
* initialCode 初始化码
* initialData 初始化的数据
* 
*///发送一条有序广播
sendOrderedBroadcast(intent, null, null, null, 10, "国务院给每一个人民发送了1000斤大米", null);

Service服务

了解:当一个应用将要开启,任何组件还没加载,Linux系统就会开启一个进程其中包含一个线程,之后默认应用的所有组件都运行在这个进程中的线程中,这个线程就是主线程或叫UI线程

重点:服务可以理解为没有界面的Activity

1.进程的生命周期( 进程的优先级)

注意:系统会按照优先级的大小,在内存不够时杀死优先级比较低的线程

1.1 Foreground  process( 前台进程)

(1)用户正在与Activity交互,onResume()方法正在执行的进程

(2)广播接受者被触发,onReceive()方法正在执行

1.2 Visible process( 可视进程)

(1)Activity一直可以看见却不可以交互,Activity执行了onPause()方法

1.3 Service  process( 服务进程)

(1)当一个服务被开启时

1.4 Background  process( 后台进程)

(1)相当于Activity执行了onStop()方法,不可见了

1.5 Empty  process( 空进程)

(1)当Activity执行了onDestory()方法,界面已经不存在,该线程不会被摧毁,还保持着这个线程存活,为了加快下一次启动该应用的时间,起到缓存的作用。系统经常杀死这个进程,

2.Service抽象类

疑问:线程和服务的区别?

解决:你从新开启一个线程用Thread实现,当Activity再可交互时这个线程优先级变成空进程级别,很容易被杀死。而你开启一个服务,让Activity不在进行交互时,这个进程会变成服务进程,能保证这个进程在在后台长期执行

Public Methods
void onCreate()
服务只会被开启一次,多次通过strartService()开启服务时将会直接执行onStartCommand()方法,服务会在后台一直运行,除非用户手工停止
int onStartCommand(Intent intent, int flags, int startId)
当服务运行时被执行,可以运行多次
void onDestroy()
服务被停止时调用,用户手工停止
abstract IBinder onBind(Intent intent)
通过bindService()开启服务是会调用这个方法。 需要把自己自定义的方法提供出去时需要复写这个方法,返回自己的中间人对象
final void stopSelf()
停止自己这个服务,关闭自己
abstract Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
从context类继承的方法,动态注册广播的方法
abstract void unregisterReceiver(BroadcastReceiver receiver)
从context类继承的方法,解绑注册的广播,当Activity中注册了广播时,必须在onDestroy方法中添加这个方法。否则报错
abstract ComponentName startService(Intent service)
从context类继承的方法,开启一个服务
abstract boolean stopService(Intent service)
从context类继承的方法,关闭一个服务
boolean bindService(Intent service, ServiceConnection conn, int flags)
从context类继承的方法。conn:是开启服务后对服务状态进行监视的连接器,可用这个连接器获取服务中的IBinder对象,从而调用服务对象中的方法;flags:绑定的类型,值在该类成员字段处
void unbindService(ServiceConnection conn)
从context类继承的方法,接触绑定服务。 当调用了多次bindService方法时,会绑定多个不同连接器对象,这是再次调用解绑方法会报异常,因为他不知道解绑哪个连接了。conn:传入已绑定服务的连接器对象

3.两种开启服务方式的对比

(1)startService()开启服务这种方法,会执行服务生命周期方法的onCreat()和onStartCommand()方法,并且是开启多少次调用多少次onStartCommand()方法。这种开启方式服务会成为服务线程,不手工停止会一直在后台执行

(2)bindService()开启服务这种方法,会执行服务生命周期方法的onCreat()和onBind()方法,你绑定多少次只会调用一次onBind()方法。这种开启方式,服务的生命周期很短,与开启服务的Activity同生共死。就是说只要开启服务的Activity一onDestroy系统就会解除服务的绑定,如果你不手动的解除绑定,会报出一些错误,不过不影响开发

(3)混合模式:当你既需要开启长期运行的服务,又需要调用服务中自定义的方法时,使用这种模式。谷歌推荐的开启流程——1.startService()==>2.bindService()==>3.unbindService()==>4.stopService()用处:开发音乐播放器、股票、天气预报、闹钟等,需要在后台长时间开启,并且需要提供一些自己私有的功能:如暂停、播放等

4.进程间通信(ipc)

4.1 名词解释

解释:ipc——inter  process  communication:进程间通信

申明:当每个应用部署到手机上,这样每个应用都会对应有自己的一个进程,这样进程间通信就是应用间通信

远程服务:运行在其他应用里面的服务

本地服务:运行在自己应用里面的服务

aild:Android  interface  defintion  language——Android接口定义语言

4.2  实现步骤

目的:保证远程服务里面的IBinder对象与本地应用里面的IBinder对象是同一个对象

(1)首先对远程服务里面为了把私有IBinder对象提供出去而建立的接口进行操作,把这个文件的【.java】扩展名改为【.aidl】,并且把该接口中的类和方法的public修饰符去掉(因为接口默认就是共有的,所以aidl不识别public字样)这样就会在远程服务应用中的gen目录中自动生成一个【接口名.java】的文件

(2)然后让远程服务里面的自定义的中间人类继承Stub(Stub类在gen目录自动生成的java文件中定义)

(3)之后在本地应用中新建一个包,包名与远程服务中aidl文件所在包的名称相同,然后把远程服务中的aidl文件拷贝到本地应用新建的这个包中即可(这样Android就会认为这两个应用使用的是同一个aidl文件,并且在本地应用的gen目录中同样会生成一个【接口名.java】的文件,其中包含Stub类)

(4)重要,这时在本地服务你想要开启远程的服务,就要建立Intent对象,Intent对象的参数怎么写

(5)解答:在远程服务的清单文件中配置服务的节点,其中配,然后在本地应用中建立Intent对象,之后setAction(),参数值为远程服务中配置的action值

(6)最后建立ServiceConnection实现类 ,复写方法时这样写==>接口名.Stub.asInterface(service);

ContentProvider内容提供者

意义:主要是让其他应用读取自己的私有数据库

知识点:私有的数据库其他应用是无法读取的,你可以用Linux的指令修改数据库文件的权限【adb shell】==>进入到数据库所在目录==>【chmod 777 数据库名.db】

1. ContentProvider抽象类

意义:自定义的内容提供者要继承这个类,复写她的抽象方法

Public Methods
abstract boolean onCreate()
内容提供者创建时调用
abstract String getType(Uri uri)
Implement this to handle requests for the MIME type of the data at the given URI.
abstract Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
提供出去的查询方法,返回游标
abstract Uri insert(Uri uri, ContentValues values)
提供出去的添加方法,返回的Uri.parse("主机名"+插入行的序号);
abstract int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
提供出去的更新方法,返回更新了多少行
abstract int delete(Uri uri, String selection, String[] selectionArgs)
提供出去的删除方法,删除了多少行

2.UriMather类

Constants
int NO_MATCH 状态码:没有连接
Public Constructors
UriMatcher(int code)
定义匹配规则对象
Public Methods
void addURI(String authority, String path, int code)
添加匹配规则,authority:主机名,通过主机名可以查找到暴露出去的方法,path:路径,code:匹配码。前两个参数是给解析者中的,最后一个参数是给提供者用的
int match(Uri uri)
解析匹配路径通过定义匹配规则,符合谁的匹配规则就返回谁的匹配码,谁都不符合返回成员字段NO_MATCH

public class FirstContentProvider extends ContentProvider {

	//当匹配任何主机时使用默认匹配值
	private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
	private static final int QUERYSUCESS = 1;
	private static final int UPDATESUCESS = 2;
	private static final int DELETESUCESS = 3;
	private static final int ADDESUCESS = 4;
	private MyDatabase database;
	
	static{
		matcher.addURI("com.heima.firstprovider","query",QUERYSUCESS);
		matcher.addURI("com.heima.firstprovider", "update", UPDATESUCESS);
		matcher.addURI("com.heima.firstprovider", "delete", DELETESUCESS);
		matcher.addURI("com.heima.firstprovider", "add", ADDESUCESS);
	}

	@Override
	public boolean onCreate() {
		database = new MyDatabase(getContext());
		return false;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		int match = matcher.match(uri);
		if(match == QUERYSUCESS){
			SQLiteDatabase sdb = database.getReadableDatabase();
			return sdb.query("info", projection, selection, selectionArgs, null, null, null);
		}else {
			throw new IllegalArgumentException("路径不匹配,请检查");
		}
	}


	@Override
	public Uri insert(Uri uri, ContentValues values) {
		int match = matcher.match(uri);
		if(match == ADDESUCESS){
			SQLiteDatabase sdb = database.getReadableDatabase();
			//标示插入多少行
			long insert = sdb.insert("info", null, values);
			return Uri.parse("com.heima.firstprovider"+"_"+insert);
		}else {
			throw new IllegalArgumentException("路径不匹配,请检查");
		}
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		int match = matcher.match(uri);
		if(match == DELETESUCESS){
			SQLiteDatabase sdb = database.getReadableDatabase();
			return sdb.delete("info", selection, selectionArgs);
		}else {
			throw new IllegalArgumentException("路径不匹配,请检查");
		}
		
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		int match = matcher.match(uri);
		if(match == UPDATESUCESS){
			SQLiteDatabase sdb = database.getReadableDatabase();
			return sdb.update("info", values, selection, selectionArgs);
		}else {
			throw new IllegalArgumentException("路径不匹配,请检查");
		}
	}

	@Override
	public String getType(Uri uri) {
		return null;
	}
}

3.配置清单文件


4.ContentResolver抽象类

意义:内容解析者,通过上下文对象的方法获取该对象

Public Methods
final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

就是内容提供者暴露的查询方法

final Uri insert(Uri url, ContentValues values)
就是内容提供者暴露的添加方法
final int delete(Uri url, String where, String[] selectionArgs)
就是内容提供者暴露的删除方法
final int update(Uri uri, ContentValues values, String where, String[] selectionArgs)
就是内容提供者暴露的更新方法
final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
注册一个内容观察者,uri:就是内容提供者定义的uri;notifyForDescendents:true——发送给该主机名下所有path,false——只发送给uri指定的path;observer:注册的观察者对象
final void unregisterContentObserver(ContentObserver observer)
解除注册的内容内容观察者
void notifyChange(Uri uri, ContentObserver observer)
提醒观察此uri的观察者自己的数据改变了,写内容提供者的逻辑时需要使用这个方法.observer:可以传入null,表示只要观察了这个uri的观察者都会接受到这个提醒

5.ContentObserver抽象类

注:当其他应用中的私有数据库一有改变我们就能察觉,可以使用谷歌定义的API————内容观察者

Public Methods
void onChange(boolean selfChange)
观察的数据改变时调用

6.常用数据库查询技巧

(1)短信数据库的提供者:TelephonyProvider-->SmsProvider-->主机名:sms;对sms短信数据表进行增删改查操作path:null,表示全部操作都可以

(2)联系人数据库提供者:ContactsProvider-->ContactProvider2-->主机名:com.android.contactspath:raw_contacts——查询raw_contacts表的path值,path:data——查询data表的path值。(Contact的提供者path值表示的是操作的表名,可以对这个表进行增删改查)

     <1>.data表==>通过raw_contact_id字段值来判断是哪个联系人的数据;通过memetype_id字段值来判断数据的类型是那种,这两个都是外键;data1字段存储数据

     <2>.raw_contacts表==>这里记录所有联系人的contact_id,这个字段关联的是data中的raw_contact_id

     <3>.mimetypes表==>这里记录了所有数据类型的mimetype,这个字段关联的是data中的mimetype_id

注意:a.对data表和mimetypes进行多表查询是不能的,因为这个数据库已经定义好了一个data_view视图,把这两张表合并成了一个大表供我们查找,视图中含有data1数据字段和mimytype类型值字段

             b.当你在手机中删除了某一个联系人后,实际上只是对联系人数据库中的raw_contacts表中的相应记录的contact_id列的值制为了空,没有把data表中数据删除。所以这时当你查询raw_contacts表时一定要判断contact_id是否为空,再获取,否则会报数据库语句解析异常

6.1 联系人查询的实例代码

public static List readContact(Context context) {
		List contactsList = new ArrayList();
		Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
		//查询raw_contacts表获取contact_id
		Cursor idCursor = context.getContentResolver().query(uri,
				new String[] { "contact_id" }, null, null, null);
		while (idCursor.moveToNext()) {
			Contact contact = new Contact();
			String id = idCursor.getString(0);
			contact.setId(id);
			System.out.println(id);

			uri = Uri.parse("content://com.android.contacts/data");
			//通过contact_id获取联系人数据,这里查阅的是data_view视图
			Cursor dataCursor = context.getContentResolver().query(uri,
					new String[] { "mimetype", "data1" }, "raw_contact_id=?",
					new String[] { id }, null);

			while (dataCursor.moveToNext()) {
				String type = dataCursor.getString(0);
				if ("vnd.android.cursor.item/email_v2".equals(type)) {
					String email = dataCursor.getString(1);
					contact.setEmail(email);
				} else if ("vnd.android.cursor.item/phone_v2".equals(type)) {
					String phone = dataCursor.getString(1);
					contact.setPhone(phone);
				} else if ("vnd.android.cursor.item/name".equals(type)) {
					String name = dataCursor.getString(1);
					contact.setName(name);
				}
			}
			contactsList.add(contact);
		}
		return contactsList;
	}

你可能感兴趣的:(Android)