Android四大组件之ContentProvider 使用及原理

ContentProvider作为Android四大组件之一,平时写自己的ContentProvider比较少,但是用到ContentProvider地方还是有的,比如去获取通讯录信息,这其实就间接的使用到了通讯录程序的ContentProvider组件.

先简单说一下ContentProvider组件.后面重点分析源码了解ContentProvider运行的过程.

ContentProvider可以实现在应用程序之间共享数据.

Android为常见的一些数据提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。 所以我们可以在其他应用程通过那些ContentProvider获取这些数据.

下面看一个简单的demo,了解一下在程序中我们如何写自己的ContentProvider,以及其他程序如何通过我们提供的ContentProvider来访问我们应用程序的数据.

  1. package com.cj.providerdemo2;  
  2.   
  3. import android.content.ContentProvider;  
  4. import android.content.ContentResolver;  
  5. import android.content.ContentUris;  
  6. import android.content.ContentValues;  
  7. import android.content.Context;  
  8. import android.content.UriMatcher;  
  9. import android.database.Cursor;  
  10. import android.database.sqlite.SQLiteDatabase;  
  11. import android.database.sqlite.SQLiteOpenHelper;  
  12. import android.net.Uri;  
  13.   
  14. public class MyContentProvider extends ContentProvider {  
  15.     private final static int CONTACT = 1;  
  16.   
  17.     private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);  
  18.     static {  
  19.         uriMatcher.addURI("com.cj.mycontentprovider","contact",CONTACT);  
  20.     }  
  21.   
  22.     private MyDBHelp dbHelp;  
  23.   
  24.     @Override  
  25.     public int delete(Uri uri, String selection, String[] selectionArgs) {  
  26.         int id =0;  
  27.         if(uriMatcher.match(uri) == CONTACT){  
  28.             SQLiteDatabase database = dbHelp.getWritableDatabase();  
  29.             id= database.delete("contact", selection, selectionArgs);  
  30.             contentResolver.notifyChange(uri,null);  
  31.         }  
  32.        return id;  
  33.     }  
  34.   
  35.     @Override  
  36.     public String getType(Uri uri) {  
  37.         // TODO: Implement this to handle requests for the MIME type of the data  
  38.         // at the given URI.  
  39.         throw new UnsupportedOperationException("Not yet implemented");  
  40.     }  
  41.   
  42.     @Override  
  43.     public Uri insert(Uri uri, ContentValues values) {  
  44.         Uri u = null;  
  45.         if(uriMatcher.match(uri) == CONTACT){  
  46.             SQLiteDatabase database = dbHelp.getWritableDatabase();  
  47.   
  48.             long d = database.insert("contact""_id", values);  
  49.             u = ContentUris.withAppendedId(uri,d);  
  50.             contentResolver.notifyChange(u,null);  
  51.         }  
  52.         return u;  
  53.   
  54.     }  
  55.     private ContentResolver contentResolver;  
  56.     @Override  
  57.     public boolean onCreate() {  
  58.         Context context = getContext();  
  59.         contentResolver = context.getContentResolver();  
  60.         dbHelp = new MyDBHelp(context,"contact.db",null,1);  
  61.         return true;  
  62.     }  
  63.   
  64.     @Override  
  65.     public Cursor query(Uri uri, String[] projection, String selection,  
  66.                         String[] selectionArgs, String sortOrder) {  
  67.         Cursor cursor = null;  
  68.         if(uriMatcher.match(uri) == CONTACT){  
  69.             SQLiteDatabase database = dbHelp.getReadableDatabase();  
  70.             cursor = database.query("contact", projection, selection, selectionArgs, nullnull, sortOrder);  
  71.             cursor.setNotificationUri(contentResolver,uri);  
  72.         }  
  73.         return cursor;  
  74.   
  75.     }  
  76.   
  77.     @Override  
  78.     public int update(Uri uri, ContentValues values, String selection,  
  79.                       String[] selectionArgs) {  
  80.         // TODO: Implement this to handle requests to update one or more rows.  
  81.         throw new UnsupportedOperationException("Not yet implemented");  
  82.     }  
  83.   
  84.     /** 
  85.      * 
  86.      */  
  87.     private static  class MyDBHelp extends SQLiteOpenHelper{  
  88.   
  89.         public MyDBHelp(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {  
  90.             super(context, name, factory, version);  
  91.   
  92.         }  
  93.   
  94.         @Override  
  95.         public void onCreate(SQLiteDatabase db) {  
  96.             String sql = "create table contact(_id integer primary key autoincrement," +  
  97.                     "name text not null,number text not null);";  
  98.             db.execSQL(sql);  
  99.         }  
  100.   
  101.         @Override  
  102.         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
  103.             onCreate(db);  
  104.   
  105.         }  
  106.     }  
  107. }  

这个程序有一个存储联系人信息的数据库,由ContentProvider组件管理.这个应用程序相当于一个平台,向其他应用程序提供了访问平台数据(联系人信息)的接口,其他应程序可以通过这些接口获取联系人的信息,也可以向这个数据库添加、更新或者删除联系人,当然前提是遵守该平台的一些规则.

第一:首先得知道该平台的地址.这里也就是ContentProvider的身份证,因为所有的操作都是经过ContentProvider的.

看上面程序,自定义一个MyContentProvider,它继承ContentProvider,在清单配置文件里,ContentProvider有一个属性authorities,这就是ContentProvider的身份证,所以一般为了保证唯一性,使用包名的方式命名.

ContentProvider提供几个重要的函数,MyContentProvider必须重载,这些函数(inset,query,update,delete)顾名思义就知道是对数据的操作.

这里用了Android自带的Sqlite数据库来存储平台的数据(当然也可以用其他的方式存储),所以MyContentProvider重载的函数其实是对sqlite数据库的操作.

为了方便操作数据库,这里用到android sdk提供SQLiteOpenHelper这个类.

第二:就知道ContentProvider身份还不行,你还需要告诉ContentProvider具体要操作什么数据.

当然前提是平台已经将数据类型公开了,比如这里只有联系人信息数据.

数据是以Uri的形式向外公开的.

这里简单介绍一下ContentProvider规定数据形式的URI组成:

主要包括三个部分

一:scheme

ContentProvider(内容提供者)的scheme已经由Android所规定, scheme为:content://

二:主机名
主机名(或叫Authority)用于唯一标识这个ContentProvider,比如这里
com.cj.mycontentprovider,外部调用者可以根据这个标识来找到它

三:path
路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定,如上面程序:
要操作contact表/contact

上面三个部分是主要的,还可以有更详细的,比如还包含数据id /contact/id。

如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:
Uri uri = Uri.parse("content://com.cj.mycontentprovider/contact").

然后再介绍一下UriMatcher类使用:

因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris 。掌握它们的使用,会便于我们的开发工作。
UriMatcher类用于匹配Uri,它的用法如下:

	if(uriMatcher.match(uri) == CONTACT)
 
 首先第一步把你需要匹配Uri路径全部给注册上,如下: 
 
