3、Content Provider
Android应用可以把它们的数据保存到文件和SQLite数据库中,但android有一个独特之处就是,数据库只能被它的创建者所使用,其他的应用是不能访问到的。当想将应用数据与其他应用共享时,Content Provider就可以发挥作用了。因为Content Provider类实现一组标准的方法,能够让其他的应用保存或读取此内容提供器处理的各种数据类型。
并且,android自身也提供了几个现成的content provider:Contacts, Browser, CallLog, Settings, MediaStore, 用可以通过一个唯一的ContentResolver interface 来使用具体的某个content provider。
ContentResolver cr = getContentResolver();
然后你就可以用ContentResolver提供的方法来使用你需要的content provider了。其中 contentResolver提供的方法包括query(),insert(),update()等。要使用这些方法,还会涉及到一个东西,那就是 Uri。你可以将它理解成一个string形式的contentProvider的完全路径,它的形式为<standard_prefix>://<authority>/<data_path>/<id>,
例如:
content://browser/bookmarks
content://contacts/people
content://contacts/people/3
下面结合一个实例来看我们如何使用一个已有的content provider,给例子展示了如何从已有的电话本中读出联系人信息:
import android.app.Activity; import android.content.ContentResolver; import android.database.Cursor; import android.os.Bundle; import android.provider.Contacts.People; import android.util.Log; import android.widget.Toast; public class ContentProviderTest extends Activity { private final String TAG = "ContentProviderTest"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i(TAG,"enter onCreate"); setContentView(R.layout.main); createCP(); } public void createCP() { ContentResolver cr = getContentResolver(); //Cursor cur = managedQuery(People.CONTENT_URI, null, null, null, null); Cursor cur = cr.query(People.CONTENT_URI, null, null, null, null); getColumnData(cur); } private void getColumnData(Cursor cur){ if (cur.moveToFirst()) { String name; String phoneNumber; int nameColumn = cur.getColumnIndex(People.NAME); int phoneColumn = cur.getColumnIndex(People.NUMBER); do { // Get the field values name = cur.getString(nameColumn); phoneNumber = cur.getString(phoneColumn); Log.i(TAG, "name="+name); DisplayToast(name+" "+phoneNumber); } while (cur.moveToNext()); } } public void DisplayToast(String s) { Toast.makeText(this, s, Toast.LENGTH_LONG).show(); }
同样需要注意的是在AndroidManifrest.xml中加上
<uses-permission android:name="android.permission.READ_CONTACTS"> </uses-permission>
否则程序无法运行。
4、Service
在学习Service之前,我们应该清楚几个概念
服务是运行在后台的一段代码。它可以运行在它自己的进程 , 也可以运行在其他应用程序进程的上下文( context )里面 ,这取决于自身的需要。其它的组件可以绑定到一个服务 ( Service )上面,通过远程过程调用( RPC )来调用 这个方法。例如媒体播放器的服务,当用户退出媒体选择用户界面,仍然希望音乐依然可以继续播放,这时就是由服务 ( service )来保证当用户界面关闭时音乐继续播放的。
它跟 Activity 的级别差不多,但是他不能自己运行,需要通过某一个 Activity 或者其他 Context 对象来调用, Context.startService() 和 Context.bindService() 。
两种启动 Service 的方式有所不同。这里要说明一下的是如果你在 Service 的 onCreate 或者 onStart 做一些很耗时间的事情,最好在 Service 里启动一个线程来完成,因为 Service 是跑在主线程中,会影响到你的 UI 操作或者阻塞主线程中的其他事情。
什么时候需要 Service 呢?比如播放多媒体的时候用户启动了其他 Activity 这个时候程序要在后台继续播放,比如检测 SD 卡上文件的变化,再或者在后台记录你地理信息位置的改变等等。
1 .第一种是通过调用 Context.startService() 启动,调用 Context.stopService() 结束, startService() 可以传递参数给 Service 。
2 .第二种方式是通过调用 Context.bindService() 启动,调用 Context.unbindservice() 结束,还可以通过 ServiceConnection 访问 Service 。
二者可以混合使用,比如说我可以先 startService 再 bindservice 。
startService 后,即使调用 startService 的进程结束了, Service 仍然还存在,直到有进程调用 stopService ,或者 Service 自己自杀( stopSelf() )。 bindService 后, Service 就和调用 bindService 的进程同生共死了,也就是说当调用 bindService 的进程死了,那么它 bind 的 Service 也要跟着被结束,当然期间也可以调用 unbindservice 让 Service 结束。
两种方式混合使用时,比如说你 startService 了,我 bindService 了,那么只有你 stopService 了而且也 unbindservice 了,这个 Service 才会被结束。
Android 系统将会尝试保留那些启动了的或者是绑定了服务的进程。如果该服务正在进程的 onCreate(), onStart(), 或者 onDestroy() 这些方法中执行时 , 那么主进程将会成为一个前台进程 , 以确保此代码不会被停止。如果服务已经开始 , 那么它的主进程会就重要性而言低于所有可见的进程但高于不可见的进程 , 由于只有少数几个进程是用户可见的 , 所以只要不是内存特别低 , 该服务不会停止 . 。如果有多个客户端绑定了服务 , 只要客户端中的一个对于用户是可见的 , 即认为该服务可见。
如果 Service 还没有运行,则 android 先调用 onCreate() 然后调用 onStart() ;如果 Service 已经运行,则只调用 onStart() ,所以一个 Service 的 onStart 方法可能会重复调用多次。
stopService 的时候直接 onDestroy ,如果是调用者自己直接退出而没有调用 stopService 的话, Service 会一直在后台运行。该 Service 的调用者再启动起来后可以通过 stopService 关闭 Service 。
所以调用 startService 的生命周期为: onCreate --> onStart( 可多次调用 ) --> onDestroy
使用 context.bindService() 启动 Service 会经历:
context.bindService()->onCreate()->onBind()->Service running
onUnbind() -> onDestroy() ->Service stop
onBind 将返回给客户端一个 IBind 接口实例, IBind 允许客户端回调服务的方法,比如得到 Service 运行的状态或其他操作。这个时候把调用者( Context ,例如 Activity )会和 Service 绑定在一起, Context 退出了, Srevice 就会调用 onUnbind->onDestroy 相应退出。
所以调用 bindService 的生命周期为: onCreate --> onBind( 只一次,不可多次绑定 ) --> onUnbind --> onDestory 。
所以,在 Service 每一次的开启关闭过程中,只有 onStart 可被多次调用 ( 通过多次 startService 调用 ) ,其他 onCreate , onBind , onUnbind , onDestory 在一个生命周期中只能被调用一次。