当前位置: 首页 > news >正文

网站做内嵌长沙近期大型招聘会

网站做内嵌,长沙近期大型招聘会,定制商品的网站建设,搭建网站平台Android 之四大组件、六大布局、五大存储#xff1a;https://blog.csdn.net/shenggaofei/article/details/52450668 Android 四大组件、五大存储、六大布局#xff1a;https://blog.csdn.net/xiankog/article/details/81702119 Android四大基本组件介绍与生命周期#xff…  Android 之四大组件、六大布局、五大存储https://blog.csdn.net/shenggaofei/article/details/52450668 Android 四大组件、五大存储、六大布局https://blog.csdn.net/xiankog/article/details/81702119 Android四大基本组件介绍与生命周期https://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html 转自Android开发入门与实践 一、Android 四大组件详解 Android 四大核心组件指的是 Activity、Service、Content Provider、BroadCast Receiver核心组件都是由 Android 系统进行管理和维护的一般都要在清单文件中进行注册或者在代码中动态注册。 活动activity用于表现功能服务service后台运行服务不提供界面呈现广播接受者Broadcast Receive用于接收广播内容提供者Content Provider支持多个应用中存储和读取数据。1.1 activity 1、概念 在 android 中Activity 相当于一个页面可以在 Activity 中添加 Button、CheckBox 等控件一个 android 程序有多个 Activity 组成。 1一个 Activity 通常就是一个单独的屏幕窗口。2Activity 之间通过 Intent 进行通信。3Android 应用中每一个 Activity 都必须要在 AndroidManifest.xml 配置文件中声明否则系统将不识别也不执行该Activity。在 android stdio会自动生成但 eclipse 需要自己手动添加 定义与作用 Activity 的中文意思是 活动代表手机屏幕的一屏或是平板电脑中的一个窗口提供了和用户交互的可视化界面。一个活动开始代表 Activity 组件启动活动 结束代表一个 Activity 的生命周期结束。一个 Android 应用必须通过 Activity 来 运行 和 启动Activity 的生命周期交给系统统一管理。Activity 是用于处理 UI 相关业务的比如加载界面、监听用户操作事件。 Activity 之间通过 Intent 进行通信直接通过 Bundle 对象来传递 在Intent 的描述结构中有两个最重要的部分动作和动作对应的数据。 典型的动作类型有MAINactivity 的门户、VIEW、PICK、EDIT 等。而动作对应的数据则以 URI 的形式进行表示。 例如要查看一个人的联系方式你需要创建一个动作类型为VIEW 的intent以及一个表示这个人的URI。 与之有关系的一个类叫 IntentFilter。相对于 intent 是一个有效的做某事的请求一个 intentfilter 则用于描述一个 activity或者IntentReceiver能够操作哪些 intent。一个 activity 如果要显示一个人的联系方式时需要声明一个 IntentFilter这个IntentFilter 要知道怎么去处理 VIEW 动作和表示一个人的 URI。IntentFilter 需要在 AndroidManifest.xml 中定义。通过解析各种intent从一个屏幕导航到另一个屏幕是很简单的。当向前导航时activity 将会调用 startActivity(Intent myIntent) 方法。然后系统会在所有安装的应用程序中定义的 IntentFilter 中查找找到最匹配 myIntent 的 Intent 对应的 activity。新的 activity 接收到myIntent 的通知后开始运行。当 startActivity 方法被调用将触发解析 myIntent 的动作这个机制提供了两个关键好处 Activities 能够重复利用从其它组件中以 Intent 的形式产生的一个请求Activities 可以在任何时候被一个具有相同 IntentFilter 的新的 Activity 取代。 AndroidManifest 文件中含有如下过滤器的 Activity 组件为默认启动类当程序启动时系统自动调用它 intent-filteraction android:nameandroid.intent.action.MAIN /category android:nameandroid.intent.category.LAUNCHER / /intent-filter 2、生命周期 生命周期 生命周期指的是 Activity 从 创建 到 销毁 所执行的一系列方法主要包括7个生命周期方法。 生命周期onCreate() - onStart() -  onResume() - onPause() - onStop() - onDestroy() 详细流程如下图 4 个重要状态 Resumed一个新 Activity 启动入栈后它在屏幕最前端处于栈的最顶端此时它处于可见并可和用户交互的激活状态。Paused一个活动不再处于栈顶位置但仍然可见时这时活动就进入了暂停状态比如对话框形式的活动只会占用屏幕中间的部分区域你还可以看到后边的界面这时后面的活动就处于暂停状态。 意思就是当 Activity 被另一个透明 或者 Dialog 样式的 Activity 覆盖时的状态。此时它依旧与窗口管理器保持连接系统继续维护其内部状态所以它依然可见但它己经失去了焦点故不可与用户交互。 Stopped当一个活动不在处于栈顶位置并且完全处于不可见的时候就进入了停止状态就是进入了一个完全不透明的活动上个活动会处于停止状态这时系统仍然会为这种活动保存相应的状态和成员变量但是这并不是完全可靠的当其他地方需要内存时处于停止状态的活动有可能会被系统回收。即当 Activity 被另一个 Activity 覆盖、失去焦点并不可见时处于 Stopped 状态Destroy 销毁状态当一个活动从返回栈中移除后就变成了销毁状态系统倾向于回收处于这种状态的活动从而保证手机内存充足 摘自《Android从入门到精通》 七大方法详解 onCreateActivity 创建时第一个被调用的方法通常在该方法中加载布局文件初始化UI组件事件注册等等onStart在 onCreate 方法之后调用用于显示界面但用户还不能进行交互 这个方法在活动由不可见变为可见的时候调用 onRestart当一个 stoped 状态的 Activity 被返回时调用之后再调用 onStart 进入运行状态。 这个方法在活动由停止状态变为运行状态之前调用也就是活动被重新启动了。 onResume在 onStart 之后调用该方法执行完成后用户可以进行交互当前 Activity 进入 resumed 状态。当一个 paused 状态的 activity 被重新返回时会再次调用该方法让 Activity 进入运行状态。 活动准备好和用户进行交互时调用此时的活动一定位于返回栈的栈顶并且处于运行状态 onPause当其它 Activity (透明或窗口模式)进入时该方法会被调用让当前 Activity 进入 paused 状态暂停状态。当前 Activity 还可见但不可交互如果其它更高优先级的 APP 需要内存时当前 Activity 可能会被销毁kill。当前 Activity 被返回时会调用 onResume 方法。 在系统准备去启动或者恢复另一个活动的时候调用onStop当其它 Activity 覆盖该 Activity 时会被调用当前 Activity 进入 stoped 状态停止状态。不可见如果其它更高优先级的 APP 需要内存时当前 Activity 可能会被销毁kill。 当前 Activity 被返回时会调用 onRestart 方法。 这个方法在活动完全不可见的时候调用 onDestroy当前 Activity 被销毁时调用通常在该方法中用来释放资源当前 Activity killed。 这个方法在活动被销毁之前调用之后活动的状态将变为销毁状态 以上七个方法除了onRestart()方法其他都是两两相对的从而又可以将活动分为三种生存期 一、完整生存期活动在onCreate()方法和onDestroy()方法之间所经历的就是完整生存期二、可见生存期活动在onStart()方法和onStop()之间所经历的在可见生命期内活动对于用户总是可见的即便有可能无法和用户进行交互。三、前台生存期活动在onResume()方法和onPause()方法之间所经历的这个生存期内活动总是处于运行状态此时的活动是可以和用户进行相互的我们平时看到和接触最多的也是这个状态下的活动。菜鸟Android开发系列之Active的生命周期https://www.iteye.com/blog/limdengrock-1023926 android Activity生命周期详解图文https://www.iteye.com/blog/104zz-1685753 安卓活动的生命周期https://blog.csdn.net/QingKing520/article/details/73496618 安卓活动生命周期https://blog.csdn.net/techdesign/article/details/80456706 活动生命周期示意图 注当 AActivity 切换 BActivity 的所执行的方法 AActivity: onCreate()-onStart()-onResume()-onPouse() BActivity: onCreate()-onStart()-onResume() AActivity: onStop()-onDestory() 当 AActivity 切换 BActivity此 activity 是以 dialog 形式存在的所执行的方法: AActivity: onCreate()-onStart()-onResume()-onPouse() BActivity: onCreate()-onStart()-onResume() 另一个图 启动activity系统先调用onCreate()然后调用onStart()最后调用onResume()方法activity进入运行状态。 activity被其他activity覆盖其上DialogActivity或者锁屏系统会调用onPause()方法暂停当前activity的执行。 当前activity由被覆盖状态回到前台或者解锁屏系统会调用onResume()方法再次进入运行状态。 当前Activity转到新的Activity界面或按Home键回到主屏自身退居后台系统会先调用onPause方法然后调用onStop方法进入停滞状态。 用户后退回到此Activity系统会先调用onRestart方法然后调用onStart方法最后调用onResume方法再次进入运行状态。 当前Activity处于被覆盖状态或者后台不可见状态即第2步和第4步系统内存不足杀死当前Activity而后用户退回当前Activity再次调用onCreate方法、onStart方法、onResume方法进入运行状态。 用户退出当前Activity系统先调用onPause方法然后调用onStop方法最后调用onDestory方法结束当前Activity。 onRestart()表示activity正在重新启动 一般情况下当前activity从不可见重新变成可见状态时onRestart()就会被调用这种情形一般是用户行为所导致的比如用户按HOME键切换到桌面然后重新打开APP或者按back键。 onStart()activity可见了但是还没有出现在前台还无法和用户交互。 onPause()表示activity正在停止此时可以做一些存储数据停止动画等工作注意不能太耗时因为这会影响到新activity的显示onPause必须先执行完新的activity的onResume才会执行。 从activity是否可见来说onstart()和onStop()是配对的从activity是否在前台来说onResume()和onPause()是配对的。 旧 activity 先 onPause然后 新 activity 在启动 注意当 activity 中弹出 dialog 对话框的时候activity不会回调onPause。然而当activity启动dialog风格的activity的时候此activity会回调onPause函数。 异常情况下的生命周期比如当系统资源配置发生改变以及系统内存不足时activity就可能被杀死。 情况1资源相关的系统配置发生改变导致 activity 被杀死并重新创建。比如说当前 activity 处于竖屏状态如果突然旋转屏幕由于系统配置发生了改变在默认情况下activity 就会被销毁并且重新创建当然我们也可以组织系统重新创建我们的 activity。系统配置发生改变以后activity 会销毁其 onPauseonStoponDestory 均会被调用由于 activity 是在异常情况下终止的系统会调用 onSaveInstance 来保存当前 activity 状态这个方法的调用时机是在 onStop之前。与 onPause 没有既定的时序关系当 activity 重新创建后系统会调用 onRestoreInstanceState并且把 activity 销毁时onSaveInstanceState 方法保存的 Bundle 对象作为参数同时传递给 onRestoreInstanceState 和 onCreate 方法。onRestoreInstanceState() onStart() 方法后回调。同时在onSaveInstanceState和onRestoreInstanceState方法中系统自动为我们做了一些恢复工作如文本框EditeText中用户输入的数据ListView滚动的位置等这些 view相关的状态系统都能够默认为我们恢复。可以查看view源码和activity一样每个view都有onSaveInstanceState方法和onRestoreInstanceState方法。 生命周期日志打印 04-11 09:44:57.350 11757-11757/cn.hotwoo.play:remote I/MainActivity: onCreate 04-11 09:44:57.354 11757-11757/cn.hotwoo.play:remote I/MainActivity: onStart 04-11 09:44:57.356 11757-11757/cn.hotwoo.play:remote I/MainActivity: onResume 04-11 09:44:57.425 11757-11757/cn.hotwoo.play:remote I/MainActivity: onCreateOptionsMenu 04-11 09:44:59.149 11757-11757/cn.hotwoo.play:remote I/MainActivity: onPause 04-11 09:44:59.151 11757-11757/cn.hotwoo.play:remote I/MainActivity: onSaveInstanceState 04-11 09:44:59.151 11757-11757/cn.hotwoo.play:remote I/MainActivity: onStop 04-11 09:44:59.151 11757-11757/cn.hotwoo.play:remote I/MainActivity: onDestroy 04-11 09:44:59.234 11757-11757/cn.hotwoo.play:remote I/MainActivity: onCreate 04-11 09:44:59.235 11757-11757/cn.hotwoo.play:remote I/MainActivity: onStart 04-11 09:44:59.236 11757-11757/cn.hotwoo.play:remote I/MainActivity: onRestoreInstanceState 04-11 09:44:59.237 11757-11757/cn.hotwoo.play:remote I/MainActivity: onResume 04-11 09:44:59.270 11757-11757/cn.hotwoo.play:remote I/MainActivity: onCreateOptionsMenu 04-11 10:02:32.320 11757-11757/cn.hotwoo.play:remote I/MainActivity: onPause 04-11 10:02:32.516 11757-11757/cn.hotwoo.play:remote I/MainActivity: onStop 04-11 10:02:32.516 11757-11757/cn.hotwoo.play:remote I/MainActivity: onDestroy 情况2资源内存不足导致低优先级的activity被杀死 这里的情况和前面的情况1数据存储和恢复是完全一致的activity按照优先级从高到低可以分为如下三种 1前台activity---正在和用户交互的activity优先级最高 2可见但非前台activity---比如activity中弹出了一个对话框导致activity可见但是位于后台无法和用户直接交互。 3后台activity---已经被暂停的activity比如执行了onStop优先级最低。 防止重新创建activityactivity 指定 configChange 属性来不让系统重新创建 activity。android : configChanges orientation Activity 活动  与 Fragment ( 碎片 ) 生命周期关系 创建过程 销毁过程 Activity 与 menu 创建先后顺序 在 activity 创建完回调 onResume 后创建 menu回调 onCreateOptionsMenu 04-05 00:35:03.452 2292-2292/cn.hotwoo.play:remote I/MainActivity: onCreate 04-05 00:35:03.453 2292-2292/cn.hotwoo.play:remote I/MainActivity: onStart 04-05 00:35:03.454 2292-2292/cn.hotwoo.play:remote I/MainActivity: onResume 04-05 00:35:03.482 2292-2292/cn.hotwoo.play:remote I/MainActivity: onCreateOptionsMenu 横竖屏切换时 Activity 的生命周期 此时的生命周期跟清单文件里的配置有关系。 ① 不设置 Activity 的 android:configChanges 时横竖屏切换会重新调用各个生命周期销毁当前 activity然后重新加载跟系统配置有关。② onSaveInstanceState方法会在当前页面销毁前被调用存储数据onRestoreInstanceState方法会被执行去取出保存的Bundle对象中的内容进行一次横竖屏切换时Activity所执行的生命周期方法以及在onSaveInstanceState与onRestoreInstanceState打印相应日志创建 与 配置 创建 一个 Activity 需继承自 android.app.Activity 这个类然后重写 onCreate()在 onCreate() 里面调用 setContentView(参数) 来加载布局参数就是布局文件。配置 则需要在清单文件的 Application 节点下面注册 Actvitiy如果要首先启动该Activity则添加带有category节点且值为LAUNCHER 的 intent-filter 节点下面就是清单文件的配置。applicationandroid:allowBackuptrueandroid:icondrawable/ic_launcherandroid:labelstring/app_nameandroid:themestyle/AppTheme activityandroid:name.MainActivityandroid:labelstring/app_name intent-filteraction android:nameandroid.intent.action.MAIN /category android:nameandroid.intent.category.LAUNCHER //intent-filter/activity/application 3、 四种启动模式 Activity 的启动模式决定了激活 Activity 时是否创建新的对象进而将影响到 任务栈 也叫 回退栈。 在 AndroidManifest.xml 文件中可以为每个 activity 节点配置 android:launchMode 属性以决定该 Activity 的启动模式该属性的值有 Standard 模式 : ( 默认 ) 标准模式每次激活 Activity 时都会创建新的 Activity 对象。standard 模式是 android 的默认启动模式在这种模式下activity 可以有多个实例每次启动 Activity无论任务栈中是否已经存在这个 activity 的实例系统都会创建一个新的 activity 实例。即 在这种模式下activity默认会进入启动它的activity所属的任务栈中。 注意在非activity类型的context如ApplicationContext并没有所谓的任务栈所以不能通过ApplicationContext去启动standard模式的activity。SingleTop 模式 栈顶模式也叫栈顶复用模式。当一个 singleTop 模式的 activity 已经位于栈顶位置时再去启动它时不会再创建实例即每次只是激活但并不会创建新的 Activity 对象如果不在栈顶就会创建实例。 如果新activity位于任务栈的栈顶的时候activity不会被重新创建同时它的onNewIntent方法会被回调。 注意这个activity的onCreateonStartonResume不会被回调因为他们并没有发生改变。 SingleTask 模式 单任务模式也叫栈内复用模式。如果启动的这个 activity 已经存在于 任务栈 中则会将该 activity 移动到栈顶并将该 activity 上面的所有 activity 出栈否则创建新的实例。 只要activity在一个栈中存在那么多次启动此activity不会被重新创建单例系统会回调onNewIntent。比如activityA系统首先会寻找是否存在A想要的任务栈如果没有则创建一个新的任务栈然后把activityA压入栈如果存在任务栈然后再看看有没有activityA的实例如果实例存在那么就会把A调到栈顶并调用它的onNewIntent方法如果不存在则把它压入栈。 SingleInstance 模式 单实例模式一个 activity 一个栈即 activity只能单独地位于一个任务栈 中。 实例(对象)唯一确保该 Activity 的对象一定只有1个被设置为 singleInstance 的 Activity 将被置于一个专门的任务栈中且该任务栈中有且仅有一个 Activity。 注意默认情况下所有 activity 所需的 任务栈 的名字为应用的包名可以通过给 activity 指定 TaskAffinity 属性来指定任务栈***** 这个属性值不能和包名相同否则就没有意义 *****。 什么 是 任务栈回退栈 任务栈 是 用来存放所有激活了的 Activity 对象激活的 Acitvity 将会按照后进先出的栈结构显示出来。因为屏幕只能显示一个Activity当有新的 Activity 被激活时原来正在显示的 Activity 就会进行压栈操作被压到新 Activity 对象下方的位置。当按下”Back” 键时栈顶 Activity 会执行弹栈操作而在第 2 位的 Activity 将获得栈顶位置显示在前台。 4、三种跳转方式 显示启动 Intrent 内部直接声明要启动的activity所对应的的class Intent intent new Intent(MainActivity.this, SecondActivity.class); startActivity(intnet); 隐式启动进行三个匹配一个是activity一个是category一个是data全部或者部分匹配应用于广播原理 清单文件中 里配置 activity 属性activity 的名字要和跳转内容一样 activity android:namecom.exanple.android.tst.secondActivityandroid:label string/titleintentfilteraction android:namecom.exanple.android.tst.secondActivity/category android:nameandroid.intent.category.DEFAULT/intent-filter/ /activity 在需要跳转的地方 Intent intent new Intent(com.example.android.tst.secondActivity); startActivity(intnet);跳转后再返回能获取返回值 Intent in new Intent(MainActivity.this,OtehrActivity.class); in.putExtra(a,a); startActivityForResult(in,1000);在OTherActivity中设置返回值 Intent int new Intent(); int.putExtra(c,c); setResult(1001,int); finish();在MainActivity中获取返回值 Override protected void onActivityResult(int requestCode, int resultCode ,Intent data) {super.onActivityResult(requestCode,resultCode,data);if(requestCode 1000){if(resultCode 1001){int c data.getExtra(c,0);}} }Intent 与 IntentFilter Intent —— 意图 用于 android 个组件的启动和组件间传递数据 属性: component  目标组件描述action  对Intent执行动作的描述data  对此次Intent操作相关数据的描述type  对Intent所关联的数据类型的描述category  对Intent执行动作的附加信息描述extra  对其他一切附加信息的描述他是对其他所有附加信息的集合Intent-Filter —— 意图 过滤器 对 Intent 的描述进行过滤操作对 Intent 的各个属性进行匹配从而选择出相应的组件来执行 Intent 想要进行的操作 定义 Intent-Filteraction name“xxxxxxxxx”/action name“yyyyyyyy”/category name“ttttttttttt”/category name“uuuuuuu”/datadata /Intent-Filter 在 IntentFilter 中 action、category、data 都可以存在多个 匹配原则 actionIntent 中的 action 只要和 IntentFilter 中的任意一个 action 一样即可categoryIntent 中添加的 category 必须全部在 IntentFileter 定义的 category 中dataIntent 中的 data 要和 IntentFilter 中的 data 的描述匹配typeIntent 中的 type 要和 IntentFilter 中 data 要求的 mimeType 一直 前台进程: 1.当前进程activity正在与用户进行交互。2.当前进程service正在与activity进行交互或者当前service调用了startForground()属于前台进程或者当前service正在执行生命周期onCreate(),onStart(),onDestory()3.进程持有一个BroadcostReceiver,这个BroadcostReceiver正在执行onReceive()方法 可见进程: 1. 进程持有一个activity这个activity不再前台处于onPouse()状态下当前覆盖的activity是以dialog形式存在的。2. 进程有一个service这个service和一个可见的Activity进行绑定。 service进程: 当前开启startSerice()启动一个service服务就可以认为进程是一个服务进程。 后台进程: activity的onStop()被调用但是onDestroy()没有调用的状态。该进程属于后台进程。 空进程: 改进程没有任何运行的数据了且保留在内存空间并没有被系统killed,属于空进程。该进程很容易被杀死。1.2 service service服务是安卓中的四大组件之一它通常用作在后台处理耗时的逻辑与 Activity 一样它存在自己的生命周期也需要在 AndroidManifest.xml 配置相关信息。 开发人员需要在应用程序配置文件中声明全部的 service使用 service/service 标签。 Service 通常位于后台运行它一般不需要与用户交互因此 Service 组件没有图形用户界面。Service 组件需要继承Service 基类。Service 组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。 定义 与 作用 Service服务是一个没有用户界面的专门在后台处理耗时任务的 Android 组件它没有UI。它有两种启动方式startService和bindService。其他应用组件能够启动 Service并且当用户切换到另外的应用场景Service将持续在后台运行。另外一个组件能够绑定到一个service与之交互IPC机制例如一个service可能会处理网络操作播放音乐操作文件I/O或者与内容提供者content provider交互所有这些活动都是在后台进行以上是Google文档的解释资料来源于大神博客 https://blog.csdn.net/ryantang03/article/details/7770939 Service 还有一个作用就是提升进程每个应用都是一个进程的优先级进程的优先级指的是在 Android 系统中会把正在运行的应用确定一个优先级当内存空间不足时系统会根据进程的优先级清理掉一部分进程占用的内存空间以获得足够的内存空间以供新启用的应用运行。详细的进程优先级划分如下         1)   前台进程应用程序存在Activity正位于前台可见并可控         2)   可见进程应用程序存在Activity处于局部可见状态即局部可见却不可控         3)   服务进程应用程序存在正在运行的Service         4)   后台进程应用程序的所有Activity均被置于后台没有任何Activity可见         5)   空进程已经退出的应用程序。 service 的进程优先级详细介绍请参考这篇博文https://blog.csdn.net/fhy_2008/article/details/7328967service 特性 【1】Service 的粘性 当 Service 被意外终止(非正常停止即不是通过 stopService() 或 stopSelf() 停止后会在未来的某一刻自动重启。 Service 的粘性是通过 onStartCommand() 方法的返回值确定的可用的值有 —–  Service.START_REDELIVER_INTENT    -----    粘性的且在自动重启时会重新给Service发送Intent对象。 —–  Service.START_STICKY                           -----    粘性的 —–  Service.START_NOT_STICKY                 -----    非粘性的 —–  Service.START_STICKY_COMPATIBILITY     -----    粘性的并且兼容的 当需要 Service 是非粘性的取值 Service.START_NOT_STICKY当需要 Service 是粘性的并且还需要获取 Intent 对象时取值 Service.START_REDELIVER_INTENT否则只是需要粘性的不需要 Intent 时取值super.onStartCommand() 默认值。【2】Service 是单例的在程序中一个 Service 类只会存在一个对象【3】Service 是没有界面的适合于在后台进行耗时操作但要注意 Service 仍然是运行在主线程中的故耗时的操作还是需要开启子线程来进行。service 用于在 后台 完成用户指定的操作service 有 2 种启动方式 第一种启动方式通过 start 方式开启服务即 startService启动。started启动当应用程序组件如 activity调用 startService() 方法启动服务时服务处于 started 状态。 使用 service 的步骤         1定义一个类继承 service         2manifest.xml 文件中配置 service         3使用 context 的 startService(Intent) 方法启动 service         4当不在使用时调用 stopService(Intent) 方法停止服务。 使用 start 方式启动的生命周期onCreate() -- onStartCommand() -- onDestory() 注意如果服务已经开启不会重复回调 onCreate() 方法如果再次调用 context.startService() 方法service 而是会调用onStart() 或者 onStartCommand() 方法。停止服务需要调用 context.stopService() 方法服务停止的时候回调 onDestory 被销毁。 特点一旦服务开启就跟调用者开启者没有任何关系了。开启者退出了开启者挂了服务还在后台长期的运行开启者不能调用服务里面的方法。 第二种启动方式采用 bind 的方式开启服务即 bindService绑定服务。bound绑定当应用程序组件调用 bindService() 方法绑定到服务时服务处于 bound 状态。 使用 service 的步骤         1定义一个类继承 Service         2在 manifest.xml 文件中注册 service         3使用 context 的 bindService(Intent, ServiceConnection, int) 方法启动 service         4不再使用时调用 unbindService(ServiceConnection) 方法停止该服务。 使用这种 bind 方式启动的 service 的生命周期如下onCreate() -- onBind() -- onUnbind() -- onDestory() 注意绑定服务不会调用 onStart() 或者 onStartCommand() 方法 特点bind 的方式开启服务绑定服务调用者挂了服务也会跟着挂掉。绑定者可以调用服务里面的方法。 下图形象地说明了 Service 两种状态的生命周期 通过这个图可以看到两种启动 service 的方式以及他们的生命周期bind service 的不同之处在于当绑定的组件销毁后对应的service 也就被 kill 了。service 的声明周期相比与 activity 的简单了许多只要好好理解两种启动 service 方式的异同就行。 startService() 与 bindService() 区别 startService 是由其他组件调用 startService() 方法启动的只是启动 Service这导致服务的 onStartCommand() 方法被调用。当服务是 started 状态时其生命周期与启动它的组件无关并且可以在后台无限期运行即使启动服务的组件已经被销毁。因此服务需要在完成任务后调用 stopSelf() 方法停止或者由其他组件调用 stopService() 方法停止。即启动它的组件如Activity和 Service 并没有关联只有当 Service 调用 stopSelf 或者其他组件调用 stopService 服务才会终止。bindService 方法启动 Service其他组件可以通过回调获取 Service 的代理对象和 Service 交互而这两方也进行了绑定当启动方销毁时Service 也会自动进行 unBind 操作当发现所有绑定都进行了unBind 时才会销毁 Service。总结started service启动服务是由其他组件调用 startService() 方法启动的。使用 bindService() 方法启用服务调用者与服务绑定在了一起调用者一旦退出服务也就终止大有“不求同时生必须同时死”的特点。 关于 Service 生命周期还有一张比较易懂的图来源于网络 总结 启动 service用 Context 类 定义的 startService(Intent) 即可启动 Service 组件其中 intent 定义方法与跳转 Activity 类似只需把 Actvity类 换成 Service类 即可。其生命周期为启动时 onCreate() – onStartCommand() – 销毁时的 onDestroy()反复调用 startService() 只会导致 Service 反复执行 onStartCommand()停止 service调用 Context类 定义的 stopService(Intent)即可停止 Service 组件反复调用并没有任何效果亦不会报告错误即即使停止没有启动的 Service 也不会出错。也可以在 Service类 的内部调用 Service 定义的 stopSelf() 方法停止当前 Service。绑定主要作用是实现组件间的通信实质的表现是 Activity 可以调用 Service 中的方法使 Service 执行特定的业务并且这些方法可以是带返回值的方法进而 Activity 可以通过获取这些返回值这样就实现与 Service 的通信。 【生命周期】                 – onCreate()   ---  当第1次绑定时执行                 – onBind()       ---  当第1次绑定时执行                 – onDestroy()  ---  当解除绑定时执行【绑定与解绑】 调用 bindService() 方法可以实现 Activity 与 Service 的绑定调用 unbindService() 可以解除绑定。在 Activity 被销毁之前必须解除与 Service 的绑定。 具体实现代码如下 //在Activity中调用bindService()来实现服务绑定 Intent intent new Intent(this,MyService.class); //用于连接的对象相当于组件间的一个连接纽带 ServiceConnection connnew ServiceConnection(){Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 当Service已经连接//参数IBinder service就是与service进行通信的对象通过这个对象可以调用Service里的方法}Overridepublic void onServiceDisconnected(ComponentName name) {// 当Service断开连接} }; //绑定标志 int FLAGSBIND_AUTO_CREATE; bindService(intent,conn,FLAGS); //然后在Activity销毁时解绑,这里需要一个连接对象所以需要在上面把conn设为全局变量 Overrideprotected void onDestroy() {unbindService(conn);super.onDestroy();}//MyService类需要继承自Service还需要在清单文件中注册 public class MyService extends Service {Overridepublic void onCreate() {}Overridepublic IBinder onBind(Intent intent) {//这里需要返回一个IBinder对象我们可以创建一个内部类来获得这个对象MyBinder binder new MyBinder(); return binder;}/**这个内部类就是我们返回的IBinder类Activity的conn里连接上后就是得到这个对象才实现了组件间的通信*/public class MyBinder extends Binder {//这个内部类可以写很多方法来让Activity调用还可以是带有返回值的方法}Overridepublic int onStartCommand(Intent intent, int flags, int startId) {return super.onStartCommand(intent, flags, startId);}Overridepublic void onDestroy() {}}service 生命周期也涉及一些回调方法这些方法都不用调用父类方法具体如下 public class ExampleService extends Service {int mStartMode; // indicates how to behave if the service is killedIBinder mBinder; // interface for clients that bindboolean mAllowRebind; // indicates whether onRebind should be usedOverridepublic void onCreate() {// The service is being created}Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// The service is starting, due to a call to startService()return mStartMode;}Overridepublic IBinder onBind(Intent intent) {// A client is binding to the service with bindService()return mBinder;}Overridepublic boolean onUnbind(Intent intent) {// All clients have unbound with unbindService()return mAllowRebind;}Overridepublic void onRebind(Intent intent) {// A client is binding to the service with bindService(),// after onUnbind() has already been called}Overridepublic void onDestroy() {// The service is no longer used and is being destroyed} } 关于 Service 还有很多知识这里就不再一一列举可以参考http://developer.android.com/guide/components/services.html 通过 start 方法开启服务 创建一个类继承Service import android.app.Service; import android.content.Intent; import android.os.IBinder;public class MyService extends Service {public MyService() {}Overridepublic IBinder onBind(Intent intent) {// TODO: Return the communication channel to the service.throw new UnsupportedOperationException(Not yet implemented);} }在清单文件中注册这个服务serviceandroid:name.MyServiceandroid:enabledtrueandroid:exportedtrue/service通过startService方法启动服务Intent intent new Intent(this, MyService.class);startService(intent);当不用服务的时候通过stopService()方法停止该服务 stopService(intent);特点 通过start方法启动的service一旦服务开启就跟调用者开启者没有任何关系了。开启者退出了开启者挂了服务还在后台长期的运行开启者不能调用服务里面的方法。 通过 bind 的方式启动服务 创建一个类继承Service import android.app.Service; import android.content.Intent; import android.os.IBinder;public class MyService extends Service {public MyService() {}Overridepublic IBinder onBind(Intent intent) {// TODO: Return the communication channel to the service.throw new UnsupportedOperationException(Not yet implemented);} }在清单文件中注册这个服务serviceandroid:name.MyServiceandroid:enabledtrueandroid:exportedtrue/service通过startService方法启动服务Intent intent new Intent(this, MyService.class);bindService(Intent,ServiceConnection,int);当不用服务的时候通过stopService()方法停止该服务 unbindService(ServiceConnection);特点使用bind方法启动的服务则调用者挂了服务也挂了调用者可以调用服务中的方法 定义一个 Server 项目内 Server 包 右键 -- New -- Service -- Service 或者直接创建 Class 类继承 Service 并重写 IBinder 方法 public class MyService extends Service{public MyService(){}Overridepublic IBinder onBind(Intent intent) {return null;}Overridepublic void onCreate() {super.onCreate();}Overridepublic int onStartCommand(Intent intent, int flags, int startId) {return super.onStartCommand(intent, flags, startId);}Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();} }重写 Service 的 onCreate()、onStartCommand() 和 onDestory() 方法。其中 onCreate() 方法在服务创建的时候调用、onStartCommand() 方法会在每次服务启动的时候调用、onDestory() 方法会在服务销毁的时候调用。 通常情况下如果我们希望服务一旦启动就立刻去执行任务就可以将逻辑卸载 onStartCommand() 方法里。 另外需要注意的是每个服务都需要在 Androidmanifest.xml 中进行注册才能生效 application.......serviceandroid:name.MyServiceandroid:enabledtrueandroid:exportedtrue/service /application示例定义一个类继承 service //本地service不涉及进程间通信 public class MyService extends Service {private String TAG MyService;Overridepublic void onCreate() {super.onCreate();Log.i(TAG,onCreate);}Overridepublic void onStart(Intent intent, int startId) {super.onStart(intent, startId);Log.i(TAG,onStart);}Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i(TAG,onStartCommand);return super.onStartCommand(intent, flags, startId);}//绑定服务时调用这个方法返回一个IBinder对象NullableOverridepublic IBinder onBind(Intent intent) {Log.i(TAG,onBind);return new MyBinder();}Overridepublic boolean onUnbind(Intent intent) {Log.i(TAG,onUnbind);return super.onUnbind(intent);}// 停止服务通过调用Context.unbindService()别忘了service也继承了Context类 // Override // public void unbindService(ServiceConnection conn) { // super.unbindService(conn); // Log.i(TAG,unbindService); // }//服务挂了Overridepublic void onDestroy() {super.onDestroy();Log.i(TAG,onDestroy);}public interface MyIBinder{void invokeMethodInMyService();}public class MyBinder extends Binder implements MyIBinder{public void stopService(ServiceConnection serviceConnection){unbindService(serviceConnection);}Overridepublic void invokeMethodInMyService() {for(int i 0; i 20; i ){System.out.println(service is opening);}}} 在 manifest.xml 文件中注册 service //Service 必须要注册service android:name.server.MyServiceandroid:exportedtrueintent-filteraction android:namecn.hotwoo.play.server.MyService/category android:nameandroid.intent.category.default //intent-filter/service 绑定自定义的 service public class CustomActivity extends AppCompatActivity {private Button startService, unbindService;private MyService.MyBinder myBinder;private ServiceConnection serviceConnection;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_custom);startService (Button) findViewById(R.id.service_start);unbindService (Button) findViewById(R.id.unbind_service);startService.setOnClickListener(new View.OnClickListener() {Overridepublic void onClick(View v) { // startService(new Intent(CustomActivity.this, MyService.class));serviceConnection new MyServiceConnection();bindService(new Intent(CustomActivity.this, MyService.class), serviceConnection, Context.BIND_AUTO_CREATE);}});unbindService.setOnClickListener(new View.OnClickListener() {Overridepublic void onClick(View v) {unbindService(serviceConnection);}});}class MyServiceConnection implements ServiceConnection {//这里的第二个参数IBinder就是Service中的onBind方法返回的Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.i(MyService, onServiceConnected);myBinder (MyService.MyBinder) service;}Overridepublic void onServiceDisconnected(ComponentName name) {Log.i(MyService, onServiceDisconnected);}} } startService输出日志 04-01 19:56:09.846 22845-22845/cn.hotwoo.play I/MyService: onCreate 04-01 19:56:09.854 22845-22845/cn.hotwoo.play I/MyService: onStartCommand 04-01 19:56:09.854 22845-22845/cn.hotwoo.play I/MyService: onStart bindService 输出日志 04-01 19:53:21.459 14704-14704/cn.hotwoo.play I/MyService: onCreate 04-01 19:53:21.460 14704-14704/cn.hotwoo.play I/MyService: onBind 04-01 19:53:21.461 14704-14704/cn.hotwoo.play I/MyService: onServiceConnected 点击back键关闭activity或者调用Context.unbindService()方法后 04-05 01:16:27.508 11427-11427/cn.hotwoo.play I/MyService: onUnbind 04-05 01:16:27.508 11427-11427/cn.hotwoo.play I/MyService: onDestroy 远程服务 调用者 和 service 不在同一个进程中service 在单独的进程中的 main 线程是一种垮进程通信方式。学习地址 绑定远程服务的步骤 在服务的内部创建一个内部类提供一个方法可以间接调用服务的方法把暴露的接口文件的扩展名改为.aidl文件 去掉访问修饰符实现服务的onbind方法继承Bander和实现aidl定义的接口提供给外界可调用的方法在 activity 中绑定服务。bindService()在服务成功绑定的时候会回调 onServiceConnected方法 传递一个 IBinder对象aidl 定义的接口.Stub.asInterface(binder) 调用接口里面的方法IntentService IntentService 是 Service 的子类比普通的 Service 增加了额外的功能。 Service 分为两种 一种是 Service这一种是运行在主线程中的如果要执行耗时操作可在service中创建一个异步来执行。一种是 IntentService这是一种异步服务是继承于Service 的子类所以推荐当要执行耗时操作时使用 IntentService如果不耗时我们可以使用 ServiceService 本身存在两个问题 Service不会专门启动一条单独的进程Service与它所在应用位于同一个进程中Service也不是专门一条新线程因此不应该在Service中直接处理耗时的任务 IntentService 特征: 会创建独立的 worker 线程来处理所有的 Intent 请求会创建独立的 worker 线程来处理 onHandleIntent() 方法实现的代码无需处理多线程问题所有请求处理完成后IntentService 会自动停止无需调用 stopSelf() 方法停止 Service为 Service 的 onBind() 提供默认实现返回 null为 Service 的 onStartCommand 提供默认实现将请求 Intent 添加到队列中IntentService 相比 父类Service 而言最大特点是其回调函数 onHandleIntent 中可以直接进行耗时操作不必再开线程。其原理是IntentService 的成员变量 Handler在初始化时已属于工作线程之后 handleMessage包括 onHandleIntent 等函数都运行在工作线程中。 IntentService 还有一个特点就是多次调用 onHandleIntent函数也就是有多个耗时任务要执行多个耗时任务会按顺序依次执行。原理是其内置的 Handler 关联了任务队列Handler 通过 looper 取任务执行是顺序执行的。这个特点就能解决多个耗时任务需要顺序依次执行的问题。而如果仅用 service开多个线程去执行耗时操作就很难管理。 官方文档的说明 IntentService This is a subclass of Service that uses a worker thread to handle all start requests, one at a time. This is the best option if you dont require that your service handle multiple requests simultaneously. All you need to do is implement onHandleIntent(), which receives the intent for each start request so you can do the background work. IntentService 使用队列的方式将请求的 Intent 加入队列然后开启一个 worker thread(线程) 来处理队列中的 Intent对于异步的startService 请求IntentService 会处理完成一个之后再处理第二个每一个请求都会在一个单独的 worker thread 中处理不会阻塞应用程序的主线程这里就给我们提供了一个思路如果有耗时的操作与其在Service里面开启新线程还不如使用IntentService 来处理耗时操作。而在一般的继承 Service 里面如果要进行耗时操作就必须另开线程但是使用 IntentService 就可以直接在里面进行耗时操作因为默认实现了一个 worker thread。对于异步的 startService 请求IntentService 会处理完成一个之后再处理第二个。 IntentService 内部有一个工作线程来完成耗时的操作只需实现 onHandleIntent 方法即可完成工作后会自动终止服务如果同时执行多个任务时会以工作队列的方式一次执行通过该类来完成本 APP 中耗时的工作 看下 IntentService 的具体实现 public class HelloIntentService extends IntentService {/** * A constructor is required, and must call the super IntentService(String)* constructor with a name for the worker thread.*/public HelloIntentService() {super(HelloIntentService);}/*** The IntentService calls this method from the default worker thread with* the intent that started the service. When this method returns, IntentService* stops the service, as appropriate.*/Overrideprotected void onHandleIntent(Intent intent) {// Normally we would do some work here, like download a file.// For our sample, we just sleep for 5 seconds.long endTime System.currentTimeMillis() 5*1000;while (System.currentTimeMillis() endTime) {synchronized (this) {try {wait(endTime - System.currentTimeMillis());} catch (Exception e) {}}}} } 关于停止 Service如果 service 是非绑定的最终当任务完成时为了节省系统资源一定要停止 service可以通过 stopSelf()来停止也可以在其他组件中通过 stopService() 来停止绑定的 service 可以通过 onUnBind() 来停止 service。 启动 和 停止 服务 启动服务 Intent startIntent new Intent(this, MyService.class); startService(startIntent); //启动服务停止服务 Intent stopIntent new Intent(this, MyService.class); stopService(stopIntent); //停止服务使用前台服务 前台服务与普通服务的最大区别在于它会一直有一个正在运行的图标在系统的状态栏中下拉状态栏后可以看到更加详细的内容非常类似于通知的效果。 public class MyService extends Service{Intent intent new Intent(this, MainActivity.class);PendingIntent pi PendingIntent.getActivity(this, 0 , intent, 0);Notification notification new NotificationCompat.Builder(this).setContentTitle( this is content titile).setContentText(this is content text).setWhen(System.currentTimeMillis()).setSmallIcon(R.mipmap.ic_launcher);.setLargeIcon(BitmapFactory.decodeResource(getResource(),R.mipmap.ic_launcher)).setContentIntent(pi).build();startForeground(1,notification); }构造一个Notification对象后并没有使用NotificationManager 来讲通知显示出来而是调用了startForeground()方法该方法会将MyService变成一个前台服务并在系统状态栏中显示出来。 使用 IntentService 服务中的代码都默认运行在主线程中如果直接在服务中执行耗时操作很容易出现ANRApplication not Responding 所以这个时候需要用到Android多线程编程技术我们应该在服务的每个具体的方法里启动一个子线程然后在这里去处理那些耗时的操作 public class MyService extends Service{...Overridepublic int onStartCommand(Intent intent , int flags, int startId){new Thread(new Runnable(){public void run(){//处理具体的逻辑}}).start();return super.onStartCommand(intent, flags, startId);} }但是这种服务一旦启动之后就会一直处于运行状态必须调用stopService()或者stopSelf()方法才能让服务停止下来所以如果想要实现让一个服务在执行完毕后自动停止的功能就可以这样写 public class MySerivce extends Servcie{...Overridepublic int onStartCommand(Intent intent, int flats , int startId){new Thread(new Runnable(){public void run(){//处理具体的逻辑stopSelf();}});} }虽说这样的写法并不复杂但是总会有一些程序员忘记开启线程或者忘记调用stopSelf() 方法。为了简单创建一个异步、会自动停止的服务。Android专门提供了一个IntentService类 public class MyIntentService extends IntentService{public MyIntentService(){super(MyIntentService); //调用父类的有参构造方法}Overrideprotected void onHandleIntent(Intent intent){ //打印当前的线程IDLog.e(mylog,Thread id is” Thread.cuttentThread().getId();}Overridepublic void onDestory(){super.onDestory();Log.e(mylog,on Destory executed);} }首先这里提供一个无参的构造方法并且必须在其内部调用父类的有参构造方法。然后要在子类中去实现onHandleIntent() 这个抽象方法在这个方法中可以去处理一些逻辑而且不用担心ANR因为这个方法已经是在子线程中运行了。 IntentService线程的调用 Intent intent new Intent(this, MyIntentService.class); startServcie(intent);如此线程就会自动启动并执行逻辑执行完毕后自动关闭。这就是IntentService 的好处能够自动开启和关闭 1.3 content provider contentprovider 是 android 四大组件之一的内容提供器它主要的作用就是将程序的内部的数据和外部进行共享为数据提供外部访问接口被访问的数据主要以数据库的形式存在而且还可以选择共享哪一部分的数据。这样一来对于程序当中的隐私数据可以不共享从而更加安全。contentprovider 是 android中 一种跨程序共享数据的重要组件。 1android 平台提供了 ContentProvider 把一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类 从该内容提供者中获取或存入数据。2只有需要在多个应用程序间共享数据是才需要 内容提供者。例如通讯录数据被多个应用程序使用且必须存储在一个内容提供者中。它的好处是统一数据访问方式。3ContentProvider 实现数据共享。ContentProvider 用于保存和获取数据并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式因为 android 没有提供所有应用共同访问的公共存储区。4开发人员不会直接使用 ContentProvider 类的对象大多数是通过 ContentResolver 对象实现对 ContentProvider 的操作。5ContentProvider 使用 URI 来唯一标识其数据集这里的 URI 以 content:// 作为前缀表示该数据由 ContentProvider来管理。 4 大基本组件都需要注册才能使用每个 Activity、service、Content Provider 都需要在 AndroidManifest 文件中进行配置。AndroidManifest 文件中未进行声明的 activity、服务 以及 内容提供者 将不为系统所见从而也就不可用。而 broadcast receiver 广播接收者的注册分静态注册在AndroidManifest文件中进行配置和通过代码动态创建并以调用Context.registerReceiver()的方式注册至系统。需要注意的是在AndroidManifest文件中进行配置的广播接收者会随系统的启动而一直处于活跃状态只要接收到感兴趣的广播就会触发即使程序未运行。1. 作用 content provider 中文意思是 内容提供者ContentProvider 可以将应用程序自身的数据对外(对其它应用程序)共享使得其它应用可以对自身的数据进行增、删、改、查操作。 使用系统的 ContentProviderAndroid 系统使用了许多 ContentProvider将系统中的绝大部分常规数据进行对外共享系统的 ContentProvider 有很多例如通讯录、通话记录、短信、相册、歌曲、视频、日历等等一般这些数据都存放于一个个的数据库中。同时这些数据一般都需要和第三方的 app 进行共享数据。既然是使用系统的那么 contentprovider 的具体实现就不需要我们担心了使用内容提供者的步骤如下 获取 ContentResolver 实例确定 Uri 的内容并解析为具体的 Uri 实例通过 ContentResolver 实例来调用相应的方法传递相应的参数但是第一个参数总是 Uri它制定了我们要操作的数据的具体地址 可以通过读取系统通讯录的联系人信息显示在Listview中来实践这些知识。不要忘记在读取通讯录的时候在清单文件中要加入相应的读取权限。 自定义 ContentProvider 系统的 contentprovider 在与我们交互的时候只接受了一个 Uri 的参数然后根据我们的操作返回给我们结果。系统到底是如何根据一个 Uri 就能够提供给我们准确的结果呢只有自己亲自实现一个看看了。 和之前提到的一样想重新自定义自己程序中的四大组件就必须重新实现一个类重写这个类中的抽象方法在清单文件中注册最后才能够正常使用。 重新实现 ContentProvider 之后发现我们重写了 6 个重要的抽象方法 oncreatequeryupdateinsertdeletegettype 大部分的方法在数据库那里已经见过了他们内部的逻辑可想而知都是对数据的增删改查操作其中这些方法的第一个参数大多都是 Uri 实例。其中有两个方法比较特殊 oncreate 方法应该是内容提供者创建的时候所执行的一个回调方法负责数据库的创建和更新操作。这个方法只有我们在程序中获取ContentResolver实例之后准备访问共享数据的时候才会被执行。gettype 方法是获取我们通过参数传递进去的Uri的MIME类型这个类型是什么后面会有实例说明。 内容提供者首先要做的一个事情就是将我们传递过来的 Uri 解析出来确定其他程序到底想访问哪些数据。 Uri 的形式一般有两种 1以路径名为结尾这种 Uri 请求的是整个表的数据如: content://com.demo.androiddemo.provider/tabl1 标识我们要访问tabl1 表中所有的数据2以 id 列值结尾这种 Uri 请求的是该表中和其提供的列值相等的单条数据。 content://com.demo.androiddemo.provider/tabl1/1 标识我们要访问 tabl1 表中_id 列值为 1 的数据。 如果是内容提供器的设计者那么我们肯定知道这个程序的数据库是什么样的每一张表或者每一张表中的 _id 都应该有一个唯一的内容 Uri 。我们可以将传递进来的 Uri 和我们存好的 Uri 进行匹配匹配到了之后就说明数据源已经找到便可以进行相应的增删改查操作。 ContentProvider详解 2. 访问 Content Provider Content Provider 以一个或多个表与在关系型数据库中找到的表类似的形式将数据呈现给外部应用。 行表示提供程序收集的某种数据类型的实例行中的每个列表示为实例收集的每条数据。 应用从具有 ContentResolver对象的Content Provider访问数据。 此对象具有调用提供程序对象ContentProvider 的某个具体子类的实例中同名方法的方法。 ContentResolver 方法可提供持续存储的基本“CRUD”创建、检索、更新和删除功能。 客户端应用进程中的 ContentResolver 对象和拥有提供程序的应用中的 ContentProvider 对象可自动处理跨进程通信。 ContentProvider 还可充当其数据存储区和表格形式的数据外部显示之间的抽象层。 注要访问提供程序您的应用通常需要在其清单文件中请求特定权限。 内容提供程序权限部分详细介绍了此内容。 例如要从用户字典提供程序中获取字词及其语言区域的列表则需调用 ContentResolver.query()。 query() 方法会调用用户字典提供程序所定义的 ContentProvider.query() 方法。 以下代码行显示了 ContentResolver.query() 调用 // Queries the user dictionary and returns results mCursor getContentResolver().query(UserDictionary.Words.CONTENT_URI, // The content URI of the words tableURI映射的数据表名mProjection, // The columns to return for each row我们所要我的选择查询的表的列名,字符串数组mSelectionClause // Selection criteria查询条件相当于SQL中的where a ? and b ?mSelectionArgs, // Selection criteria,字符串数组替代上面条件中的占位符mSortOrder); // The sort order for the returned rows排列的顺序相当于SQL中的order(字段) 3. URI 资源访问格式 内容 URI 是用于在 Content Provider 中标识数据的 URI。内容 URI 包括整个提供程序的符号名称其授权和一个指向表的名称路径。 当您调用客户端方法来访问提供程序中的表时该表的内容 URI 将是其参数之一。 在前面的代码行中常量 CONTENT_URI 包含用户字典的“字词”表的内容 URI。 ContentResolver 对象会分析出 URI 的授权并通过将该授权与已知提供程序的系统表进行比较来“解析”提供程序。 然后 ContentResolver 可以将查询参数分派给正确的提供程序。 ContentProvider 使用内容 URI 的路径部分来选择要访问的表。 提供程序通常会为其公开的每个表显示一条路径。 在前面的代码行中“字词”表的完整 URI 是 content://user_dictionary/words 其中user_dictionary 是提供程序的授权需要在清单文件中注册words 是表的路径 content://架构始终显示并将此标识为内容 URI 。 许多提供程序都允许您通过将 ID 值追加到 URI 末尾来访问表中的单个行。 例如要从用户字典中检索 _ID 为 4 的行则可使用此内容 URI Uri singleUri ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4); 在检索到一组行后想要更新或删除其中某一行时通常会用到 ID 值。 注Uri 和 Uri.Builder 类包含根据字符串构建格式规范的 URI 对象的便利方法。 ContentUris 包含一些可以将 ID 值轻松追加到 URI 后的方法。 前面的代码段就是使用 withAppendedId() 将 ID 追加到 UserDictionary 内容 URI 后。 4. 显示数据 ContentResolver.query() 方法始终会返回符合以下条件的 Cursor包含查询的表为匹配查询选择条件的行指定的列 Cursor 对象为其包含的行和列提供随机读取访问权限。 通过使用 Cursor 方法您可以循环访问结果中的行、确定每个列的数据类型、从列中获取数据并检查结果的其他属性。 某些 Cursor 实现会在提供程序的数据发生更改时自动更新对象或在 Cursor 更改时触发观察程序对象中的方法。 注提供程序可能会根据发出查询的对象的性质来限制对列的访问。 例如联系人提供程序会限定只有同步适配器才能访问某些列因此不会将它们返回至 Activity 或服务。 如果没有与选择条件匹配的行则提供程序会返回 Cursor.getCount() 为 0空游标的 Cursor 对象。 如果出现内部错误查询结果将取决于具体的提供程序。它可能会选择返回 null或引发 Exception。 由于 Cursor 是行“列表”因此显示 Cursor 内容的较好方式是通过 SimpleCursorAdapter 将其与 ListView 关联。 以下代码段会创建一个包含由查询检索到的 Cursor 的 SimpleCursorAdapter 对象并将此对象设置为 ListView 的适配器 // Defines a list of columns to retrieve from the Cursor and load into an output row String[] mWordListColumns {UserDictionary.Words.WORD, // Contract class constant containing the word column nameUserDictionary.Words.LOCALE // Contract class constant containing the locale column name };// Defines a list of View IDs that will receive the Cursor columns for each row int[] mWordListItems { R.id.dictWord, R.id.locale};// Creates a new SimpleCursorAdapter mCursorAdapter new SimpleCursorAdapter(getApplicationContext(), // The applications Context objectR.layout.wordlistrow, // A layout in XML for one row in the ListViewmCursor, // The result from the querymWordListColumns, // A string array of column names in the cursormWordListItems, // An integer array of view IDs in the row layout0); // Flags (usually none are needed)// Sets the adapter for the ListView mWordList.setAdapter(mCursorAdapter); 注意要通过 Cursor 支持 ListView游标必需包含名为 _ID 的列。 正因如此前文显示的查询会为“字词”表检索 _ID 列即使 ListView 未显示该列。 此限制也解释了为什么大多数提供程序的每个表都具有 _ID 列。 获取某个列的值 // 得到words表中WORD的字段标签也即是上面查询时列名的位置mWordListColumns的下标这里应该是第一个 int index mCursor.getColumnIndex(UserDictionary.Words.WORD); if (mCursor ! null) {while (mCursor.moveToNext()) {// 得到该下标列名的值.newWord mCursor.getString(index); } } else {} 5.创建 Content Provider 实现 ContentProvider 类实现它的抽象方法。 query() 从您的提供程序检索数据。使用参数选择要查询的表、要返回的行和列以及结果的排序顺序。 将数据作为 Cursor 对象返回。 insert() 在您的提供程序中插入一个新行。使用参数选择目标表并获取要使用的列值。 返回新插入行的内容 URI。 update() 更新您提供程序中的现有行。使用参数选择要更新的表和行并获取更新后的列值。 返回已更新的行数。 delete() 从您的提供程序中删除行。使用参数选择要删除的表和行。 返回已删除的行数。 getType() 返回内容 URI 对应的 MIME 类型。实现内容提供程序 MIME 类型部分对此方法做了更详尽的描述。 onCreate() 初始化您的提供程序。Android 系统会在创建您的提供程序后立即调用此方法。 请注意ContentResolver 对象尝试访问您的提供程序时系统才会创建它。 要创建我们自己的 Content Provider 的话我们需要遵循以下几步 创建一个继承了ContentProvider父类的类定义一个名为CONTENT_URI并且是public static final的Uri类型的类变量你必须为其指定一个唯一的字符串值最好的方案是以类的全名称 如public static final Uri CONTENT_URI Uri.parse( “content://com.google.android.MyContentProvider”);创建你的数据存储系统。大多数Content Provider使用Android文件系统或SQLite数据库来保持数据但是你也可以以任何你想要的方式来存储。定义你要返回给客户端的数据列名。如果你正在使用Android数据库则数据列的使用方式就和你以往所熟悉的其他数据库一样。但是你必须为其定义一个叫_id的列它用来表示每条记录的唯一性。如果你要存储字节型数据比如位图文件等那保存该数据的数据列其实是一个表示实际保存文件的URI字符串客户端通过它来读取对应的文件数据处理这种数据类型的Content Provider需要实现一个名为_data的字段_data字段列出了该文件在Android文件系统上的精确路径。这个字段不仅是供客户端使用而且也可以供ContentResolver使用。客户端可以调用ContentResolver.openOutputStream()方法来处理该URI指向的文件资源如果是ContentResolver本身的话由于其持有的权限比客户端要高所以它能直接访问该数据文件。声明public static String型的变量用于指定要从游标处返回的数据列。查询返回一个Cursor类型的对象。所有执行写操作的方法如insert(), update() 以及delete()都将被监听。我们可以通过使用ContentResover().notifyChange()方法来通知监听器关于数据更新的信息。在AndroidMenifest.xml中使用标签来设置Content Provider。如果你要处理的数据类型是一种比较新的类型你就必须先定义一个新的MIME类型以供ContentProvider.geType(url)来返回。MIME类型有两种形式:一种是为指定的单个记录的还有一种是为多条记录的。 这里给出一种常用的格式 vnd.android.cursor.item/vnd.yourcompanyname.contenttype 单个记录的MIME类型 比如, 一个请求列车信息的URI如content://com.example.transportationprovider/trains/122 可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型。 vnd.android.cursor.dir/vnd.yourcompanyname.contenttype 多个记录的MIME类型 比如, 一个请求所有列车信息的URI如content://com.example.transportationprovider/trains 可能就会返回vnd.android.cursor.dir/vnd.example.rail这样一个MIME 类型。 实例读取系统联系人 读取系统联系人需要声明权限如果系统是 6.0 以后的需要申请运行时权限 if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) ! PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 1);}else {readContacts(); //读取联系人} private void readContacts(){Cursor cursor null;try{//查询联系人数据cursor getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);if(cursor!null){while(cursor.moveToNext()){//获取联系人姓名String name cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));//获取联系人电话号码String number cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));list.add(name\nnumber);}}}catch(Exception e){e.printStackTrace()}finally{if(cursor ! null){cursor.close();}} }Override public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults){switch(requestCode){case 1:if(grantResults.length 0 grantResults[0] PackageManager.PERMISSION_GRANTED){readContacts();}else {//您拒绝了权限}} }创建自己的内容提供器 创建自己的内容提供器需要去继承 ContentProvider 类ContentProvider 类中有6个抽象方法我们在使用子类继承它的时候需要将这6个方法全部重写。 public class MyProvider extends ContentProvider{Overridepublic boolean onCreate() {return false;}Overridepublic Cursor query(Uri uri, String[] projection, Stirng selection, String[] selectionArgs, String sortOrder){return null;}Overrridepublic Uri insert(Uri uri , ContentValues values){return null;}Overridepublic int update(Uri uri, ContentValuse values, String selection, String[] selectionArgs){return 0;}Overridepublic int delete(Uri uri, String selection, String[] selectionArgs){return 0;}Overridepublic String getType(Uri uri{return null} }设计内容 URI 内容 URI 是用于在提供程序中标识数据的 URI。内容 URI 包括整个提供程序的符号名称其授权和一个指向表或文件的名称路径。 可选 ID 部分指向表中的单个行。 ContentProvider 的每一个数据访问方法都将内容 URI 作为参数您可以利用这一点确定要访问的表、行或文件。 设计授权 提供程序通常具有单一授权该授权充当其 Android 内部名称。为避免与其他提供程序发生冲突您应该使用互联网网域所有权反向作为提供程序授权的基础。 由于此建议也适用于 Android 软件包名称因此您可以将提供程序授权定义为包含该提供程序的软件包名称的扩展名。 例如如果您的 Android 软件包名称为 com.example.appname 就应使用 com.example.appname.provider 授权。 设计路径结构 开发者通常通过追加指向单个表的路径来根据权限创建内容 URI。 例如如果您有两个表table1 和 table2则可以通过合并上一示例中的权限来生成 内容 URI com.example..provider/table1 和 com.example..provider/table2。路径并不限定于单个段也无需为每一级路径都创建一个表。 处理内容 URI ID 按照惯例提供程序通过接受末尾具有行所对应 ID 值的内容 URI 来提供对表中单个行的访问。 同样按照惯例提供程序会将该 ID 值与表的 _ID 列进行匹配并对匹配的行执行请求的访问。 这一惯例为访问提供程序的应用的常见设计模式提供了便利。应用会对提供程序执行查询并使用 CursorAdapter 以 ListView 显示生成的 Cursor。 定义 CursorAdapter 的条件是 Cursor 中的其中一个列必须是 _ID 用户随后从 UI 上显示的行中选取其中一行以查看或修改数据。 应用会从支持 ListView 的 Cursor 中获取对应行获取该行的 _ID 值将其追加到内容 URI然后向提供程序发送访问请求。 然后提供程序便可对用户选取的特定行执行查询或修改。 内容 URI 模式 为帮助您选择对传入的内容 URI 执行的操作提供程序 API 加入了实用类 UriMatcher它会将内容 URI “模式 ”映射到整型值。 您可以在一个 switch 语句中使用这些整型值为匹配特定模式的一个或多个内容 URI 选择所需操作。 URI 的主要格式有以下两种 content://com.example.app.provider/table1 content://com.example.app.provider/table1/1*匹配由任意长度的任何有效字符组成的字符串 #匹配由任意长度的数字字符组成的字符串//一个能够匹配任意表的内容 URI 格式就可以写成: content://com.example.app.provider/*//一个能够匹配表中任意一行数据的内容 URI 格式就可以写成 content://com.example.app.provider/table1/#以设计和编码内容 URI 处理为例假设一个具有授权 com.example.app.provider 的提供程序能识别以下指向表的内容 URI content://com.example.app.provider/table1一个名为 table1 的表 content://com.example.app.provider/table2/dataset1一个名为 dataset1 的表 content://com.example.app.provider/table2/dataset2一个名为 dataset2 的表 content://com.example.app.provider/table3一个名为 table3 的表 提供程序也能识别追加了行 ID 的内容 URI 例如content://com.example.app.provider/table3/1 对应由 table3 中 1 标识的行的内容 URI。 可以使用以下内容 URI 模式 content://com.example.app.provider/* 匹配提供程序中的任何内容 URI。content://com.example.app.provider/table2/* 匹配表 dataset1 和表 dataset2 的内容 URI但不匹配 table1 或 table3 的内容 URI。content://com.example.app.provider/table3/#匹配 table3 中单个行的内容 URI 如 content://com.example.app.provider/table3/6 对应由 6 标识的行的内容 URI。 在清单文件中注册实现 content provider 的类 与 Activity 和 Service 组件类似必须使用 provider 元素在清单文件中为其应用定义 ContentProvider 的子类。 Android 系统会从该元素获取以下信息 授权 (android:authorities) 用于在系统内标识整个提供程序的符号名称也即是上面所说的URI包含路径名表名的字符串。 提供程序类名 ( android:name ) 实现 ContentProvider 的类。实现 ContentProvider 类中对此类做了更详尽的描述。 【 实现 】 由于 ContentProvider 可提供增、删、改、查这些操作通常结合 SQLite 使用。 ContentResolver 是读取由 ContentProvider 共享的数据的工具。通过 Context类 定义的 getContentResolver() 方法可以获取ContentResolver 对象。如果您不打算与其他应用共享数据则无需开发自己的提供程序。 对于每一个应用程序来说如果想要访问 内容提供器 中共享的数据就一定要借助 ContentResolver 类可以通过Context中的getContentResolver() 方法获取该类的实例。ContentResolver中提供了一系列的方法用于对数据进行CRUD操作其中insert() 方法用于添加数据update() 方法用于更新数据delete() 方法用于删除数据query() 方法用于查询数据。 不同于SQLiteDatabaseContentResolver 中的增删改查都是接收一个URl参数这个参数被称为内容URL。内容URL给内容提供器中的数据建立了唯一标识符它主要由两部分组成authority 和 path 。authority 是用于对不同的应用程序做区分的一般为了避免冲突都会采用程序包名的方式进行命名。path则是用于对同一应用程序中不同的表做区分通常都会添加到authority后面 content://com.example.app.provider/table1 content://com.example.app.provider/table2在使用内容URL作为参数的时候需要将URL转换成URL对象 Uri uri Uri.parse(content://com.example.app.provider/table1)现在我们就可以使用这个uri对象来查询talbe1表中的数据了 Cursor cursor getContentResolver().queryuri,projection,selection,selectionArgs,sortOrder ;对应参数的解释 查询完之后就可以从游标中取值了 if(cursor ! null){while(cursor.moveToNext()) {String column1 cursor.getString(cursor.getColumnIndex(column1));int column2 cursor.getInt(cursor.getColumnIndex(column2));}cursor.close(); }增、删、改、查 添加数据 要增加记录可以调用 ContentResolver.insert() 方法该方法接受一个要增加的记录的目标URI以及一个包含了新记录值的Map对象调用后的返回值是新记录的URI包含记录号。 上面的例子中我们都是基于联系人信息簿这个标准的Content Provider现在我们继续来创建一个insertRecord() 方法以对联系人信息簿中进行数据的添加 ContentValues values new ContentValues(); values.put(“column1”, text); values.put(column2, 1); getContentResolver().insert(uri, values);示例方法 private void insertRecords(String name, String phoneNo) {ContentValues values new ContentValues();values.put(People.NAME, name);Uri uri getContentResolver().insert(People.CONTENT_URI, values);Log.d(”ANDROID”, uri.toString());Uri numberUri Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);values.clear();values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE);values.put(People.NUMBER, phoneNo);getContentResolver().insert(numberUri, values); }更新数据 可以使用 ContentResolver.update() 方法来修改数据。 ContentValues valuse new ContentValues(); valuse.put(column1, ); getContentResolver().update(uri, values, column1 ? and column2 ?, new String[]{text, 1});示例方法 private void updateRecord(int recNo, String name) {Uri uri ContentUris.withAppendedId(People.CONTENT_URI, recNo);ContentValues values new ContentValues();values.put(People.NAME, name);getContentResolver().update(uri, values, null, null); }删除数据 Content Provide r中的 getContextResolver.delete() 方法可以用来删除记录。 getContentResolver().delete(uri , column2 ?, new String[]{ 1});下面的记录用来删除设备上所有的联系人信息 private void deleteRecords() {Uri uri People.CONTENT_URI;getContentResolver().delete(uri, null, null); }查询记录: 在 Content Provider 中使用的查询字符串有别于标准的 SQL 查询。很多诸如 select、add、delete、modify 等操作我们都使用一种特殊的 URI 来进行这种 URI 由 3 个部分组成 “content://” 代表数据的路径和一个可选的标识数据的 ID。以下是一些示例URI content://media/internal/images 这个URI将返回设备上存储的所有图片 content://contacts/people/ 这个URI将返回设备上的所有联系人信息 content://contacts/people/45 这个URI返回单个结果联系人信息中ID为45的联系人记录尽管这种查询字符串格式很常见但是它看起来还是有点令人迷惑。为此Android提供一系列的帮助类在android.provider包下里面包含了很多以类变量形式给出的查询字符串这种方式更容易让我们理解一点参见下例: MediaStore.Images.Media.INTERNAL_CONTENT_URI Contacts.People.CONTENT_URI因此如上面 content://contacts/people/45 这个 URI 就可以写成如下形式 Uri person ContentUris.withAppendedId(People.CONTENT_URI, 45);然后执行数据查询: Cursor cur managedQuery(person, null, null, null);1.4 broadcast receiver 1. 概述 broadcast receiver 广播接收者 顾名思义就是用来接收来自系统和应用中的广播 的 系统组件。广播是一种 1对多 的通信方式即存在1个发送方若干个接收方。在 Android 系统中广播体现在方方面面例如当开机完成后系统会产生一条广播接收到这条广播就能实现开机启动服务的功能当网络状态改变时系统会产生一条广播接收到这条广播就能及时地做出提示和保存数据等操作当电池电量改变时系统会产生一条广播接收到这条广播就能在电量低时告知用户及时保存进度等等。把这种数据的传递方式的机制称之为 “广播” 。Android 系统会在特定的情景下发出各种广播例如开机、锁屏了、电量不足了、正在充电了、呼出电话了、被呼叫了…… android 广播分为两个角色 广播  发送者广播  接收者 android 广播 广播是一种跨进程的、“全设备之内” 的通信方式  1用于不同组件间的通信含应用内/不同应用之间2用于多线程通信3与 android 系统的通信 在 Android 中广播是一种广泛运用的在应用程序之间传输信息的机制。而 广播接收者 是对发送出来的广播进行过滤接受并响应的一类组件。可以使用 广播接收者 来让 应用(app) 对一个外部事件做出响应。应用(app) 可以对外部事件进行过滤只对感兴趣的外部事件(如当电话呼入时或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而它们可以启动一个 activity 或 serice 来响应它们收到的信息或者用 NotificationManager 来通知用户。通知可以用很多种方式来吸引用户的注意力例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标用户可以打开它并获取消息。 广播接收者 的类型 1Normal broadcasts默认广播。发送一个默认广播使用 Content.sendBroadcast() 方法普通广播对于接收者来说是完全异步的通常每个接收者都无需等待即可以接收到广播接收者相互之间不会有影响。对于这种广播接收者无法终止广播即无法阻止其他接收者的接收动作。2Ordered broadcasts有序广播。发送一个有序广播使用 Content.sendOrderedBroadcast() 方法有序广播比较特殊它每次只发送到优先级较高的接收者那里然后由优先级高的接收者再传播到优先级低的接收者那里优先级高的接收者有能力终止这个广播3Sticky Broadcast粘性广播。当处理完之后的 Intent依然存在直到你把它去掉。注册 广播接接收者 的两种方式 广播接收者的注册有两种方法分别是 AndroidManifest 文件中进行静态注册 和 程序动态注册。 静态注册静态注册是在 AndroidManifest.xml 配置文件中注册动态注册需要在代码中动态指定广播地址并注册通常我们是在 Activity 或 Service 注册一个广播。 静态注册 和 动态注册 的区别 动态注册广播接收者特点是当用来注册的 Activity 关掉后广播也就失效了。( 动态注册广播不是常驻型广播也就是说广播跟随 activity 的生命周期。注意在 activity 结束前移除广播接收器。 )静态注册时无需担忧广播接收者是否被关闭只要设备是开启状态广播接收者也是打开着的。也就是说哪怕 app 本身未启动该 app 订阅的广播在触发时也会对它起作用。( 静态注册是常驻型也就是说当应用程序关闭后如果有信息广播来程序也会被系统调用自动运行。 )当广播为有序广播时         1 优先级高的先接收         2 同优先级的广播接收器动态优先于静态         3 同优先级的同类广播接收器静态先扫描的优先于后扫描的动态先注册的优先于后注册的。当广播为普通广播时         1 无视优先级动态广播接收器优先于静态广播接收器         2 同优先级的同类广播接收器静态先扫描的优先于后扫描的动态先注册的优先于后注册的。广播接收者 的 创建 1构建 Intent使用 sendBroadcast 方法发出广播。2自定义一个类该 类 继承 BroadcastReceive 基类3重写抽象方法 onReceive() 方法4注册该广播接收者我们可以在代码中注册也可以在 manifest.xml 中注册。 1广播接收器收到相应广播后会自动调用 onReceive( 方法 2一般情况下onReceive方法会涉及与其他组件之间的交互如 发送 Notiotification启动 server 等 3默认情况下广播接收器运行在 UI 线程因此onReceive 方法不能执行耗时操作否则将导致 ANR public class MyReceiver1 extends BroadcastReceiver {public MyReceiver1(){}//接收Overridepublic void onReceive(Context context, Intent intent) {String infointent.getStringExtra(info);Toast.makeText(context,info,Toast.LENGTH_SHORT).show();} } 创建一个广播的步骤 创建一个类继承BroadcastReceiver并且重写其onReceive方法 public class MyBroadcastReceiver extends BroadcastReceiver {Overridepublic void onReceive(Context context, Intent intent) {Log.i(fuck,intent-action : intent.getAction());if(intent.getAction().equals(test)){Toast.makeText(context,fuck,Toast.LENGTH_LONG).show();}}}在清单文件中注册静态注册//广播接收器receiver android:name.broadcast.MyBroadcastReceiverintent-filteraction android:nameandroid.intent.action.ACTION_POWER_CONNECTED /action android:nametest///这里自定义一个广播动作/intent-filter/receiver或者动态注册 registerReceiver(new MyBroadcastReceiver(),new IntentFilter(test)); 加上权限 uses-permission android:nameandroid.permission.PROCESS_OUTGOING_CALLS/ 发送广播Intent intent new Intent(test);sendBroadcast(intent); 生命周期如果一个广播处理完 onReceive 那么系统将认定此对象将不再是一个活动的对象也就会 finished 掉它。 至此大家应该能明白 Android 的广播生命周期的原理。 注意 BroadcastReceiver 生命周期很短。如果需要在 onReceiver 完成一些耗时操作应该考虑在 Service 中开启一个新线程处理耗时操作不应该在 BroadcastReceiver 中开启一个新的线程因为 BroadcastReceiver 生命周期很短在执行完 onReceiver 以后就结束如果开启一个新的线程可能出现 BroadcastRecevier 退出以后线程还在而如果 BroadcastReceiver 所在的进程结束了该线程就会被标记为一个空线程根据 Android 的内存管理策略在系统内存紧张的时候会按照优先级结束优先级低的线程而空线程无异是优先级最低的这样就可能导致 BroadcastReceiver 启动的子线程不能执行完成。 示例 public class MyBroadcastReceiver extends BroadcastReceiver {Overridepublic void onReceive(Context context, Intent intent) {Log.i(fuck,intent-action : intent.getAction());if(intent.getAction().equals(test)){Toast.makeText(context,fuck,Toast.LENGTH_LONG).show();}}} 注册 //广播接收器receiver android:name.broadcast.MyBroadcastReceiverintent-filteraction android:nameandroid.intent.action.ACTION_POWER_CONNECTED /action android:nametest///这里自定义一个广播动作/intent-filter/receiver 广播还可以通过动态注册 registerReceiver(new MyBroadcastReceiver(),new IntentFilter(test)); 一定要加上这个权限坑 uses-permission android:nameandroid.permission.PROCESS_OUTGOING_CALLS/ 注意xml 中注册的优先级高于动态注册广播。 有序广播的创建 先创建两个接收器 public class MyReceiver3 extends BroadcastReceiver {public MyReceiver3(){}Overridepublic void onReceive(Context context, Intent intent) {Bundle data getResultExtras(false);String info data.getString(info);Toast.makeText(context,有序广播-1----info,Toast.LENGTH_SHORT).show();} }public class MyReceiver4 extends BroadcastReceiver {public MyReceiver4(){}Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context,有序广播-2,Toast.LENGTH_SHORT).show();Bundle datanew Bundle();data.putString(info,广播-2);this.setResultExtras(data);} } 粘性广播的创建 创建一个广播接收器 public class MyReceiver5 extends BroadcastReceiver { public MyReceiver5(){}Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context,接收一个粘性的广播,Toast.LENGTH_SHORT).show();} }发送一个粘性广播 public void sendStickyClick(View v){Intent intentnew Intent(com.example.action.MY_BROADCAST3);this.sendStickyBroadcast(intent);}粘性广播必须要设置权限在清单文件中加入 uses-permission android:nameandroid.permission.BROADCAST_STICKY /2. 发送广播 调用 Context对象 的 sendBroadcast(Intent) 即可发送广播在参数 Intent 对象中应该调用 setAction() 方法配置广播的“频道号”即是相当于收音机要接收某个电台的频段只有注册了相同的 Action 的广播接收者才可以接收到该广播。 广播的发送 广播发送者 将此广播的 意图intent通过 sendBroasdcast() 方法发送出去 发送广播 Intent intent new Intent(test); sendBroadcast(intent); 广播的类型 普通广播系统广播有序广播粘性广播App 应用内广播 特别注意 对于不同注册方式的广播接收器回调 OnReceiveContext contextIntent intent中的context返回值是不一样的 对于静态注册全局应用内广播回调onReceive(context, intent)中的context返回值是ReceiverRestrictedContext对于全局广播的动态注册回调onReceive(context, intent)中的context返回值是ActivityContext对于应用内广播的动态注册LocalBroadcastManager方式回调onReceive(context, intent)中的context返回值是Application Context。对于应用内广播的动态注册非LocalBroadcastManager方式回调onReceive(context, intent)中的context返回值是Activity Context发送广播 Context.sendBroadcast() 发送的是普通广播所有订阅者都有机会获得并进行处理。 发送一个普通的广播 public void sendNormalClick(View v){Intent intentnew Intent(com.example.action.MY_BROADCAST);intent.putExtra(info,姑娘约否);this.sendBroadcast(intent);} 注册文件中添加对应的 action 发送一个有序广播 public void sendOrderClick(View v){Intent intentnew Intent(com.example.action.MY_BROADCAST2);//参数:intent ,接收权限this.sendOrderedBroadcast(intent,null);} 配置 其中 priority 表示优先级属性范围-1000,1000数值越大优先级越高。 中断有序广播 this.abortBroadcast();同级别接收先后是随机的再到级别低的收到广播如果先接收到的把广播截断了同级别以外的接收者是无法收到该广播的。 在这个方法发来的广播中代码注册方式中收到广播先后次序为注明优先级的、代码注册的、没有优先级的如果都没有优先级代码注册收到为最先。 发送有序广播 Context.sendOrderedBroadcast() 发送的是有序广播系统会根据接收者声明的优先级别按顺序逐个执行接收者 前面的接收者有权终止广播(BroadcastReceiver.abortBroadcast()) 如果广播被前面的接收者终止后面的接收者就再也无法获取到广播。 对于有序广播前面的接收者可以将处理结果通过setResultExtras(Bundle)方法存放进结果对象 然后传给下一个接收者通过代码Bundle bundle getResultExtras(true))可以获取 上一个接收者存入在结果对象中的数据。系统收到短信发出的广播属于有序广播。如果想阻止用户收到短信可以通过设置优先级 让你们自定义的接收者先获取到广播然后终止广播这样用户就接收不到短信了。 3. 接收广播 自定义类继承自 android.content.BroadcastReceiver 后需要注册注册时使用 IntentFilter 配置与发送方相同的 Action重写onReceiver() 方法实现对广播的处理。 public class MyBroadcastReceiver extends BroadcastReceiver {private static final String TAG MyBroadcastReceiver;Overridepublic void onReceive(Context context, Intent intent) {StringBuilder sb new StringBuilder();sb.append(Action: intent.getAction() \n);sb.append(URI: intent.toUri(Intent.URI_INTENT_SCHEME).toString() \n);String log sb.toString();Log.d(TAG, log);Toast.makeText(context, log, Toast.LENGTH_LONG).show();} } 4. 注册广播接收者              注册广播接收者的两种方式 静态注册静态注册是在 AndroidManifest.xml 文件中配置。动态注册动态注册需要在代码中动态的指定广播地址并注册通常是在 Activity 或 Service 注册一个广播。 例如 MyReceiver receivernew MyReceiver(); IntentFilter filternew IntentFilter(); filter.addAction(android.intent.action.MY_BROADCAST); registerReceiver(receiver,filter); 解除注册 unregisterReceiver(receiver); 静态注册 静态注册在清单文件AndroidManifest.xml中在application节点下使用receiver节点进行注册。这种方式注册的广播接收者必须是一个单独的只实现BroadcastReceiver的类不能是内部类。且这样的广播接收者是常驻型的即从APP安装到手机上的那一刻即开始处于接收广播状态且直至该APP被从手机移除。 receiver android:name.MyBroadcastReceiver android:exportedtrueintent-filteraction android:nameandroid.intent.action.BOOT_COMPLETED/action android:nameandroid.intent.action.INPUT_METHOD_CHANGED //intent-filter /receiver 注册方式在 AndroidManifest.xml 里通过receive 标签声明属性说明 receiverandroid:enabletrue/false//此broadcastReceiver 是否接受其他应用发出的广播//默认值时由receiver 中d有无inter-filter决定如果有默认true否则默认falseandroid:exportedtrue/falseandroid:icondrawable resourceandroid:labelstring resource//继承BroadcastReceiver子类的类名android:name.mBroadcastReceiver //具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收android:permissionstring //BroadcastReceiver运行所处的进程 //默认为app的进程可以指定独立的进程 //注Android四大基本组件都可以通过此属性指定自己的独立进程android:processstring //用于指定此广播接收器将接收的广播类型 //本示例中给出的是用于接收网络状态改变时发出的广播intent-filteraction android:nameandroid.net.conn.CONNECTIVITY_CHANGE //intent-filter/receiver注册示例 receiver //此广播接收者类是mBroadcastReceiverandroid:name.mBroadcastReceiver //用于接收网络状态改变时发出的广播intent-filteraction android:nameandroid.net.conn.CONNECTIVITY_CHANGE //intent-filter /receiver当此 APP 首次启动时系统会自动实例化 mBroadcastReceiver 类并注册到系统中。 动态注册 动态注册在程序中调用 Context对象 的 registerReceiver(BroadcastReceiver, IntentFilter) 方法进行注册。这种方式可以注册以内部类的形式存在的广播接收者且这种方式的广播接收者仅当注册后才开始接收广播并且在调用了Context对象的unregisterReceiver(BroadcastReceiver) 方法后就会停止接收广播。 BroadcastReceiver br new MyBroadcastReceiver(); IntentFilter filter new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); this.registerReceiver(br, filter); 注册方式在代码中调用 Context.registerReceiver() 方法具体代码如下// 1. 实例化BroadcastReceiver子类 IntentFiltermBroadcastReceiver mBroadcastReceiver new mBroadcastReceiver();IntentFilter intentFilter new IntentFilter();// 2. 设置接收广播的类型intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);// 3. 动态注册调用Context的registerReceiver方法registerReceiver(mBroadcastReceiver, intentFilter);//动态注册广播后需要在相应位置记得销毁广播unregisterReceiver(mBroadcastReceiver);特别注意 动态广播最好在 onResume中注册 onPause 注销 原因 1对于动态广播有注册必然得有注销否则会导致内存泄漏2onPause在App死亡前一定会被执行从而保证app死亡前一定会被注销从而防止内存泄漏 两种注册方式的区别 5. 无序广播 与 有序广播 1. 普通广播 即为 无序广播谁都可以接收并不会相互打扰。普通广播是完全异步的可以在同一时刻逻辑上被所有接收者接收到消息传递的效率比较高但缺点是接收者不能将处理结果传递给下一个接收者并且无法终止广播 Intent 的传播2. 有序广播调用 sendOrderedBroadcast(Intent, String permission)方法发送的广播各广播接收者在接收广播时会存在一定的先后顺序即某接收者会先收到广播其他接收者后收到广播广播会在各接收者之间按照一定的先后顺序进行传递。在广播的传递过程中先接收到广播的接收者可以对广播进行拦截或篡改。 有序广播是按照接收者声明的优先级别声明在 intent-filter 元素的 android:priority 属性中数越大优先级别越高取值范围-1000 到 1000。也可以调用IntentFilter 对象的 setPriority() 进行设置被接收者依次接收广播。如A 的级别高于 BB 的级别高于 C那么广播先传给A再传给B最后传给C。A 得到广播后可以往广播里存入数据当广播传给 B 时B可以从广播中得到 A 存入的数据。 6. 有序广播的接收者们的优先级 有序广播的接收者们的优先级用于确定接收的先后顺序优先级越高的接收者将更优先接收到广播反之则更靠后接收到广播。 1. 注册广播时在广播对应的 IntentFilter 中的 priority 属性直接决定优先级该属性值为 int 类型的数值取值越大则优先级越高2. 如果存在多个广播接收者配置的 priority 属性值相同则动态注册的广播接收者的优先级高于静态注册的广播接收者。3. 如果根据以上 2 条规则都无法确定优先级则根据注册的先后顺序确定各接收者们的优先级。7. 有序广播的拦截或篡改 1. 【拦截】在广播接收者中使用abortBroadcast()方法可以终止有序广播向后继续传递即后续的接收者们将无法接收到该广播。注意该方法只能在接收有序广播时调用2. 【篡改】在广播接收者中调用setResult()方法可以向广播中添加数据并在后续的接收者中可以通过getResult()获取这些数据同时后续的接收者也可以再次调用setResult()方法重新向广播中写入数据即覆盖原有的数据以实现篡改。小 结 在Android 中如果要发送一个广播必须使用sendBroadCast 向系统发送对其感兴趣的广播接收器中。使用广播必须要有一个intent 对象必设置其action动作对象使用广播必须在配置文件中显式的指明该广播对象每次接收广播都会重新生成一个接收广播的对象在BroadCastReceiver中尽量不要处理太多逻辑问题建议复杂的逻辑交给Activity 或者 Service 去处理如果在AndroidManifest.xml中注册当应用程序关闭的时候也会接收到广播。在应用程序中注册就不产生这种情况了。 注意 当如果要进行的操作需要花费比较长的时间则不适合放在BroadcastReceiver中进行处理。 引用网上找到的一段解释 在 Android 中程序的响应 Responsive 被活动管理器 Activity Manager 和窗口管理器 Window Manager 这两个系统服务所监视。当 BroadcastReceiver 在 10 秒内没有执行完毕Android 会认为该程序无响应。所以在 BroadcastReceiver 里不能做一些比较耗时的操作否侧会弹出ANR Application No Response 的对话框。如果需要完成一项比较耗时的工作应该通过发送Intent 给 Service 由 Service 来完成。而不是使用子线程的方法来解决因为 BroadcastReceiver 的生命周期很短在 onReceive() 执行后 BroadcastReceiver 的实例就会被销毁子线程可能还没有结束BroadcastReceiver 就先结束了。如果 BroadcastReceiver 结束了它的宿主进程还在运行那么子线程还会继续执行。但宿主进程此时很容易在系统需要内存时被优先杀死因为它属于空进程没有任何活动组件的进程。 1.5 Android 四大组件总结 Activity是整个应用程序的门面主要负责应用程序当中数据的展示是各种各样控件的容器是用户和应用程序之间交互的接口Service在前台不可见但是承担大部分数据处理工作它和 Activity 的地位是并列的区别在于 Activity 运行于前台Service 运行于后台没有图形用户界面通常他为其他的组件提供后台服务或监控其他组件的运行状态BroadcastReceiver实现消息的异步接收他非常类似事件编程中的监听器但他与普通事件监听器有所不同普通的事件监听器监听的事件源是程序中的控件而 BroadcastReceiver 监听的事件源是 Android 应用中其他的组件ContentProvider为不同的应用程序之间数据访问提供统一的访问接口通常它与ContentResolver结合使用一个是应用程序使用 ContentProvider 来暴露自己的数据而另外一个是应用程序通过 ContentResolver 来访问数据14大组件的注册 4大基本组件都需要注册才能使用每个Activity、service、Content Provider都需要在AndroidManifest文件中进行配置。AndroidManifest文件中未进行声明的activity、服务以及内容提供者将不为系统所见从而也就不可用。而broadcast receiver广播接收者的注册分静态注册在AndroidManifest文件中进行配置和通过代码动态创建并以调用Context.registerReceiver()的方式注册至系统。需要注意的是在AndroidManifest文件中进行配置的广播接收者会随系统的启动而一直处于活跃状态只要接收到感兴趣的广播就会触发即使程序未运行。 24大组件的激活 内容提供者的激活当接收到ContentResolver发出的请求后内容提供者被激活。而其它三种组件activity、服务和广播接收器被一种叫做intent的异步消息所激活。 34大组件的关闭 内容提供者仅在响应ContentResolver提出请求的时候激活。而一个广播接收器仅在响应广播信息的时候激活。所以没有必要去显式的关闭这些组件。Activity关闭可以通过调用它的finish()方法来关闭一个activity。服务关闭对于通过startService()方法启动的服务要调用Context.stopService()方法关闭服务使用bindService()方法启动的服务要调用Contex.unbindService()方法关闭服务。 4android中的任务activity栈 a任务其实就是activity的栈它由一个或多个Activity组成共同完成一个完整的用户体验。栈底的是启动整个任务的Activity栈顶的是当前运行的用户可以交互的Activity当一个activity启动另外一个的时候新的activity就被压入栈并成为当前运行的activity。而前一个activity仍保持在栈之中。当用户按下BACK键的时候当前activity出栈而前一个恢复为当前运行的activity。栈中保存的其实是对象栈中的Activity永远不会重排只会压入或弹出。b任务中的所有activity是作为一个整体进行移动的。整个的任务即activity栈可以移到前台或退至后台。cAndroid系统是一个多任务(Multi-Task)的操作系统可以在用手机听音乐的同时也执行其他多个程序。每多执行一个应用程序就会多耗费一些系统内存当同时执行的程序过多或是关闭的程序没有正确释放掉内存系统就会觉得越来越慢甚至不稳定。为了解决这个问题Android引入了一个新的机制即生命周期(Life Cycle)。二、六大布局 2.1 声明 Android 程序布局有两种方式 1) 使用 XML 文件描述界面布局2) 在 Java 代码中通过调用方法进行控制。 既可以使用任何一种声明界面布局的方式也可以同时使用两种方式。 使用XML文件声明有以下3个特点 1) 将程序的表现层和控制层分离2) 在后期修改用户界面时无须更改程序的源程序3) 可通过WYSIWYG可视化工具直接看到所设计的用户界面有利于加快界面设计的过程。 建议尽量采用 XML 文件声明界面元素布局。在程序运行时动态添加界面布局会大大降低应用响应速度但依然可以在必要时动态改变屏幕内容。 2.2 Android 六大界面布局方式 Android 六大界面布局方式包括  线性布局 (LinearLayout)框架布局 (FrameLayout)表格布局 (TableLayout)相对布局 (RelativeLayout)绝对布局 (AbsoluteLayout)网格布局 (GridLayout) 1. LinearLayout 线性布局 LinearLayout容器中的组件一个挨一个排列通过控制android:orientation属性可控制各组件是横向排列还是纵向排列。 线性布局如名字所描述的那样这个布局将它所包含的控件在线性方向上一次排列方向分为 水平方向和数值方向。 属性 android:orientation “vertical” | “horizontal” 竖直或水平默认水平 属性 android:layout_gravity “top” | “center” | “bottom” 内部的布局方式 属性 android:gravity “top”|center”|“bottom” 相对于父容器的对齐方式 属性 android:layout_weidht 使用比例方式执行控件的大小在手机屏幕适配方面起到非常重要的作用 LinearLayout 的常用XML属性及相关方法 XML属性 相关方法 说明 android:gravity setGravity(int) 设置布局管理器内组件的对齐方式 android:orientation setOrientation(int) 设置布局管理器内组件的排列方式可以设置为horizontal、vertical两个值之一 其中gravity属性支持top, left, right, center_vertical, fill_vertical,center_horizontal, fill_horizontal, center, fill, clip_vertical,clip_horizontal。也可以同时指定多种对齐方式的组合。 LinearLayout子元素支持的常用XML属性及方法 XML属性 说明 android:layout_gravity 指定该子元素在LinearLayout中的对齐方式 android:layout_weight 指定子元素在LinearLayout中所占的权重 2. TableLayout 表格布局 表格布局与HTML中的table td tr标签类似 tabletrtd/td/tr /table 如何确定行与列 如果在TableLayout下添加组件这个组件会占满整行如果想把多个组件放在同一行需要添加TableRow的容器然后把组件放进去TableRow中的组件个数决定的该行的列数而列的宽度由列中最宽的单元格决定TableRow嗯layout_width属性默认是fill-parent修改无效。但是layout_height默认是wrapcontent,可以修改整个表格的宽度取决于父容器的宽度占满父容器 重要的属性 android:collapaseColumns设置需要被隐藏的列的序号android:shrinkColumns设置允许被收缩的列的序号android:stretchCoumns设置运行被拉伸嗯列的序号 这三个属性都是从0开始算的 shrinkColumns 2 //对应第三行 shrinkColumns 0,2 //设置多个都生效 shrinkColumns //所有列都生效android:layout_column“2”: 表示跳过第二个直接显示第三个从1开始android:layout_span“4”:表示合并*4个单元格,也就说这个组件占4个单元格 TableLayout 继承自 Linearout本质上仍然是线性布局管理器。表格布局采用行、列的形式来管理 UI 组件并不需要明确地声明包含多少行、多少列而是通过添加 TableRow、其他组件来控制表格的行数和列数。 每向 TableLayout 中添加一个 TableRow 就代表一行 每向 TableRow 中添加一个一个子组件就表示一列 如果直接向 TableLayout 添加组件那么该组件将直接占用一行 在表格布局中可以为单元格设置如下三种行为方式 Shrinkable该列的所有单元格的宽度可以被收缩以保证该表格能适应父容器的宽度Strentchable该列所有单元格的宽度可以被拉伸以保证组件能完全填满表格空余空间Collapsed如果该列被设置为 Collapsed那么该列的所有单元格会被隐藏 TableLayout 的常用 XML 属性及方法 XML属性 相关方法 说明 android:collapseColumns setColumns(int, boolean) 设置需要被隐藏的列的序号多个序号间用逗号分隔 android:shrinkColumns setShrinkAllColumns(boolean) 设置需要被收缩的列的序号 android:stretchColumns setStretchAllColumns(boolean) 设置允许被拉伸的列的序号 3. FrameLayout 帧布局 FrameLayout直接继承自ViewGroup组件。帧布局为每个加入其中的组件创建一个空白的区域(称为一帧)每个子组件占据一帧这些帧会根据gravity属性执行自动对齐。 FrameLayout的常用XM了属性及方法 XML属性 相关方法 说明 android:foreground setForeground(Drawable) 设置该帧布局容器的前景图像 android:foregroundGravity setForeGroundGraity(int) 定义绘制前景图像的gravity属性 4. RelativeLayout 相对布局 RelativeLayout 的 XML 属性及相关方法说明 XML属性 相关方法 说明 android:gravity setGravity(int)   android:ignoreGravity setIgnoreGravity(int) 设置哪个组件不受gravity属性的影响 为了控制该布局容器的各子组件的布局分布RelativeLayout提供了一个内部类RelativeLayout.LayoutParams。 RelativeLayout.LayoutParams里只能设为boolean的XML属性 XML属性 说明 android:layout_centerHorizontal 设置该子组件是否位于布局容器的水平居中 android:layout_centerVertical   android:layout_centerParent   android:layout_alignParentBottom   android:layout_alignParentLeft   android:layout_alignParentRight   android:layout_alignParentTop   RelativeLayout.LayoutParams里属性值为其他UI组件ID的XML属性 XML属性 说明 android:layout_toRightOf 控制该子组件位于给出ID组件的右侧 android:layout_toLeftOf   android:layout_above   android:layout_below   android:layout_alignTop   android:layout_alignBottom   android:layout_alignRight   android:layout_alignLeft  5. Android 4.0 新增的网格布局 GridLayout GridLayout是Android4.0增加的网格布局控件与之前的TableLayout有些相似它把整个容器划分为rows × columns个网格每个网格可以放置一个组件。性能及功能都要比tablelayout好比如GridLayout布局中的单元格可以跨越多行而tablelayout则不行此外其渲染速度也比tablelayout要快。 GridLayout提供了setRowCount(int)和setColumnCount(int)方法来控制该网格的行和列的数量。 GridLayout常用的XML属性和方法说明 XML属性 相关方法 说明 android:alignmentMode setAlignmentMode(int) 设置该布局管理器采用的对齐模式 android:columnCount setColumnCount(int) 设置该网格的列数量 android:columnOrderPreserved setColumnOrderPreserved(boolean) 设置该网格容器是否保留序列号 android:roeCount setRowCount(int) 设置该网格的行数量 android:rowOrderPreserved setRowOrderPreserved(boolean) 设置该网格容器是否保留行序号 android:useDefaultMargins setUseDefaultMargins(boolean) 设置该布局管理器是否使用默认的页边距 为了控制GridLayout布局容器中各子组件的布局分布GridLayout提供了一个内部类GridLayout.LayoutParams来控制Gridlayout布局容器中子组件的布局分布。 GridLayout.LayoutParams常用的XML属性和方法说明 XML属性 说明 android:layout_column 设置该组件在GridLayout的第几列 android:layout_columnSpan 设置该子组件在GridLayout横向上跨几列 android:layout_gravity 设置该子组件采用何种方式占据该网格的空间 android:layout_row 设置该子组件在GridLayout的第几行 android:layout_rowSpan 设置该子组件在GridLayout纵向上跨几行 6. AbsoluteLayout 绝对布局 即 Android 不提供任何布局控制而是由开发人员自己通过 X 坐标、Y 坐标 来控制组件的位置。每个组件都可指定如下两个 XML 属性 layour_x;layout_y; 绝对布局已经过时不应使用或少使用。 界面布局类型的选择和性能优化 首先得明确界面布局类型的嵌套越多越深越复杂会使布局实例化变慢使 Activity 的展开时间延长。建议尽量减少布局嵌套尽量减少创建View 对象的数量。 1 . 减少布局层次可考虑用 RelativeLayout 来代替 LinearLayout。通过 Relative 的相对其他元素的位置来布局可减少块状嵌套 2 . 另一种减少布局层次的技巧是使用 merge / 标签来合并布局 3 . 重用布局。Android 支持在 XML 中使用 include / 标签 include / 通过指定 android:layout 属性来指定要包含的另一个XML布局。 如 includeandroid:idid/id1android:layoutlayout/mylayout includeandroid:idid/id2android:layoutlayout/mylayout includeandroid:idid/id3android:layoutlayout/mylayout 三、五大存储 在 Android 中可供选择的存储方式有 SharedPreferences、文件存储、SQLite数据库方式、内容提供器Content provider和网络。 一. SharedPreferences 方式 Android 提供用来存储一些简单的配置信息的一种机制例如一些默认欢迎语、登录的用户名和密码等。其以键值对的方式存储 使得我们可以很方便的读取和存入. 1程序要实现的功能        我们在Name文本框中输入wangwu在Password文本框中输入123456然后退出这个应用。我们在应用程序列表中找到这个应用重新启动可以看到其使用了前面输入的Name和Password 2)实现的代码 布局 ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:orientationverticalandroid:layout_widthfill_parentandroid:layout_heightfill_parentTextView android:layout_widthfill_parent android:layout_heightwrap_content android:textSharedPreferences demo/TextViewandroid:layout_widthfill_parentandroid:layout_heightwrap_contentandroid:textname/TextViewEditTextandroid:idid/nameandroid:layout_widthfill_parentandroid:layout_heightwrap_contentandroid:text/EditTextTextViewandroid:layout_widthfill_parentandroid:layout_heightwrap_contentandroid:textpassword/TextViewEditTextandroid:idid/passwordandroid:layout_widthfill_parentandroid:layout_heightwrap_contentandroid:passwordtrueandroid:text/EditText /LinearLayout主要实现代码 package com.demo;import android.app.Activity; import android.content.SharedPreferences; import android.os.Bundle; import android.widget.EditText;public class SharedPreferencesDemo extends Activity {public static final String SETTING_INFOS SETTING_Infos; public static final String NAME NAME; public static final String PASSWORD PASSWORD; private EditText field_name; //接收用户名的组件private EditText filed_pass; //接收密码的组件Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);//Find VIew field_name (EditText) findViewById(R.id.name); //首先获取用来输入用户名的组件filed_pass (EditText) findViewById(R.id.password); //同时也需要获取输入密码的组件// Restore preferencesSharedPreferences settings getSharedPreferences(SETTING_INFOS, 0); //获取一个SharedPreferences对象String name settings.getString(NAME, ); //取出保存的NAMEString password settings.getString(PASSWORD, ); //取出保存的PASSWORD//Set valuefield_name.setText(name); //将取出来的用户名赋予field_namefiled_pass.setText(password); //将取出来的密码赋予filed_pass}Override protected void onStop(){ super.onStop(); SharedPreferences settings getSharedPreferences(SETTING_INFOS, 0); //首先获取一个SharedPreferences对象settings.edit() .putString(NAME, field_name.getText().toString()) .putString(PASSWORD, filed_pass.getText().toString()) .commit(); } //将用户名和密码保存进去} SharedPreferences 保存到哪里去了 SharedPreferences 是以 XML 的格式以文件的方式自动保存的在 DDMS中的File Explorer中展开到/data/data/package name/shared_prefs下以上面这个为例可以看到一个叫做SETTING_Infos.xml的文件 注意Preferences只能在同一个包内使用不能在不同的包之间使用。 SharedPreferences 是使用键值对的方式进行存储数据的。 想要使用SharedPreferences 来存储数据首先主要获取到SharedPreferences 对象。Android提供了三种方法用于获取SharedPreferences对象 1Context类中的getSharedPreferences(方法 //此方法接收两个参数一个参数用于指定SharedPreferences文件的名称 //如果指定的文件不存在则会创建一个 //SharedPreferences文件都是存放在/data/data/package name/shared_prefs/目录下 //第二个参数用于指定操作模式目前只有MODE_PRIVATE这种模式和直接传入0效果相同 SharedPreferences.Editor editor getSharedPreferences(data,MODE_PRIVATE).edit(); editor.putString(name, Tom); editor.putInt(age,13); editor.putBoolean(married,false); editor.apply();2Activity类中的getPreferences()方法 //这个方法和Context中的getSharedPreferences()方法很类似不过它只接收一个操作模式 //因为使用这个方法时会自动将当前活动的类名作为SharedPreferences的文件名3PreferencesManager类中的getDefaultSharedPreferences()方法 // 这是一个静态方法它接收一个Context参数 // 并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件得到了SharedPreferences对象后 就可以开始想SharedPreferences文件中存储数据了主要可以分为三步 1调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象 2向SharedPreferences.Editor 对象中添加数据比如添加一个布尔值可以使用putBoolean() 方法 3调用apply()方法的添加的数据提交从而完成数据存储操作 SharedPreferences中读取数据 SharedPreferences pref getSharedPreferences(data,MODE_PRIVATE ); String name pref.getString(name,); int age pref.getInt(age,0); boolean married pref.getBoolean(married, false);二. 文件存储方式 在 Android 中其提供了openFileInput 和 openFileOuput 方法读取设备上的文件下面看个例子代码具体如下所示  StringFILE_NAME tempfile.tmp;  //确定要操作文件的文件名 FileOutputStream fos openFileOutput(FILE_NAME, Context.MODE_PRIVATE); //初始化 FileInputStream fis openFileInput(FILE_NAME); //创建写入流 上述代码中两个方法只支持读取该应用目录下的文件读取非其自身目录下的文件将会抛出异常。需要提醒的是如果调用FileOutputStream 时指定的文件不存在Android 会自动创建它。另外在默认情况下写入的时候会覆盖原文件内容如果想把新写入的内容附加到原文件内容后则可以指定其模式为Context.MODE_APPEND。 三. SQLite数据库方式 SQLite 是 Android 所带的一个标准的数据库它支持SQL语句它是一个轻量级的嵌入式数据库。 1实现的功能 在这个例子里边我们在程序的主界面有一些按钮通过这些按钮可以对数据库进行标准的增、删、改、查。 2实现代码 所用到的字符文件 ?xml version1.0 encodingutf-8? resourcesstring nameapp_nameSQLite数据库操作实例/stringstring namebutton1建立数据库表/stringstring namebutton2删除数据库表/stringstring namebutton3插入两条记录/stringstring namebutton4删除一条记录/stringstring namebutton5查看数据库表/string /resources布局代码 ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:orientationverticalandroid:layout_widthfill_parentandroid:layout_heightfill_parentTextView android:layout_widthfill_parent android:layout_heightwrap_content android:textstring/app_name/Button android:textstring/button1 android:idid/button1 android:layout_widthwrap_content android:layout_heightwrap_content/ButtonButton android:textstring/button2 android:idid/button2 android:layout_widthwrap_content android:layout_heightwrap_content/ButtonButton android:textstring/button3 android:idid/button3 android:layout_widthwrap_content android:layout_heightwrap_content/ButtonButton android:textstring/button4 android:idid/button4 android:layout_widthwrap_content android:layout_heightwrap_content/ButtonButton android:textstring/button5 android:idid/button5 android:layout_widthwrap_content android:layout_heightwrap_content/Button/LinearLayout主要代码 package com.sqlite;import android.app.Activity; import android.content.Context; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button;/** 什么是SQLiteDatabase* 一个SQLiteDatabase的实例代表了一个SQLite的数据库通过SQLiteDatabase实例的一些方法我们可以执行SQL语句* 对数据库进行增、删、查、改的操作。需要注意的是数据库对于一个应用来说是私有的并且在一个应用当中数据库的名字也是惟一的。 *//** 什么是SQLiteOpenHelper * 这个类主要生成一个数据库并对数据库的版本进行管理。* 当在程序当中调用这个类的方法getWritableDatabase()或者getReadableDatabase()方法的时候如果当时没有数据那么Android系统就会自动生成一个数据库。* SQLiteOpenHelper 是一个抽象类我们通常需要继承它并且实现里边的3个函数* onCreateSQLiteDatabase在数据库第一次生成的时候会调用这个方法一般我们在这个方法里边生成数据库表。* onUpgradeSQLiteDatabase, int, int当数据库需要升级的时候Android系统会主动的调用这个方法。一般我们在这个方法里边删除数据表并建立新的数据表当然是否还需要做其他的操作完全取决于应用的需求。* onOpenSQLiteDatabase这是当打开数据库时的回调函数一般也不会用到。 */public class SQLiteDemo extends Activity {OnClickListener listener1 null;OnClickListener listener2 null;OnClickListener listener3 null;OnClickListener listener4 null;OnClickListener listener5 null;Button button1;Button button2;Button button3;Button button4;Button button5;DatabaseHelper mOpenHelper;private static final String DATABASE_NAME dbForTest.db;private static final int DATABASE_VERSION 1;private static final String TABLE_NAME diary;private static final String TITLE title;private static final String BODY body;//建立一个内部类,主要生成一个数据库private static class DatabaseHelper extends SQLiteOpenHelper {DatabaseHelper(Context context) {super(context, DATABASE_NAME, null, DATABASE_VERSION);}//在数据库第一次生成的时候会调用这个方法一般我们在这个方法里边生成数据库表。Overridepublic void onCreate(SQLiteDatabase db) {String sql CREATE TABLE TABLE_NAME ( TITLE text not null, BODY text not null );;Log.i(haiyang:createDB, sql);db.execSQL(sql);}Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}}Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);prepareListener();initLayout();mOpenHelper new DatabaseHelper(this);}private void initLayout() {button1 (Button) findViewById(R.id.button1);button1.setOnClickListener(listener1);button2 (Button) findViewById(R.id.button2);button2.setOnClickListener(listener2);button3 (Button) findViewById(R.id.button3);button3.setOnClickListener(listener3);button4 (Button) findViewById(R.id.button4);button4.setOnClickListener(listener4);button5 (Button) findViewById(R.id.button5);button5.setOnClickListener(listener5);}private void prepareListener() {listener1 new OnClickListener() {public void onClick(View v) {CreateTable();}};listener2 new OnClickListener() {public void onClick(View v) {dropTable();}};listener3 new OnClickListener() {public void onClick(View v) {insertItem();}};listener4 new OnClickListener() {public void onClick(View v) {deleteItem();}};listener5 new OnClickListener() {public void onClick(View v) {showItems();}};}/** 重新建立数据表*/private void CreateTable() {//mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库如果这个数据库还没有建立//那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立那么直接返回一个可写的数据库。SQLiteDatabase db mOpenHelper.getWritableDatabase();String sql CREATE TABLE TABLE_NAME ( TITLE text not null, BODY text not null );;Log.i(haiyang:createDB, sql);try {db.execSQL(DROP TABLE IF EXISTS diary);db.execSQL(sql);setTitle(数据表成功重建);} catch (SQLException e) {setTitle(数据表重建错误);}}/** 删除数据表*/private void dropTable() {//mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库如果这个数据库还没有建立//那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立那么直接返回一个可写的数据库。SQLiteDatabase db mOpenHelper.getWritableDatabase();String sql drop table TABLE_NAME;try {db.execSQL(sql);setTitle(数据表成功删除 sql);} catch (SQLException e) {setTitle(数据表删除错误);}}/** 插入两条数据*/private void insertItem() {//mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库如果这个数据库还没有建立//那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立那么直接返回一个可写的数据库。SQLiteDatabase db mOpenHelper.getWritableDatabase();String sql1 insert into TABLE_NAME ( TITLE , BODY ) values(haiyang, android的发展真是迅速啊);;String sql2 insert into TABLE_NAME ( TITLE , BODY ) values(icesky, android的发展真是迅速啊);;try {// Log.i会将参数内容打印到日志当中并且打印级别是Info级别// Android支持5种打印级别分别是Verbose、Debug、Info、Warning、Error当然我们在程序当中一般用到的是Info级别Log.i(haiyang:sql1, sql1);Log.i(haiyang:sql2, sql2);db.execSQL(sql1);db.execSQL(sql2);setTitle(插入两条数据成功);} catch (SQLException e) {setTitle(插入两条数据失败);}}/** 删除其中的一条数据*/private void deleteItem() {try {//mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库如果这个数据库还没有建立//那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立那么直接返回一个可写的数据库。SQLiteDatabase db mOpenHelper.getWritableDatabase();//第一个参数是数据库表名在这里是TABLE_NAME也就是diary。 //第二个参数相当于SQL语句当中的where部分也就是描述了删除的条件。//如果在第二个参数当中有“”符号那么第三个参数中的字符串会依次替换在第二个参数当中出现的“”符号。 db.delete(TABLE_NAME, title haiyang, null);setTitle(删除title为haiyang的一条记录);} catch (SQLException e) {}}/** 在屏幕的title区域显示当前数据表当中的数据的条数。*//** Cursor cur db.query(TABLE_NAME, col, null, null, null, null, null)语句将查询到的数据放到一个Cursor 当中。这个Cursor里边封装了这个数据表TABLE_NAME当中的所有条列。 query()方法相当的有用在这里我们简单地讲一下。第一个参数是数据库里边表的名字比如在我们这个例子表的名字就是TABLE_NAME也就是diary。第二个字段是我们想要返回数据包含的列的信息。在这个例子当中我们想要得到的列有title、body。我们把这两个列的名字放到字符串数组里边来。第三个参数为selection相当于SQL语句的where部分如果想返回所有的数据那么就直接置为null。第四个参数为selectionArgs。在selection部分你有可能用到“?”那么在selectionArgs定义的字符串会代替selection中的“?”。第五个参数为groupBy。定义查询出来的数据是否分组如果为null则说明不用分组。第六个参数为having 相当于SQL语句当中的having部分。第七个参数为orderBy来描述我们期望的返回值是否需要排序如果设置为null则说明不需要排序。*/private void showItems() {SQLiteDatabase db mOpenHelper.getReadableDatabase();String col[] { TITLE, BODY };//查询数据Cursor cur db.query(TABLE_NAME, col, null, null, null, null, null);Integer num cur.getCount();setTitle(Integer.toString(num) 条记录);} } Android 为了让我们能够更加方便的管理数据库专门提供了一个SQLiteOpenHelper 帮助类借助这个类可以非常简单的将数据库进行创建好升级。 SQLiteOpenHelper 中有两个非常重要的实例方法getReadableDatabase() 和 getWritableDatabase() 。这两个方法可以创建或者打开一个现有的数据库如果数据库存在则直接打开否则创建一个新的数据库并返回一个可对数据库进行读写操作的对象。不同的是当数据库不可写入如磁盘空间已满getReadableDatabase方法返回的对象将以只读的方式打开数据库而getWeitableDatabase则出现异常 例子在指定路径下创建数据库文件 .db public class MainActivity extends Activity {public static final String PATH_ONE KogBill;public static final String PATH_NAME KogBill.db;private SQLiteDatabase db; //声明SQLiteDatabase 该对象可以操作数据库String path Environment.getExternalStorageDirectory().getAbsolutePath();String path1 path File.separator PATH_ONE; //需要创建的路径String path2 path File.separator PATH_ONE File.separator PATH_NAME; //需要创建的文件Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);File f new File(path1);if( !f.exists()){ //创建数据库文件路径f.mkdirs();} //实例化MySQLiteHelper 创建指定目录下数据库文件并创建表MySQLiteHelper mSQL new MySQLiteHelper(MainActivity.this, path2);db mSQL.getWritableDatabase();}class MySQLiteHelper extends SQLiteOpenHelper{private static final int DATABASE_VERSION 1;//数据库版本号private static final String CREATE_TABLE create table kog_bill ( _id integer primary key autoincrement, date text, breakfast text, lunch text, dinner text, happy text, other text, spare text);//方便创建实例简化构造方法方法内调用4参数构造方法//参数 name 可以是 数据库名称也可以数据库文件路径即可以指定数据库文件路径public MySQLiteHelper(Context context, String name) {this(context, name, null, DATABASE_VERSION);}//必须要实现的方法public MySQLiteHelper(Context context, String name, CursorFactory factory, int version) {super(context, name, factory, version);}Overridepublic void onCreate(SQLiteDatabase db) {// 第一次创建数据库时 才会调用Log.e(mylog, 创建数据库表);db.execSQL(CREATE_TABLE);}Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}} }根据上述代码便获得db对象通过dbSQLiteDatabase可进行数据库的操作如 db.query() db.delete() 如果我们想在创建一个数据库表参照上述代码可以在SQLiteOpenHelper的onCreate方法中加入语句: Override public void onCreate(SQLiteDatebase db) {db.execSQL(CREATE_TABLE);db.execSQL(CREATE_BOOK); //新创建一个数据库表 }然后重新运行一下发现并没有创建成功因为KogBill.db数据库已经存在所以MySQLiteHelper 中的onCreate方法都不会执行解决这个办法的方法很简单只需要将db文件删除重新运行便可成功但是原来数据库中的数据都会被删除。所以需要用到MySQLiteHelper中的update方法。 class MySQLiteHelper extends SQLiteOpenHelper{.....Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){db.execSQL(drop table if exists book); //如果已经存在就删除防止重复创建onCreate(db); // 再次执行onCreate 方法} }但是onUpgrade方法默认是不执行的如何让onUpgrade方法执行需要用到MySQLiteHelper 构造参数中的版本号 private static final int DATABASE_VERSION 1;// 将版本号 由 1 改为2 这里将数据库版本号由 1 改为 2表示对数据库的升级 数据库的 增、删、改、查 添加数据 ContentValues values new ContentValues();values.put(date, str1.isEmpty()?0.0:str1);values.put(breakfast, str2);values.put(lunch, str3);values.put(dinner, str4);values.put(happy, str5);values.put(other, str6);values.put(spare, str7);long ii db.insert(kog_bill, , values);values.clear();if(ii ! -1) {Utils.showToast(保存成功, MainActivity.this);}else {Utils.showToast(保存失败, MainActivity.this);}更新数据 ContentValues valus new ContentValues(); valuse.put(other,12); db.update(kogBill, values, _id?,new String[]{id}删除数据 db.delete(kogBill, _id?,new String[]{id});查询数据 db.query(kog_bill, new String[]{_id,date,breakfast,lunch,dinner,happy,other,spare}, null, null, null, null, date desc);使用SQL操作数据库 虽然Android 已经给我们提供了非常方便的API用于操作数据库不过总会有些人不习惯去使用这些辅助行的方法而是更加青睐于直接使用SQL来操作数据库当然Android也是提供的。 添加数据 db.execSQL(insert into kogBill (date,breakfest,lunch,dinner,happy,other,spare) values (?,?,?,?,?,?,?), new String[]{1921-1-1“123”“1”“1”“11”“2”“3”}更新数据 db.execSQL(update kogBill set other ? where _id ? , new String[]{12,id});删除数据 db.execSQL(delete from kogBill where _id ”, new String[]{id});使用 LitePal 操作数据库 假设编译环境为AndroidStudio。 1,引进包 dependencies{compile fileTree(dir:libs, include:[*.jar])compile com.android.support:appcompat-v7:23.2.0testCompile junt:junt:4.12compile org.litepal.android:core:1.3.2 //引入litepal包 }2,配置litepal.xml 文件 右键app/src/main 目录-New - Directory ,创建一个assets目录然后在 assets目录下再新建一个litepal.xml 文件接着编辑文件中的内容 xml version1.0 encodingutf-8? litepaldbname value BookStore/dbnameversion value1/versionlist/list /listepal其中dbname 标签用来指定数据库名version 用来指定数据库版本号list 标签用来指定所有映射模型。 最后还需要在配置以下 LitePalApplication, 修改AndroidManifest.xml 中的代码 manifest xmlns:androidhttp://schemas.android.com/apk/res/androidpackagecom.example.litepaltest applicationandroid:nameorg.litepal.LitePalApplication //配置 LitePalApplicationandroid:allowBackuptrue...../application /manifest以上LitePal的配置工作已经结束了接下来使用LitePal。 首先将需要实现 javabean类 对应 数据库表. 然后将javabean类添加到映射模型列表中修改litepal.xml 中的代码 litepaldbname valuekogBill /dbnameversion value1/versionlistmapping classcom.example.litepaltest.book/mapping //javabean类的路径/list这样所有工作就已经完成现在只要进行任意一次数据库的操作数据库db文件就会自动创建比如 Connector.getDatabase();操作数据 如果需要对某个表进行数据操作需要让其对应的javaBean类继承DataSupport public class Book extends DataSupport { //让对应的类继承DataSupport... }接下来进行添加数据的操作 Book book new Book(); book.setName(...); book.setAuthor(...); book.setPages(234); book.setPrice(12,21); book.setPress(unkow); book.save(); //执行sava 就可以插入数据了执行更新数据 Book book new Book(); book.setPrice(11.11); book.setPress(Anchor); book.updateAll(name ? and authro ?,..,...);删除数据 DataSupport.deleteAll(Book.class, price?,13);查询数据 //查询所有 ListBook books DataSupport.findAll(Book.class); // 查询第一条 ListBook books DataSupport.findFirst(Book.class); //查询最后一条 ListBook books DataSupport.findLast(Book.class); //查询那几列的数据 ListBook books DataSupport.select(name,author).find(Book.class); //条件查询 页面大于400 ListBook books DataSupport.where(pages ?,400).find(Book.class); //将 price 降序排序 ListBook books DataSupport.order(price desc).find(Book.class); //查询前3条 ListBook books DataSupport.limit(3).find(Book.class); //从下表1开始往后查询3条 ListBook boods DataSupport.limit(3).offset(1),find(Book.class)当然这些方法也可以组合起来使用 ListBook books DataSupport.select(name,author,pages).where(pages?”,400).order(pages).limit(10).offset(10).find(Book.class);如果有些特殊查询使用上述方法无法查询时可以使用如下语句 Cursor c DataSupport.findBySQL(select * from Book where pages ? and price ”, 400,20”);四. 内容提供器Content provider方式 在 Android 的设计“哲学”里是鼓励开发者使用内部类的这样不但使用方便而且执行效率也高。 1.什么是 ContentProvider  数据在Android当中是私有的当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。难道两个程序之间就没有办法对于数据进行交换解决这个问题主要靠ContentProvider。  一个Content Provider类实现了一组标准的方法接口从而能够让其他的应用保存或读取此Content Provider的各种数据类型。也就是说一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据暴露出去。外界根本看不到也不用看到这个应用暴露的数据在应用当中是如何存储的或者是用数据库存储还是用文件存储还是通过网上获得这些一切都不重要重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道可以读取程序的数据也可以删除程序的数据当然中间也会涉及一些权限的问题。 下边列举一些较常见的接口这些接口如下所示。 // 通过Uri进行查询返回一个Cursor。  query(Uriuri, String[] projection, String selection, String[] selectionArgs,StringsortOrder)// 将一组数据插入到Uri 指定的地方。  insert(Uriurl, ContentValues values)// 更新Uri指定位置的数据。  update(Uriuri, ContentValues values, String where, String[] selectionArgs)// 删除指定Uri并且符合一定条件的数据。 delete(Uriurl, String where, String[] selectionArgs) 2.什么是 ContentResolver  外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据在Activity当中通过getContentResolver()可以得到当前应用的ContentResolver实例。       ContentResolver提供的接口和ContentProvider中需要实现的接口对应主要有以下几个。  // 通过Uri进行查询返回一个Cursor。  query(Uriuri, String[] projection, String selection, String[] selectionArgs,StringsortOrder)// 将一组数据插入到Uri 指定的地方。  insert(Uriurl, ContentValues values)// 更新Uri指定位置的数据。  update(Uriuri, ContentValues values, String where, String[] selectionArgs)// 删除指定Uri并且符合一定条件的数据。 delete(Uriurl, String where, String[] selectionArgs) 3. ContentProvider和ContentResolver中用到的Uri  在 ContentProvider 和 ContentResolver 当中用到了 Uri 的形式通常有两种 一种是指定全部数据        另一种是指定某个ID的数据。我们看下面的例子。  content://contacts/people/  这个Uri指定的就是全部的联系人数据。         content://contacts/people/1 这个Uri指定的是ID为1的联系人的数据。 在上边两个类中用到的Uri一般由3部分组成。 第一部分是content://。         第二部分是要获得数据的一个字符串片段。          最后就是ID如果没有指定ID那么表示返回全部。由于URI通常比较长而且有时候容易出错且难以理解。所以在Android当中定义了一些辅助类并且定义了一些常量来代替这些长字符串的使用例如下边的代码  Contacts.People.CONTENT_URI 联系人的URI。      1实现的功能 在这个例子里边首先在系统的联系人应用当中插入一些联系人信息然后把这些联系人的名字和电话再显示出来 2实现方法 package com.contentProvider;import android.app.ListActivity; import android.database.Cursor; import android.os.Bundle; import android.provider.Contacts.Phones; import android.widget.ListAdapter; import android.widget.SimpleCursorAdapter;public class ContentProviderDemo extends ListActivity {protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//getContentResolver()方法得到应用的ContentResolver实例。// queryPhones.CONTENT_URI, null, null, null, null。它是ContentResolver里的方法负责查询所有联系人并返回一个Cursor。这个方法参数比较多每个参数的具体含义如下。//· 第一个参数为Uri在这个例子里边这个Uri是联系人的Uri。//· 第二个参数是一个字符串的数组数组里边的每一个字符串都是数据表中某一列的名字它指定返回数据表中那些列的值。//· 第三个参数相当于SQL语句的where部分描述哪些值是我们需要的。//· 第四个参数是一个字符串数组它里边的值依次代替在第三个参数中出现的“?”符号。//· 第五个参数指定了排序的方式。Cursor c getContentResolver().query(Phones.CONTENT_URI, null, null, null, null);startManagingCursor(c); //让系统来管理生成的Cursor。ListAdapter adapter new SimpleCursorAdapter(this,android.R.layout.simple_list_item_2, c, new String[] { Phones.NAME, Phones.NUMBER }, new int[] { android.R.id.text1, android.R.id.text2 });setListAdapter(adapter); //将ListView和SimpleCursorAdapter进行绑定。}} 五. 网络存储方式 1例子介绍          通过邮政编码查询该地区的天气预报以POST发送的方式发送请求到webservicex.net站点访问WebService.webservicex.net站点上提供查询天气预报的服务具体信息请参考其WSDL文档网址为http://www.webservicex.net/WeatherForecast.asmx?WSDL。 输入美国某个城市的邮政编码。  输出该邮政编码对应城市的天气预报。 2实现步骤如下 1如果需要访问外部网络则需要在AndroidManifest.xml文件中加入如下代码申请权限许可  !-- Permissions --  uses-permission Android:nameAndroid.permission.INTERNET/  2以HTTP POST的方式发送注意SERVER_URL并不是指WSDL的URL而是服务本身的URL。实现的代码如下所示 //定义需要获取的内容来源地址private static final String SERVER_URL http://www.webservicex.net/WeatherForecast. asmx/GetWeatherByZipCode;//根据内容来源地址创建一个Http请求HttpPost request new HttpPost(SERVER_URL); // 添加一个变量ListNameValuePair params new ArrayListNameValuePair();// 设置一个华盛顿区号params.add(new BasicNameValuePair(ZipCode,200120)); //添加必须的参数request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); //设置参数的编码try{//发送请求并获取反馈HttpResponse httpResponse new DefaultHttpClient().execute(request); // 解析返回的内容if (httpResponse.getStatusLine().getStatusCode() ! 404) {String result EntityUtils.toString(httpResponse.getEntity());Log.d(LOG_TAG, result);}} catch(Exception e){Log.e(LOG_TAG, e.getMessage());} 代码解释 如上代码使用Http从webservicex获取ZipCode为“200120”美国WASHINGTON D.C的内容其返回的内容如下 WeatherForecasts xmlns:xsdhttp://www.w3.org/2001/XMLSchema xmlns:xsihttp: //www.w3.org/2001/XMLSchema-instance xmlnshttp://www.webservicex.netLatitude38.97571/LatitudeLongitude77.02825/LongitudeAllocationFactor0.024849/AllocationFactorFipsCode11/FipsCodePlaceNameWASHINGTON/PlaceNameStateCodeDC/StateCodeDetailsWeatherDataDaySaturday, April 25, 2009/DayWeatherImagehttp://forecast.weather.gov/images/wtf/sct.jpg/WeatherImageMaxTemperatureF88/MaxTemperatureFMinTemperatureF57/MinTemperatureFMaxTemperatureC31/MaxTemperatureCMinTemperatureC14/MinTemperatureC/WeatherDataWeatherDataDaySunday, April 26, 2009/DayWeatherImagehttp://forecast.weather.gov/images/wtf/few.jpg/WeatherImageMaxTemperatureF89/MaxTemperatureFMinTemperatureF60/MinTemperatureFMaxTemperatureC32/MaxTemperatureCMinTemperatureC16/MinTemperatureC/WeatherData …/Details /WeatherForecasts
http://www.pierceye.com/news/280134/