uriMatcher.addURI("com.cj.mycontentprovider","contact",CONTACT);

      注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用          addURI()方法传入的第三个参数。

再看一下ContentUris

ContentUris类用于操作Uri路径后面的ID部分,它有个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分:

        u = ContentUris.withAppendedId(uri,d);
  1. package com.cj.providerdemo1;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.ContentResolver;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.database.ContentObserver;  
  8. import android.database.Cursor;  
  9. import android.net.Uri;  
  10. import android.os.Bundle;  
  11. import android.os.Handler;  
  12. import android.support.v7.app.AppCompatActivity;  
  13. import android.view.LayoutInflater;  
  14. import android.view.View;  
  15. import android.view.ViewGroup;  
  16. import android.widget.AdapterView;  
  17. import android.widget.Button;  
  18. import android.widget.CursorAdapter;  
  19. import android.widget.ListView;  
  20. import android.widget.TextView;  
  21.   
  22. public class MainActivity extends AppCompatActivity {  
  23.     private ListView lv;  
  24.     private Button btn;  
  25.     private ContentResolver resolver;  
  26.     private MyAdapter myAdapter;  
  27.     private Cursor cursor;  
  28.     @Override  
  29.     protected void onCreate(Bundle savedInstanceState) {  
  30.         super.onCreate(savedInstanceState);  
  31.         setContentView(R.layout.activity_main);  
  32.         lv = (ListView) findViewById(R.id.lv);  
  33.         btn = (Button) findViewById(R.id.btn);  
  34.         resolver = getContentResolver();  
  35.         cursor = resolver.query(Uri.parse("content://com.cj.mycontentprovider/contact"), nullnull,  
  36.                 nullnull);  
  37.             myAdapter = new MyAdapter(this,cursor);  
  38.             lv.setAdapter(myAdapter);  
  39.         btn.setOnClickListener(new View.OnClickListener() {  
  40.             @Override  
  41.             public void onClick(View v) {  
  42.                 MainActivity.this.startActivityForResult(new Intent(MainActivity.this,AddActivity.class),  
  43.                         1);  
  44.             }  
  45.         });  
  46.         String[] s = new String[2];  
  47.         lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {  
  48.             @Override  
  49.             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {  
  50.                 resolver.delete(Uri.parse("content://com.cj.mycontentprovider/contact"),  
  51.                 null,null);  
  52.             }  
  53.         });  
  54.         resolver.registerContentObserver(Uri.parse("content://com.cj.mycontentprovider/contact"),  
  55.                 true,new MyContentObserver(new Handler()));  
  56.     }  
  57.   
  58.     @Override  
  59.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  60.         super.onActivityResult(requestCode, resultCode, data);  
  61.     }  
  62.     private class MyContentObserver extends ContentObserver{  
  63.         /** 
  64.          * Creates a content observer. 
  65.          * 
  66.          * @param handler The handler to run {@link #onChange} on, or null if none. 
  67.          */  
  68.         public MyContentObserver(Handler handler) {  
  69.             super(handler);  
  70.         }  
  71.   
  72.         @Override  
  73.         public void onChange(boolean selfChange) {  
  74.             myAdapter.notifyDataSetChanged();  
  75.   
  76.         }  
  77.     }  
  78.     private static class MyAdapter extends CursorAdapter{  
  79.         private Cursor cursor;  
  80.         private Context context;  
  81.         public MyAdapter(Context context, Cursor c) {  
  82.             super(context, c);  
  83.             this.context = context;  
  84.             this.cursor = c;  
  85.         }  
  86.   
  87.         @Override  
  88.         public View newView(Context context, Cursor cursor, ViewGroup parent) {  
  89.             LayoutInflater layoutInflater = LayoutInflater.from(context);  
  90.             View view = layoutInflater.inflate(R.layout.item, null);  
  91.             if(cursor !=null){  
  92.                 view.setTag(cursor.getInt(cursor.getColumnIndex("_id")));  
  93.             }  
  94.             return view;  
  95.         }  
  96.   
  97.         @Override  
  98.         public void bindView(View view, Context context, Cursor cursor) {  
  99.             TextView name = (TextView) view.findViewById(R.id.tv_name);  
  100.             TextView num = (TextView) view.findViewById(R.id.tv_num);  
  101.             if(cursor !=null){  
  102.                 name.setText(cursor.getString(cursor.getColumnIndex("name")));  
  103.                 num.setText(cursor.getString(cursor.getColumnIndex("number")));  
  104.             }  
  105.         }  
  106.   
  107.         @Override  
  108.         public long getItemId(int position) {  
  109.             return super.getItemId(position);  
  110.         }  
  111.     }  
  112.   
  113.   
  114. }  
  1. package com.cj.providerdemo1;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.ContentValues;  
  5. import android.net.Uri;  
  6. import android.support.v7.app.AppCompatActivity;  
  7. import android.os.Bundle;  
  8. import android.view.View;  
  9. import android.widget.EditText;  
  10. import android.widget.Toast;  
  11.   
  12. public class AddActivity extends AppCompatActivity {  
  13.     private EditText et_name;  
  14.     private EditText et_num;  
  15.     @Override  
  16.     protected void onCreate(Bundle savedInstanceState) {  
  17.         super.onCreate(savedInstanceState);  
  18.         setContentView(R.layout.activity_add);  
  19.         et_name = (EditText) findViewById(R.id.et_name);  
  20.         et_num = (EditText) findViewById(R.id.et_num);  
  21.     }  
  22.     public void add(View view){  
  23.         String name = et_name.getText().toString();  
  24.         String num = et_num.getText().toString();  
  25.         if(name == null || num == null){  
  26.             Toast.makeText(this,"姓名和号码不能为空",Toast.LENGTH_SHORT).show();  
  27.         }else {  
  28.             ContentValues contentValues = new ContentValues();  
  29.             contentValues.put("name",name);  
  30.             contentValues.put("number",num);  
  31.             getContentResolver().insert(Uri.parse("content://com.cj.mycontentprovider/contact"),contentValues);  
  32.             setResult(Activity.RESULT_OK);  
  33.             finish();  
  34.         }  
  35.   
  36.     }  
  37. }  

接下主要通过源码来分析上面的过程.

从第二个应用程序的getContentResolver()函数开始:

第一步:getContentResolver()

   在frameworks/base/core/Java/android/content/ContextWrapper.java文件中

   这个方法是Activty的父类ContextWrapper中的.

[java]  view plain  copy
  1. @Override  
  2.     public ContentResolver getContentResolver() {  
  3.         return mBase.getContentResolver();  
  4.     }  
这里mBase变量是ContextImpl类型,是在创建activity的时候,new 一个ContextImpl对象,赋值给activity的.

第二步:getContentResolver()

在frameworks/base/core/java/android/app/ContextImpl.java文件中

[java]  view plain  copy
  1. @Override  
  2.    public ContentResolver getContentResolver() {  
  3.        return mContentResolver;  
  4.    }  
这里直接返回了ContextImpl对象的成员变量mContentResolver;那这个变量是什么时候赋值的呢?

回到分析点击android桌面app图标启动应用程序的过程这篇文章看第五十步

在frameworks/base/core/java/android/app/ActivityThread.java中

[java]  view plain  copy
  1. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
  2.        ....  
  3.   
  4.        Activity activity = null;  
  5.        try {  
  6.            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();  
  7.            activity = mInstrumentation.newActivity(  
  8.                    cl, component.getClassName(), r.intent);  
  9.            StrictMode.incrementExpectedActivityCount(activity.getClass());  
  10.            r.intent.setExtrasClassLoader(cl);  
  11.            if (r.state != null) {  
  12.                r.state.setClassLoader(cl);  
  13.            }  
  14.        } catch (Exception e) {  
  15.      ...  
  16.        }  
  17.   
  18.        try {  
  19.            Application app = r.packageInfo.makeApplication(false, mInstrumentation); 
  20.        ...  
  21.            if (activity != null) {  
  22.                Context appContext = createBaseContextForActivity(r, activity);//创建contextimpl的地方  
  23.                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());  
  24.                Configuration config = new Configuration(mCompatConfiguration);  
  25.                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "  
  26.                        + r.activityInfo.name + " with config " + config);  
  27.                activity.attach(appContext, this, getInstrumentation(), r.token,  
  28.                        r.ident, app, r.intent, r.activityInfo, title, r.parent,  
  29.                        r.embeddedID, r.lastNonConfigurationInstances, config);//这里将创建的contextimpl对象传给activity内部mBase变量  
  30.             ....  
  31.        return activity;  
  32.    }  

上面通过createBaseContextForActivity()函数创建contextimpl对象,然后通过attach()函数将创建的contextimpl对象赋值给activity的内部变量,对应第一步的mBase变量. 这里attach()函数就没去分析了.

我们看创建contextimpl对象的地方,看createBaseContextForActivity()函数

[java]  view plain  copy
  1. private Context createBaseContextForActivity(ActivityClientRecord r,  
  2.             final Activity activity) {  
  3.         ContextImpl appContext = new ContextImpl();  
  4.         appContext.init(r.packageInfo, r.token, this);  
  5.         appContext.setOuterContext(activity);  
  6. .....  
  7.         return baseContext;  
  8.     }  
contextimpl类的无参构造函数
[java]  view plain  copy
  1. ContextImpl() {  
  2.         mOuterContext = this;  
  3.     }  
继续看它的init()函数

[java]  view plain  copy
  1. final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread) {  
  2.         init(packageInfo, activityToken, mainThread, nullnull, Process.myUserHandle());  
  3.     }  
  4.   
  5.     final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread,  
  6.             Resources container, String basePackageName, UserHandle user) {  
  7.         mPackageInfo = packageInfo;  
  8.         mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName;  
  9.         mResources = mPackageInfo.getResources(mainThread);  
  10. ......  
  11.         mMainThread = mainThread;  
  12.         mActivityToken = activityToken;  
  13.         mContentResolver = new ApplicationContentResolver(this, mainThread, user);//在这里给mContentResoler赋值的  
  14.         mUser = user;  
  15.     }  
上面函数给mContentResolver赋值了.


来看看ApplicationContentResolver这个类的结构


这样获得ContentResolver对象.

接着就分析它query()函数.

第三步:query()

在frameworks/base/core/java/android/content/ContentResolver.java文件中

[java]  view plain  copy
  1. public final Cursor query(Uri uri, String[] projection,  
  2.             String selection, String[] selectionArgs, String sortOrder) {  
  3.         return query(uri, projection, selection, selectionArgs, sortOrder, null);  
  4.     }  
  5.     public final Cursor query(final Uri uri, String[] projection,  
  6.             String selection, String[] selectionArgs, String sortOrder,  
  7.             CancellationSignal cancellationSignal) {  
  8.         IContentProvider unstableProvider = acquireUnstableProvider(uri);//  
  9.         if (unstableProvider == null) {  
  10.            ..  
  11.         }  
  12.         IContentProvider stableProvider = null;  
  13.         try {  
  14.         ...  
  15.             Cursor qCursor;  
  16.             try {  
  17.                 qCursor = unstableProvider.query(uri, projection,  
  18.                         selection, selectionArgs, sortOrder, remoteCancellationSignal);//使用IContentProvider对象的query方法,最终会调用ContentProvider对象的query()方法  
  19.             } catch (DeadObjectException e) {  
  20.               ...  
  21.             }  
  22.             if (qCursor == null) {  
  23.                 return null;  
  24.             }  
  25.             // force query execution  
  26.             qCursor.getCount();  
  27.             long durationMillis = SystemClock.uptimeMillis() - startTime;  
  28.             maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);  
  29.             // Wrap the cursor object into CursorWrapperInner object  
  30.             CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,  
  31.                     stableProvider != null ? stableProvider : acquireProvider(uri));  
  32.             stableProvider = null;  
  33.             return wrapper;  
  34.         } catch (RemoteException e) {  
  35.         ..  
  36.         } finally {  
  37.          ...  
  38.         }  
  39.     }  

query()函数首先调用了acquireUnstableProvider()函数,我们就先分析此函数

第四步: acquireUnstableProvider()

在frameworks/base/core/java/android/content/ContentResolver.java文件中

[java]  view plain  copy
  1. public final IContentProvider acquireUnstableProvider(Uri uri) {  
  2.         if (!SCHEME_CONTENT.equals(uri.getScheme())) {  
  3.             return null;  
  4.         }  
  5.         String auth = uri.getAuthority();  
  6.         if (auth != null) {  
  7.             return acquireUnstableProvider(mContext, uri.getAuthority());  
  8.         }  
  9.         return null;  
  10.     }  

该函数返回一个IContentProvider类型对象.是不是有点奇怪,理论上应该返回ContentProvider对象才对,因为我们目的是要调ContentProvider的方法呀.

先看一下类图:


一个IContentProvider对应一个ContentProvider对象.

调用IContentProvider对象的方法最终会调到ContentProvider对象的方法.这里也就是MyContentProvider.继续看上面的函数体

函数首先验证参数uri的scheme是否正确,即是否是以content://开头,然后取出它的authority部分,最后调用另外一个成员函数acquireUnstableProvider执行获取IContentProvider的操作。在我们这个情景中,参数uri的authority的内容便是“com.cj.mycontentprovider”了

acquireUnstableProvider(Context context, String auth) 这个函数在ApplicationContentResolver类中

第五步:acquireUnstableProvider()

ApplicationContentResolver类是ContextImpl类的一个内部类

在frameworks/base/core/java/android/app/ContextImpl.java文件

 
 