相关文章:

  • asp.net 网站提速廊坊企业官网搭建
  • 网站开发全过程电商数据分析
  • 代理 指定网站 hostwordpress图片无限放大
  • 中材建设有限公司招标网站包装设计网课答案
  • python云服务器网站开发实例外贸小家电网站推广
  • 郑州做网站公司中天猫商城的商品来源
  • 织梦网站首页互联网保险平台排名
  • 免费做链接的网站做动画相册在哪个网站好
  • 做思维导图好看的网站可以做富集分析的网站
  • wordpress 媒体库 cos百度网站怎样优化排名
  • 自助建站程序html样式模板
  • 公主岭网站建设筑梦网站建设
  • 昊源建设监理有限公司网站广州住房与城乡建设部网站
  • 如何免费建立自己网站wordpress媒体优化
  • 南京企业做网站网站建设的类型有几种
  • 不需要证件做网站相城区建设网站
  • 游戏推广网站如何做的全网投放广告的渠道有哪些
  • 飞数石家庄网站建设seo 关键词优化
  • 织梦新手网站建设建筑工程公司资质
  • 网站建设开什么名目外贸网站建设关键点
  • 大学生网站设计河南省建筑工程信息网
  • 安徽省住房和城乡建设厅网站首页wordpress评论框中加文字提示
  • 南京营销型网站建设公司杭州模板建站代理
  • 网页设计比较优秀的网站沈阳网站推广公司
  • 西安网站建设维护如何免费制作app软件
  • 用 net做网站大理市城乡建设局网站
  • 怎么在建筑网站做翻译兼职哈尔滨 高端网站建设
  • 网站建设颜色注意事项优化网站要怎么做
  • 作图神器沧州网站优化
  • 做水果的网站有哪些公司网页设计作品