[java]  view plain  copy
  1. @Override  
  2.        protected IContentProvider acquireUnstableProvider(Context c, String auth) {  
  3.            return mMainThread.acquireProvider(c, auth, mUser.getIdentifier(), false);  
  4.        }  
这里mMainThread是ActivityThread类型,在第二步中创建ApplicationContentResolver对象时作为它的构造参数传进去的.

第六步:acquireProvider()

在frameworks/base/core/java/android/app/ActivityThread.java中

[java]  view plain  copy
  1. public final IContentProvider acquireProvider(  
  2.             Context c, String auth, int userId, boolean stable) {  
  3.         final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);  
  4.         if (provider != null) {  
  5.             return provider;  
  6.         }  
  7.   
  8.         // There is a possible race here.  Another thread may try to acquire  
  9.         // the same provider at the same time.  When this happens, we want to ensure  
  10.         // that the first one wins.  
  11.         // Note that we cannot hold the lock while acquiring and installing the  
  12.         // provider since it might take a long time to run and it could also potentially  
  13.         // be re-entrant in the case where the provider is in the same process.  
  14.         IActivityManager.ContentProviderHolder holder = null;  
  15.         try {  
  16.             holder = ActivityManagerNative.getDefault().getContentProvider(  
  17.                     getApplicationThread(), auth, userId, stable);  
  18.         } catch (RemoteException ex) {  
  19.         }  
  20.         if (holder == null) {  
  21.             Slog.e(TAG, "Failed to find provider info for " + auth);  
  22.             return null;  
  23.         }  
  24.   
  25.         // Install provider will increment the reference count for us, and break  
  26.         // any ties in the race.  
  27.         holder = installProvider(c, holder, holder.info,  
  28.                 true /*noisy*/, holder.noReleaseNeeded, stable);  
  29.         return holder.provider;  
  30.     }  

这个函数首先会通过getExistingProvider()函数来检查本地是否已经存在这个要获取的IContentProvider,如果存在,就直接返回了。本地已经存在的IContentProvider保存在ActivityThread类的mProviderMap成员变量中,以ContentProvider对应的URI的authority为键值保存。在我们这个情景中,因为是第一次调用MyContentProvider,因此,这时候通过getExistingProvider()函数得到的IContentProvider为null,于是下面就会调用ActivityManagerService服务的getContentProvider()函数来获取一个ContentProviderHolder对象holder,这个对象就包含了我们所要获取的MyContentProvider对应的IContentProvider,在将IContentProvider返回给调用者之前,还会调用installProvider函数来把这个IContentProvider保存在本地中,以便下次要使用这个IContentProvider时,直接就可以通过getExistingProvider()函数获取了。


我们先进入到ActivityManagerService服务的getContentProvider函数中看看它是如何获取我们所需要的MyContentProvider对应的ContentProviderHolder对象的,然后再返回来看看installProvider()函数的实现。

这其中又是经过Binder驱动进行进程间通信,这过程就不写了,反正知道会调用到ActivityManagerService中的getContentProvider()函数,所以直接到ActivityManagerService中去.

第七步:getContentProvider()

在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java.文件中

[java]  view plain  copy
  1. public final ContentProviderHolder getContentProvider(  
  2.             IApplicationThread caller, String name, int userId, boolean stable) {  
  3.       ...  
  4.         if (caller == null) {  
  5.          ....  
  6.         }  
  7. ....  
  8.         return getContentProviderImpl(caller, name, null, stable, userId);  
  9.     }  
继续往下调用

第八步:getContentProviderImpl()

在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java.文件

[java]  view plain  copy
  1. private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,  
  2.             String name, IBinder token, boolean stable, int userId) {  
  3.         ContentProviderRecord cpr;  
  4.         ContentProviderConnection conn = null;  
  5.         ProviderInfo cpi = null;  
  6.         synchronized(this) {  
  7.             ProcessRecord r = null;  
  8.             if (caller != null) {  
  9.                 r = getRecordForAppLocked(caller);  
  10.                 if (r == null) {  
  11.                 ....  
  12.                 }  
  13.             }  
  14.             // First check if this content provider has been published...  
  15.             cpr = mProviderMap.getProviderByName(name, userId);  
  16.             boolean providerRunning = cpr != null;  
  17.             if (providerRunning) {  
  18.                 ....  
  19.             }  
  20.             boolean singleton;  
  21.             if (!providerRunning) {  
  22.                 try {  
  23.                     cpi = AppGlobals.getPackageManager().  
  24.                         resolveContentProvider(name,  
  25.                             STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);  
  26.                 } catch (RemoteException ex) {  
  27.                 }  
  28.                 if (cpi == null) {  
  29.                  ...  
  30.                 }  
  31.            ...  
  32.                 ComponentName comp = new ComponentName(cpi.packageName, cpi.name);  
  33.                 cpr = mProviderMap.getProviderByClass(comp, userId);  
  34.                 final boolean firstClass = cpr == null;  
  35.                 if (firstClass) {  
  36.                     try {  
  37.                         ApplicationInfo ai =  
  38.                             AppGlobals.getPackageManager().  
  39.                                 getApplicationInfo(  
  40.                                         cpi.applicationInfo.packageName,  
  41.                                         STOCK_PM_FLAGS, userId);  
  42.                         if (ai == null) {  
  43.                      ...  
  44.                         }  
  45.                         ai = getAppInfoForUser(ai, userId);  
  46.                         cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);  
  47.                     } catch (RemoteException ex) {  
  48.                         // pm is in same process, this will never happen.  
  49.                     }  
  50.                 }  
  51.   
  52.            ...  
  53.                 // This is single process, and our app is now connecting to it.  
  54.                 // See if we are already in the process of launching this  
  55.                 // provider.  
  56.                 final int N = mLaunchingProviders.size();  
  57.                 int i;  
  58.                 for (i=0; i<N; i++) {  
  59.                     if (mLaunchingProviders.get(i) == cpr) {  
  60.                         break;  
  61.                     }  
  62.                 }  
  63.   
  64.                 // If the provider is not already being launched, then get it  
  65.                 // started.  
  66.                 if (i >= N) {  
  67.                     final long origId = Binder.clearCallingIdentity();  
  68.   
  69.                     try {  
  70.                         // Content provider is now in use, its package can't be stopped.  
  71.                         try {  
  72.                             AppGlobals.getPackageManager().setPackageStoppedState(  
  73.                                     cpr.appInfo.packageName, false, userId);  
  74.                         } catch (RemoteException e) {  
  75.                         } catch (IllegalArgumentException e) {  
  76.                      ..  
  77.                         }  
  78.   
  79.                         ProcessRecord proc = startProcessLocked(cpi.processName,  
  80.                                 cpr.appInfo, false0"content provider",  
  81.                                 new ComponentName(cpi.applicationInfo.packageName,  
  82.                                         cpi.name), falsefalse);  
  83.                         if (proc == null) {  
  84.                       ...  
  85.                             return null;  
  86.                         }  
  87.                         cpr.launchingApp = proc;  
  88.                         mLaunchingProviders.add(cpr);  
  89.                     } finally {  
  90.                         Binder.restoreCallingIdentity(origId);  
  91.                     }  
  92.                 }  
  93.   
  94.                 // Make sure the provider is published (the same provider class  
  95.                 // may be published under multiple names).  
  96.                 if (firstClass) {  
  97.                     mProviderMap.putProviderByClass(comp, cpr);  
  98.                 }  
  99.   
  100.                 mProviderMap.putProviderByName(name, cpr);  
  101.                 conn = incProviderCountLocked(r, cpr, token, stable);  
  102.                 if (conn != null) {  
  103.                     conn.waiting = true;  
  104.                 }  
  105.             }  
  106.         }  
  107.   
  108.         // Wait for the provider to be published...  
  109.         synchronized (cpr) {  
  110.             while (cpr.provider == null) {  
  111.                 if (cpr.launchingApp == null) {  
  112.                ...  
  113.                     return null;  
  114.                 }  
  115.                 try {  
  116.               ...  
  117.                     if (conn != null) {  
  118.                         conn.waiting = true;  
  119.                     }  
  120.                     cpr.wait();  
  121.                 } catch (InterruptedException ex) {  
  122.                 } finally {  
  123.                   ...  
  124.                 }  
  125.             }  
  126.         }  
  127.         return cpr != null ? cpr.newHolder(conn) : null;  
  128.     }  

这个函数比较长,我们一步一步地分析。
函数首先是获取调用者的进程记录块信息:

[java]  view plain  copy
  1. ProcessRecord r = null;  
  2.            if (caller != null) {  
  3.                r = getRecordForAppLocked(caller);  
  4.                if (r == null) {  
  5.                ...  
  6.                }  
  7.            }  

在我们这个情景中,要获取的就是第二个应用程序的进程记录块信息了,后面会用到。

在ActivityManagerService中,用mProviderMap保存系统中的ContentProvider信息的。这里ContentProviderRecord对象就封装了ContentProvider的相关信息,下面的代码就是用来检查要获取的ContentProvider是否已经加存在的了:

[java]  view plain  copy
  1. cpr = mProviderMap.getProviderByName(name, userId);  
  2.            boolean providerRunning = cpr != null;  
  3.            if (providerRunning) {  
  4.                ....  
  5.            }  
  6.   
  7.            boolean singleton;  
  8.            if (!providerRunning) {  
  9.                try {  
  10.                    cpi = AppGlobals.getPackageManager().  
  11.                        resolveContentProvider(name,  
  12.                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);  
  13.                } catch (RemoteException ex) {  
  14.                }  
  15.                if (cpi == null) {  
  16.                    return null;  
  17.                }  
  18.            .  
  19.                cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);  
  20.   
  21.           ...  
  22.   
  23.                ComponentName comp = new ComponentName(cpi.packageName, cpi.name);  
  24.                cpr = mProviderMap.getProviderByClass(comp, userId);  
  25.                final boolean firstClass = cpr == null;  
  26.                if (firstClass) {  
  27.                    try {  
  28.                        ApplicationInfo ai =  
  29.                            AppGlobals.getPackageManager().  
  30.                                getApplicationInfo(  
  31.                                        cpi.applicationInfo.packageName,  
  32.                                        STOCK_PM_FLAGS, userId);  
  33.                        if (ai == null) {  
  34.                          ...  
  35.                        }  
  36.                        ai = getAppInfoForUser(ai, userId);  
  37.                        cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);  
  38.                    } catch (RemoteException ex) {  
  39.                       ..  
  40.                    }  
  41.                }  

 在我们这个情景中,由于是第一次调用MyContentProvider,因此,在mProviderMap中不存在MyContentProvider的相关信息,因此,这里会通过AppGlobals.getPackageManager函数来获得PackageManagerService服务接口,然后分别通过它的resolveContentProvider和getApplicationInfo函数来分别获取MyContentProvider所在应用程序的相关信息,分别保存在cpi和cpr这两个本地变量中。这些信息都是在安装应用程序的过程中保存下来的.

[java]  view plain  copy
  1. // This is single process, and our app is now connecting to it.  
  2.                // See if we are already in the process of launching this  
  3.                // provider.  
  4.                final int N = mLaunchingProviders.size();  
  5.                int i;  
  6.                for (i=0; i<N; i++) {  
  7.                    if (mLaunchingProviders.get(i) == cpr) {  
  8.                        break;  
  9.                    }  
  10.                }  
系统中所有正在加载的ContentProvider都保存在mLaunchingProviders成员变量中。在加载相应的ContentProvider之前,首先要判断一下它是可否正在被其它应用程序加载,如果是的话,就不用重复加载了。在我们这个情景中,没有其它应用程序也正在加载MyContentProvider这个Content Provider,继续往前执行:

[java]  view plain  copy
  1. // If the provider is not already being launched, then get it  
  2.                 // started.  
  3.                 if (i >= N) {  
  4.                     try {  
  5.                         // Content provider is now in use, its package can't be stopped.  
  6.                         try {  
  7.                             AppGlobals.getPackageManager().setPackageStoppedState(  
  8.                                     cpr.appInfo.packageName, false, userId);  
  9.                         } catch (RemoteException e) {  
  10.                         } catch (IllegalArgumentException e) {  
  11.                  ....  
  12.                         }  
  13.                         ProcessRecord proc = startProcessLocked(cpi.processName,  
  14.                                 cpr.appInfo, false0"content provider",  
  15.                                 new ComponentName(cpi.applicationInfo.packageName,  
  16.                                         cpi.name), falsefalse);  
  17.                         if (proc == null) {  
  18.                            ...  
  19.                         }  
  20.                         cpr.launchingApp = proc;  
  21.                         mLaunchingProviders.add(cpr);  
  22.                     } finally {  
  23.                         Binder.restoreCallingIdentity(origId);  
  24.                     }  
  25.                 }  
 这里的条件i >= N为true,就表明没有其它应用程序正在加载这个ContentProvider,因此,就要调用startProcessLocked函数来启动一个新的进程来加载这个ContentProvider对应的类了,然后把这个正在加载的信息增加到mLaunchingProviders中去。我们先接着分析这个函数,然后再来看在新进程中加载ContentProvider的过程,继续往下执行:

[java]  view plain  copy
  1. // Make sure the provider is published (the same provider class  
  2.                // may be published under multiple names).  
  3.                if (firstClass) {  
  4.                    mProviderMap.putProviderByClass(comp, cpr);  
  5.                }  
  6.                mProviderMap.putProviderByName(name, cpr);  
  7.                conn = incProviderCountLocked(r, cpr, token, stable);  
  8.                if (conn != null) {  
  9.                    conn.waiting = true;  
  10.                }  
 这段代码把这个ContentProvider的信息保存到mProviderMap中去,以方便后续查询。

 因为我们需要获取的ContentProvider是在新的进程中加载的,而getContentProviderImpl()这个函数是在系统进程中执行的,它必须要等到要获取的ContentProvider是在新的进程中加载完成后才能返回,这样就涉及到进程同步的问题了。这里使用的同步方法是不断地去检查变量cpr的provider域是否被设置了。当要获取的ContentProvider在新的进程加载完成之后,它会通过Binder进程间通信机制调用到系统进程中,把这个cpr变量的provider域设置为已经加载好的IContentProvider接口,这时候,函数getContentProviderImpl()就可以返回了。下面的代码就是用来等待要获取的ContentProvider,是在新的进程中加载完成的:

[java]  view plain  copy
  1. // Wait for the provider to be published...  
  2.         synchronized (cpr) {  
  3.             while (cpr.provider == null) {  
  4.                 if (cpr.launchingApp == null) {  
  5.                    ...  
  6.                     return null;  
  7.                 }  
  8.                 try {  
  9.                    ..  
  10.                     if (conn != null) {  
  11.                         conn.waiting = true;  
  12.                     }  
  13.                     cpr.wait();  
  14.                 } catch (InterruptedException ex) {  
  15.                 } finally {  
  16.                 ...  
  17.                 }  
  18.             }  
  19.         }  
  20.         return cpr != null ? cpr.newHolder(conn) : null;  

cpr就是ContentProviderRecord,它的provider域就是IContentProvider,看一下类图



下面我们再分析在新进程中加载MyContentProvider这个ContentProvider的过程。其实就是启动demo的第一个应用程序.

这里我们参考分析点击android桌面app图标启动应用程序的过程这篇文章,其中就是在新的进程启动第一个activity,与这里启动ContentProvider差不多.

所以参考文章中的第三十一步至第四十二步

首先看第三十七

第九步:attachApplication()

在ActivityManagerService.java中

[java]  view plain  copy
  1. private final boolean attachApplicationLocked(IApplicationThread thread,  
  2.           int pid) {  
  3.   
  4.       // Find the application record that is being attached...  either via  
  5.       // the pid if we are running in multiple processes, or just pull the  
  6.       // next app record if we are emulating process with anonymous threads.  
  7.       ProcessRecord app;  
  8.  .....  
  9.   
  10.       boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);  
  11.       List providers = normalMode ? generateApplicationProvidersLocked(app) : null;  
  12.   
  13.   ....  
  14.           thread.bindApplication(processName, appInfo, providers,  
  15.                   app.instrumentationClass, profileFile, profileFd, profileAutoStop,  
  16.                   app.instrumentationArguments, app.instrumentationWatcher, testMode,  
  17.                   enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent,  
  18.                   new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),  
  19.                   mCoreSettingsObserver.getCoreSettingsLocked());  
  20.           updateLruProcessLocked(app, false);  
  21.           app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();  
  22.       } catch (Exception e) {  
  23.         ...  
  24.       }  
  25.   
  26.    ...  
  27.       ActivityRecord hr = mMainStack.topRunningActivityLocked(null);  
  28.   .....  
  29.   
  30.       return true;  
  31.   }  
这个函数首先是根据传进来的进程ID找到相应的进程记录块,注意,这个进程ID是MyContentProvider所在程序的ID,然后对这个进程记录块做一些初倾始化的工作。再接下来通过调用generateApplicationProvidersLocked()获得需要在这个过程中加载的Content Provider列表,在我们这个情景中,就只有MyContentProvider这个ContentProvider了。最后调用从参数传进来的IApplicationThread对象thread的bindApplication函数来执行一些应用程序初始化工作。

去到第四十二步:

第十步:handleBindApplication()
在ActivityThread.java中

[java]  view plain  copy
  1. private void handleBindApplication(AppBindData data) {  
  2.      ....  
  3.       try {  
  4.    .....  
  5.           if (!data.restrictedBackupMode) {  
  6.               List<ProviderInfo> providers = data.providers;  
  7.               if (providers != null) {  
  8.                   installContentProviders(app, providers);//安装contentprovider  
  9.           .....  
  10.               }  
  11.           }  
  12.   
  13.           // Do this after providers, since instrumentation tests generally start their  
  14.           // test thread at this point, and we don't want that racing.  
  15.           try {  
  16.               mInstrumentation.onCreate(data.instrumentationArgs);  
  17.           }  
  18.           catch (Exception e) {  
  19.              ..  
  20.           }  
  21.   
  22.           try {  
  23.               mInstrumentation.callApplicationOnCreate(app);  
  24.           } catch (Exception e) {  
  25.           ...  
  26.           }  
  27.       } finally {  
  28.           StrictMode.setThreadPolicy(savedPolicy);  
  29.       }  
  30.   }  

这个函数的内容比较多,我们忽略了其它无关的部分,只关注和ContentProvider有关的逻辑,这里主要就是调用installContentProviders函数来在本地安装ContentProviders信息。

第十一步:installContentProviders()
在ActivityThread.java中

[java]  view plain  copy
  1. private void installContentProviders(  
  2.            Context context, List<ProviderInfo> providers) {  
  3.        final ArrayList<IActivityManager.ContentProviderHolder> results =  
  4.            new ArrayList<IActivityManager.ContentProviderHolder>();  
  5.   
  6.        for (ProviderInfo cpi : providers) {  
  7.         ...  
  8.            IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,  
  9.                    false /*noisy*/true /*noReleaseNeeded*/true /*stable*/);  
  10.            if (cph != null) {  
  11.                cph.noReleaseNeeded = true;  
  12.                results.add(cph);  
  13.            }  
  14.        }  
  15.   
  16.        try {  
  17.            ActivityManagerNative.getDefault().publishContentProviders(  
  18.                getApplicationThread(), results);  
  19.        } catch (RemoteException ex) {  
  20.        }  
  21.    }  
 这个函数主要是做了两件事情,一是调用installProvider()来在本地安装每一个ContentProivder的信息,并且为每一个ContentProvider创建一个ContentProviderHolder对象来保存相关的信息。ContentProviderHolder对象是一个Binder对象,是用来把ContentProvider的信息传递给ActivityManagerService服务的。当这些ContentProvider都处理好了以后,还要调用ActivityManagerService服务的publishContentProviders()函数来通知ActivityManagerService服务这个进程中所要加载的ContentProvider都已经准备完毕了,而ActivityManagerService服务的publishContentProviders()函数的作用就是用来唤醒在前面第八步等待的线程的了。我们先来看installProvider()的实现,然后再来看ActivityManagerService服务的publishContentProviders()函数的实现。

第十二步:installProvider()
在ActivityThread.java中

[java]  view plain  copy
  1. private IActivityManager.ContentProviderHolder installProvider(Context context,  
  2.            IActivityManager.ContentProviderHolder holder, ProviderInfo info,  
  3.            boolean noisy, boolean noReleaseNeeded, boolean stable) {  
  4.        ContentProvider localProvider = null;  
  5.        IContentProvider provider;  
  6.        if (holder == null || holder.provider == null) {  
  7.          ...  
  8.            Context c = null;  
  9.            ApplicationInfo ai = info.applicationInfo;  
  10.            if (context.getPackageName().equals(ai.packageName)) {  
  11.                c = context;  
  12.            } else if (mInitialApplication != null &&  
  13.                    mInitialApplication.getPackageName().equals(ai.packageName)) {  
  14.               ..  
  15.            } else {  
  16.               ....  
  17.            }  
  18.         ...  
  19.            try {  
  20.                final java.lang.ClassLoader cl = c.getClassLoader();  
  21.                localProvider = (ContentProvider)cl.  
  22.                    loadClass(info.name).newInstance();  
  23.                provider = localProvider.getIContentProvider();  
[java]  view plain  copy
  1.  <span style="white-space:pre">   </span>  localProvider.attachInfo(c, info);  
  2.     } catch (java.lang.Exception e) {  
  3.       ...  
  4.     }  
  5. else {  
  6. ...  
  7. }  
  8. IActivityManager.ContentProviderHolder retHolder;  
  9.   
  10. synchronized (mProviderMap) {  
  11.    ...  
  12.     IBinder jBinder = provider.asBinder();  
  13.     if (localProvider != null) {  
  14.         ComponentName cname = new ComponentName(info.packageName, info.name);  
  15.         ProviderClientRecord pr = mLocalProvidersByName.get(cname);  
  16.         if (pr != null) {  
  17.         ...  
  18.         } else {  
  19.             holder = new IActivityManager.ContentProviderHolder(info);  
  20.             holder.provider = provider;  
  21.             holder.noReleaseNeeded = true;  
  22.             pr = installProviderAuthoritiesLocked(provider, localProvider, holder);  
  23.             mLocalProviders.put(jBinder, pr);  
  24.             mLocalProvidersByName.put(cname, pr);  
  25.         }  
  26.         retHolder = pr.mHolder;  
  27.     } else {  
  28.       ..  
  29. }  
  30.   
  31. return retHolder;  

  这个函数的作用主要就是在应用程序进程中把相应的ContentProvider类加载进来了,在我们这个种情景中,就是要在MyContentProvider所在应用程序中把MyContentProvider这个ContentProvider类加载到内存中来了:

[java]  view plain  copy
  1. final java.lang.ClassLoader cl = c.getClassLoader();  
  2.               localProvider = (ContentProvider)cl.  
  3.                   loadClass(info.name).newInstance();  
 接着通过调用localProvider(ContentProvider类型)的getIContentProvider()函数来获得一个Binder对象(IContentProvider类型),将这个Binder对象赋值给ContentProviderHolder对象的内部变量provider,ContentProviderHolder返回,传到ActivityManagerService中去,后续其他应用程序就是通过获得这个ContentProviderHolder对象的内部IContentProvider对象来和相应的ContentProvider进行通信的了。我们先看一下这个函数的实现,然后再回到installProvider()函数中继续分析。

第十三步:getIContentProvider()

ContentProvider.java中

[java]  view plain  copy
  1. public IContentProvider getIContentProvider() {  
  2.       return mTransport;  
  3.   }  
[java]  view plain  copy
  1. private Transport mTransport = new Transport();  


从这里我们可以看出,ContentProvider类和Transport类的关系就类似于ActivityThread和ApplicationThread的关系,其它应用程序不是直接调用ContentProvider接口来访问它的数据,而是通过调用它的内部对象mTransport来间接调用ContentProvider的接口.
 回到前面的installProvider()函数中,它接下来调用下面接口来初始化刚刚加载好的ContentProvider:

[java]  view plain  copy
  1. localProvider.attachInfo(c, info);  
 同样,我们先进入到ContentProvider类的attachInfo()函数去看看它的实现,然后再回到installProvider()函数来。

第十四步:attachInfo()

ContentProvider.java中

[java]  view plain  copy
  1. public void attachInfo(Context context, ProviderInfo info) {  
  2.        /* 
  3.         * We may be using AsyncTask from binder threads.  Make it init here 
  4.         * so its static handler is on the main thread. 
  5.         */  
  6.        AsyncTask.init();  
  7.   
  8.        /* 
  9.         * Only allow it to be set once, so after the content service gives 
  10.         * this to us clients can't change it. 
  11.         */  
  12.        if (mContext == null) {  
  13.            mContext = context;  
  14.            mMyUid = Process.myUid();  
  15.            if (info != null) {  
  16.                setReadPermission(info.readPermission);  
  17.                setWritePermission(info.writePermission);  
  18.                setPathPermissions(info.pathPermissions);  
  19.                mExported = info.exported;  
  20.            }  
  21.            ContentProvider.this.onCreate();  
  22.        }  
  23.    }  

这个函数很简单,主要就是根据这个Content Provider的信息info来设置相应的读写权限,然后调用它的子类的onCreate函数来让子类执行一些初始化的工作。在我们这个情景中,这个子类就是MyContentProvider所在应用程序中的MyContentProvider类了。

回到前面第十二步的installProvider()函数中,它接下来就是把这些在本地中加载的ContentProvider信息保存下来了,以方便后面查询和使用,是在

installProviderAuthoritiesLocked()这个方法里面去执行的

[java]  view plain  copy
  1. ...  
  2.                     pr = installProviderAuthoritiesLocked(provider, localProvider, holder);  
  3.         ..  

 

[java]  view plain  copy
  1. private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,  
  2.             ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {  
  3.         final String auths[] = PATTERN_SEMICOLON.split(holder.info.authority);  
  4.         final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);  
  5.   
  6.         final ProviderClientRecord pcr = new ProviderClientRecord(  
  7.                 auths, provider, localProvider, holder);  
  8.         for (String auth : auths) {  
  9.             final ProviderKey key = new ProviderKey(auth, userId);  
  10.             final ProviderClientRecord existing = mProviderMap.get(key);  
  11.             if (existing != null) {  
  12.                 Slog.w(TAG, "Content provider " + pcr.mHolder.info.name  
  13.                         + " already published as " + auth);  
  14.             } else {  
  15.                 mProviderMap.put(key, pcr);  
  16.             }  
  17.         }  
  18.         return pcr;  
  19.     }  

函数installProvider()执行完成以后,返回到第十一步中的instalContentProviders()函数中,执行下面语句:

[java]  view plain  copy
  1. ActivityManagerNative.getDefault().publishContentProviders(  
  2.               getApplicationThread(), results);  

前面已经提到,这个函数调用的作用就是通知ActivityMangerService,需要在这个进程中加载的Content Provider已经完加载完成了,参数results就包含了这些已经加载好的ContentProvider接口。

第十五步:publishContentProviders()

在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中

[java]  view plain  copy
  1. public final void publishContentProviders(IApplicationThread caller,  
  2.         List<ContentProviderHolder> providers) {  
  3.     if (providers == null) {  
  4.     ..  
  5.     }  
  6.   
  7.     synchronized (this) {  
  8.         final ProcessRecord r = getRecordForAppLocked(caller);  
  9.       ..  
  10.         if (r == null) {  
  11.         ..  
  12.         }  
  13.    ...  
  14.         final int N = providers.size();  
  15.         for (int i=0; i<N; i++) {  
  16.             ContentProviderHolder src = providers.get(i);  
  17.             if (src == null || src.info == null || src.provider == null) {  
  18.                 continue;  
  19.             }  
  20.             ContentProviderRecord dst = r.pubProviders.get(src.info.name);  
  21.           ..  
  22.             if (dst != null) {  
  23.                 ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);  
  24.                 mProviderMap.putProviderByClass(comp, dst);  
  25.                 String names[] = dst.info.authority.split(";");  
  26.                 for (int j = 0; j < names.length; j++) {  
  27.                     mProviderMap.putProviderByName(names[j], dst);  
  28.                 }  
  29.   
  30.                 int NL = mLaunchingProviders.size();  
  31.                 int j;  
  32.                 for (j=0; j<NL; j++) {  
  33.                     if (mLaunchingProviders.get(j) == dst) {  
  34.                         mLaunchingProviders.remove(j);  
  35.                         j--;  
  36.                         NL--;  
  37.                     }  
  38.                 }  
  39.                 synchronized (dst) {  
  40.                     dst.provider = src.provider;  
  41.                     dst.proc = r;  
  42.                     dst.notifyAll();  
  43.                 }  
  44.                 updateOomAdjLocked(r);  
  45.             }  
  46.         }  
  47.   
  48.         Binder.restoreCallingIdentity(origId);  
  49.     }  
  50. }  

 在我们这个情景中,只有一个ContentProvider,因此,这里的N等于1。在中间的for循环里面,最重要的是下面这个语句:

[java]  view plain  copy
  1. ContentProviderRecord dst = r.pubProviders.get(src.info.name);  
从这里得到的ContentProviderRecord对象dst,就是在前面 第八步 中创建的ContentProviderRecord对象cpr了。在for循环中,首先是把这个ContentProvider信息保存好在 mProviderMap 中:

[java]  view plain  copy
  1. ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);  
  2.                    mProviderMap.putProviderByClass(comp, dst);  
  3.                    String names[] = dst.info.authority.split(";");  
  4.                    for (int j = 0; j < names.length; j++) {  
  5.                        mProviderMap.putProviderByName(names[j], dst);  
  6.                    }  

  因为这个Content Provider已经加载好了,因此,把它从mLaunchingProviders列表中删除:

[java]  view plain  copy
  1. int NL = mLaunchingProviders.size();  
  2.                    int j;  
  3.                    for (j=0; j<NL; j++) {  
  4.                        if (mLaunchingProviders.get(j) == dst) {  
  5.                            mLaunchingProviders.remove(j);  
  6.                            j--;  
  7.                            NL--;  
  8.                        }  
  9.                    }  
最后,设置这个ContentProviderRecord对象dst的provider域为从参数传进来的IContentProvider远程接口:
[java]  view plain  copy
  1. synchronized (dst) {  
  2.                        dst.provider = src.provider;  
  3.                        dst.proc = r;  
  4.                        dst.notifyAll();  
  5.                    }  
 执行了dst.notiryAll语句后,在 第八步 中等待要获取的ContentProvider接口加载完毕的线程就被唤醒了。唤醒之后,它检查本地ContentProviderRecord变量cpr的provider域不为null,于是就返回了。它最终返回到 第六步 中的ActivityThread类的acquireProvider()函数中,继续往下执行:
[java]  view plain  copy
  1. holder = installProvider(c, holder, holder.info,  
  2.                true /*noisy*/, holder.noReleaseNeeded, stable);  
注意,这里是在第二个应用程序中进程中执行installProvider()函数的,而前面的 第十二步 的installProvider()函数是在第一个应用程序进程中执行的。

第十六步:installProvider()

在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java]  view plain  copy
  1. private IActivityManager.ContentProviderHolder installProvider(Context context,  
  2.            IActivityManager.ContentProviderHolder holder, ProviderInfo info,  
  3.            boolean noisy, boolean noReleaseNeeded, boolean stable) {  
  4.        ContentProvider localProvider = null;  
  5.        IContentProvider provider;  
  6.        if (holder == null || holder.provider == null) {  
  7.        ....  
  8.        } else {  
  9.            provider = holder.provider;  
  10.     ....  
  11.        }  
  12.   
  13.        IActivityManager.ContentProviderHolder retHolder;  
  14.   
  15.        synchronized (mProviderMap) {  
  16.            if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider  
  17.                    + " / " + info.name);  
  18.            IBinder jBinder = provider.asBinder();  
  19.            if (localProvider != null) {  
  20.              ...  
  21.            } else {  
  22.                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);  
  23.                if (prc != null) {  
  24.                ....  
  25.                } else {  
  26.                    ProviderClientRecord client = installProviderAuthoritiesLocked(  
  27.                            provider, localProvider, holder);  
  28.                    if (noReleaseNeeded) {  
  29.                        prc = new ProviderRefCount(holder, client, 10001000);  
  30.                    } else {  
  31.                        prc = stable  
  32.                                ? new ProviderRefCount(holder, client, 10)  
  33.                                : new ProviderRefCount(holder, client, 01);  
  34.                    }  
  35.                    mProviderRefCountMap.put(jBinder, prc);  
  36.                }  
  37.                retHolder = prc.holder;  
  38.            }  
  39.        }  
  40.   
  41.        return retHolder;  
  42.    }  
  同样是执行installProvider函数,与 第十二步 不同,这里传进来的参数provider是不为null的,因此,它不需要执行在本地加载ContentProvider的工作,只需要把从ActivityMangerService中获得的ContentProviderHolder对象中IContentProvider的保存在成员变量mProviderMap中就可以了。然后将ContentProviderHolder对象返回,回到第六步中的ActivityThread类的acquireProvider()函数中,将IContentProvider返回到第三步query()函数中,然后调用IContentProvider的query()函数,其实最终调用到了MyContentProvider类里面的query()函数了.

 上面过程就是与"com.cj.mycontentprovider"这个uri对应的ContentProvider(MyContentProvider)通信的过程,其他几个函数(inset(),delete()..)都是同样的过程.

上面的使用ContentProvider的过程中,还用到数据更新,例如我在第二个应用程序中添加到一个联系人后,回到前面一个界面,数据会刷新.

[java]  view plain  copy
  1. contentResolver.notifyChange(u,null);  
[java]  view plain  copy
  1. resolver.registerContentObserver(Uri.parse("content://com.cj.mycontentprovider/contact"),  
  2.                true,new MyContentObserver(new Handler()));  

猜你喜欢

转载自blog.csdn.net/weixin_41205419/article/details/79915816
今日推荐