Android四大组件之四:ContentProvider

一、终于到最后一个组件了。首先运行一个Demo吧,这个Demo是在网上找的,自己修改了点:

1.ContentproviderDemoActivity演示类,用户获取ContentResolver,对ContentProvider进行操作

public class ContentproviderDemoActivity extends AppCompatActivity implements View.OnClickListener {
    private Button bt1, bt2, bt3, bt4;
    private EditText ed1, ed2;
    private ListView list1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contentprovider);
        bt1 = (Button) findViewById(R.id.bt1);
        bt2 = (Button) findViewById(R.id.bt2);
        bt3 = (Button) findViewById(R.id.bt3);
        bt4 = (Button) findViewById(R.id.bt4);
        ed1 = (EditText) findViewById(R.id.ed1);
        ed2 = (EditText) findViewById(R.id.ed2);
        list1 = (ListView) findViewById(R.id.list);

        // 显示所有数据
        list1.setAdapter(adapter(0));

        bt1.setOnClickListener(this);
        bt2.setOnClickListener(this);
        bt3.setOnClickListener(this);
        bt4.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.bt1:
                String eds1 = ed1.getText().toString();
                String eds2 = ed2.getText().toString();
                ContentValues content = new ContentValues();
                if (!eds1.equals("") && !eds2.equals("")) {
                    content.put(UserInfo.User.NAME, eds1);
                    content.put(UserInfo.User.AGE, eds2);
                    ContentproviderDemoActivity.this.getContentResolver().insert(
                            Uri.parse(UserInfo.User.uri), content);
                    Toast.makeText(ContentproviderDemoActivity.this, "数据插入成功",
                            Toast.LENGTH_LONG).show();
                    // 刷新ListView界面
                    list1.setAdapter(adapter(0));
                } else {
                    Toast.makeText(ContentproviderDemoActivity.this, "name和age不能为空",
                            Toast.LENGTH_LONG).show();
                }
                break;
            case R.id.bt2:
                String eds3 = ed1.getText().toString();
                String eds4 = ed2.getText().toString();
                if (!eds3.equals("") || !eds4.equals("")) {
                    HashMap<String, String[]> wheres = wheres(eds3, eds4);
                    String sql = wheres.get("sql")[0];
                    String[] selectags = wheres.get("selectages");
                    ContentproviderDemoActivity.this.getContentResolver().delete(
                            Uri.parse(UserInfo.User.uri), sql, selectags);

                } else {
                    Toast.makeText(ContentproviderDemoActivity.this, "请输入删除条件",
                            Toast.LENGTH_LONG).show();
                }
                // 刷新ListView界面
                list1.setAdapter(adapter(0));
                break;
            case R.id.bt3:
                String eds5 = ed1.getText().toString();
                String eds6 = ed2.getText().toString();
                ContentValues values = new ContentValues();
                // 根据条件将列修改为xiong,23
                values.put(UserInfo.User.NAME, "王安东");
                values.put(UserInfo.User.AGE, "18");
                if (!eds5.equals("") || !eds6.equals("")) {
                    HashMap<String, String[]> wheres = wheres(eds5, eds6);
                    String sql = wheres.get("sql")[0];
                    String[] selectags = wheres.get("selectages");
                    int i=ContentproviderDemoActivity.this.getContentResolver().update(
                            Uri.parse(UserInfo.User.uri), values, sql,
                            selectags);

                } else {
                    Toast.makeText(ContentproviderDemoActivity.this, "请输入删除条件",
                            Toast.LENGTH_LONG).show();
                }
                // 刷新ListView界面
                list1.setAdapter(adapter(0));
                break;
            case R.id.bt4:
                if (!ed1.getText().toString().equals("")
                        || !ed2.getText().toString().equals(""))
                    list1.setAdapter(adapter(1));
                else
                    list1.setAdapter(adapter(0));
                break;
            default:
                break;
        }
    }

    // 用来显示数据
    public SimpleAdapter adapter(int i) {
        Cursor cs = ContentproviderDemoActivity.this.getContentResolver().query(
                Uri.parse(UserInfo.User.uriall), null, null, null, null);
        String eds1 = ed1.getText().toString();
        String eds2 = ed2.getText().toString();
        if (i == 1) {
            if (!eds1.equals("") || !eds2.equals("")) {
                HashMap<String, String[]> wheres = wheres(eds1, eds2);
                String sql = wheres.get("sql")[0];
                String[] selectags = wheres.get("selectages");
                cs = ContentproviderDemoActivity.this.getContentResolver().query(
                        Uri.parse(UserInfo.User.uri), null, sql, selectags,
                        null);
            }

        }

        List<Map<String, Object>> lists = new ArrayList<Map<String, Object>>();
        while (cs!=null&&cs.moveToNext()) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("name", cs.getString(0));
            map.put("age", cs.getString(1));
            lists.add(map);
        }
        SimpleAdapter simepl = new SimpleAdapter(ContentproviderDemoActivity.this, lists,
                R.layout.item, new String[] { "name", "age" }, new int[] {
                R.id.item_txt1, R.id.item_txt2 });
        return simepl;
    }

    // 用来判别条件
    public HashMap<String, String[]> wheres(String eds1, String eds2) {
        HashMap<String, String[]> where = new HashMap<String, String[]>();
        if (!eds1.equals("") && !eds2.equals("")) {
            String[] sql = { UserInfo.User.NAME + "=? and " + UserInfo.User.AGE
                    + " =?" };
            String[] selectages = { eds1, eds2 };
            where.put("sql", sql);
            where.put("selectages", selectages);

        }
        if (!eds1.equals("") && eds2.equals("")) {
            String[] sql = { UserInfo.User.NAME + "=? " };
            String[] selectages = { eds1 };
            where.put("sql", sql);
            where.put("selectages", selectages);

        }
        if (eds1.equals("") && !eds2.equals("")) {
            String[] sql = { UserInfo.User.AGE + " =?" };
            String[] selectages = { eds2 };
            where.put("sql", sql);
            where.put("selectages", selectages);

        }
        return where;
    }
}
2.ContentProvider类,对数据库进行的CRUD操作

public class FirstContentProvider extends ContentProvider {

    // UriMatcher类主要用来匹配Uri
    private static final UriMatcher uriMatcher = new UriMatcher(
            UriMatcher.NO_MATCH);
    private MySqlite mysqlite;
    static {
        // 注册向外部程序提供的Uri
        uriMatcher.addURI(UserInfo.AUTOR, "userinfo", 1);
        uriMatcher.addURI(UserInfo.AUTOR, "userinfoall", 2);
    }
    //删除数据
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int number = 0;
        if (uriMatcher.match(uri) == 1) {
            // 根据条件删除数据,并获取删除的行数
            number = mysqlite.getReadableDatabase().delete("user_info",
                    selection, selectionArgs);
        }
        // 通知数据已经改变
        getContext().getContentResolver().notifyChange(uri, null);
        return number;
    }

    @Override
    public String getType(Uri uri) {

        return null;
    }
    //插入数据
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        String name = values.getAsString(UserInfo.User.NAME).toString();
        String age = values.getAsInteger(UserInfo.User.AGE).toString();
        String maxId = "select max(id) id from user_info";
        Cursor cursor = mysqlite.getReadableDatabase().rawQuery(maxId, null);
        cursor.moveToFirst();
        int userid = cursor.getInt(0) + 1;
        if (uriMatcher.match(uri) == 1) {

            mysqlite.getWritableDatabase().execSQL(
                    "insert into user_info values(?,?,?)",
                    new String[] { String.valueOf(userid), name, age });
        }
        return uri;
    }

    // 连接数据库
    @Override
    public boolean onCreate() {
        mysqlite = new MySqlite(getContext(), "userinfo.db", null, 1);
        return true;
    }
    //查询数据
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        SQLiteDatabase sqlite = mysqlite.getReadableDatabase();
        String str = "select name,age from user_info";
        if (uriMatcher.match(uri) == 1) {
            str += " where " + selection;
        }
        Cursor cursor = sqlite.rawQuery(str, selectionArgs);
        return cursor;
    }
    //修改数据
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        SQLiteDatabase sqlite = mysqlite.getReadableDatabase();
        int number = 0;
        if (uriMatcher.match(uri) == 1) {
            number = sqlite.update("user_info", values, selection,
                    selectionArgs);
        }
        return number;
    }

}
3.数据库表格的建立和更新

public class MySqlite extends SQLiteOpenHelper {

    static final String sql = "create table user_info(id int,name varchar(30),age int)";

    public MySqlite(Context context, String name, SQLiteDatabase.CursorFactory factory,
                    int version) {
        super(context, name, factory, version);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        //创建数据表
        db.execSQL(sql);

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // TODO Auto-generated method stub

    }

}
4.内部封装了访问数据库的uri,字段等内容

public class UserInfo {

    // 获取ContentProvider的“域名”
    public static final String AUTOR = "com.android.xiong.ConentProviderTestA.firstContentProvider";

    //定义一个静态内部类,提供ContentProvider可操作的列
    public static final class User  {

        public  static final  String ID="id";
        public static final String NAME="name";
        public static final String AGE="age";
        //定义该content提供服务的一个Uri
        public static final String uri="content://"+AUTOR+"/userinfo";
        public static final String uriall="content://"+AUTOR+"/userinfoall";

    }

}
5.activity_contentprovider.xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <ScrollView
        android:id="@+id/scr1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <Button
                android:id="@+id/bt1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="添加" />

            <Button
                android:id="@+id/bt2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="删除" />

            <Button
                android:id="@+id/bt3"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="更改" />

            <Button
                android:id="@+id/bt4"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="查询" />

            <EditText
                android:id="@+id/ed1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输入name条件进行增删改查" />

            <EditText
                android:id="@+id/ed2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="输age条件进行增删改查" />
        </LinearLayout>
    </ScrollView>

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

</LinearLayout>

6.item.xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <TextView
        android:id="@+id/item_txt1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/item_txt2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>


</LinearLayout> 
7.AndroidManifest.xml需要增加的代码

<activity android:name="com.example.wangandong.share.contentproviderdemo.ContentproviderDemoActivity"/>
<provider
            android:authorities="com.android.xiong.ConentProviderTestA.firstContentProvider"
            android:name="com.example.wangandong.share.contentproviderdemo.FirstContentProvider"/>

这样就可以演示ContentProvider了。

二、下面详细介绍下ContentProvider:

1.ContentProvider在Android中的作用是对外提供数据,除了可以为所在应用提供数据外,还可以共享数据给其他应用,这是Android中解决应用之间数据共享的机制。通过ContentProvider我们可以对数据进行增删改查的操作。但要处理好数据的安全性。

2.继承ContentProvider需要重写它的几个方法,在代码都有体现,都是在这里对数据库进行CRUD的。

3.在AndroidManifest注册我们的ContentProvider:

<provider android:authorities="list"
          android:enabled=["true" | "false"]
          android:exported=["true" | "false"]
          android:grantUriPermissions=["true" | "false"]
          android:icon="drawable resource"
          android:initOrder="integer"
          android:label="string resource"
          android:multiprocess=["true" | "false"]
          android:name="string"
          android:permission="string"
          android:process="string"
          android:readPermission="string"
          android:syncable=["true" | "false"]
          android:writePermission="string">
介绍:

android:authorities

标识内容提供器范围内的数据URI的授权列表,有多个授权时,要用分号来分离每个授权。为了避免冲突,授权名应该使用Java样式的命名规则(如:com.example.provider.cartoonprovider)。通常,用ContentProvider子类名称来设定这个属性。

这个属性没有默认值,至少要指定一个授权。

android:enabled

这个属性用于指定这个内容提供器是否能够被系统安装。设置为true,则可以安装;否则不能安装。默认值是true。<application>元素有它自己的enabled属性,这个属性会应用给所有的应用程序组件,包括内容提供器。<application>和<provider>的enabled属性都必须设置为true(它们的默认值都是true)。如果有一个设置为false,那么提供器就被禁止安装。

android:exported

这个属性用于指定该内容提供器是否能够被其他的应用程序组件使用。如果设置为true,则可以被使用,否则不能被使用。如果设置为false,该提供器只对同名的应用程序或有相同用户ID的应用程序有效。默认值是true。虽然能够使用这个属性来公开内容提供器,但是依然还要用permission属性来限制对它的访问。

android:grantUriPermission

这个属性用于设定那些对内容提供的数据没有访问权限的访问者,是否能够被授予访问的权限,这个权限是临时性的,它会克服由readPermission、writePermission和permission属性的设置限制。如果这个属性设置为true,那么权限就可以授予访问者,否则不会授予没有访问权限的访问者。如果设置为true,则权限可以临时被授予内容提供器的任何数据。如果设置为false,则权限只能被授予<gran-uri-permission>子元素中所列出的数据子集。默认值是false。

这种授权是应用程序提供了一种一次性访问被权限所保护的数据的方法。例如,当一个e-mail包含了一个附件时,mail应用程序就可以调用适当的浏览器来打开附件,即使这个浏览器没有查看所有内容提供器数据的权限。

在这样的场景中,权限是通过激活组件的Intent对象中的FLAG_GRANT_READ_URI_PERMISSION和FLAG_GRANT_WRITE_URI_PERMISSION标记来授予的。例如,mail应用程序可以把FLAG_GRANT_READ_URI_PERMISSION标记放到传递给Context.startActivity()方法的Intent参数中。这样权限就被授予了Intent对象中所指定的URI。

如果要启用这个功能,既可以通过把这个属性设置为true来完成,也可以通过定义<grant-uri-permission>子元素来实现,在切换RUI时,必须调用Context.revokeUriPermission()方法从提供器把权限删除。

android:icon

这个属性用于定义一个代表内容提供器的图标。它必须要引用一个包含图片定义的可绘制资源。如果这个属性没有设置,那么就会使用应用程序的<application>元素的icon属性值来代替。

android:initOrder

这个属性用于定义内容提供器应该被实例化的顺序,这个顺序是相对与相同进程所拥有的其他内容提供器的。当内容提供器间有相互的依赖时,就需要设置这个属性,以确保它们能够按照其依赖的顺序被创建。这个属性值是一个简单的整数,大的数字要被优先初始化。

Android:label

这个属性用于给内容提供器定义一个用户可读的标签。如果这个属性没有设置,那么它会使用<application>元素的label属性值来代替。这个标签应该引用一个字符串资源来设置,以便它能够像其他的用户界面中的字符串一样被本地化。但是为了方便应用程序的开发,也能够使用原生的字符串来设置这个属性,但正式发布时一定要引用字符串资源。

android:multiprocess

这个属性用于设定是否能够在每个使用该内容提供器的客户端进程中都创建一个内容提供器的实例,如果设置为true,这个能够在其每个客户端进程中创建一个实例,否则不可以。默认值是false。通常,内容提供器是在定义它的应用程序的进程中被实例化的。但是,如果这个属性被设置为true,系统就能够在每个要与该内容提供器进行交互的客户端进程中创建一个内容提供器的实例,这样就避免了进程间通信的开销。

android:name

这个属性用于定义内容提供器的实现类的名称,它是ContentProvider类的一个子类。这个属性应该使用完整的Java类名来设定(如:com.example.project.TransportationProvider)。但是也可以使用简写(如:.TransporttationProvider),这时系统会使用<manifest>元素中指定的包名跟这个简写名称的组合来识别内容提供器。这个属性没有默认值,必须要给这个属性设定一个名称。

android:permission

这个属性用于设定客户端在读写内容提供器的数据时必须要有的权限的名称。这个属性为同时设置读写权限提供了一种便利的方法。但是readPermission和writePermission属性的优先级要比这个属性高。如果readPermission属性也被设置了,那么它就会控制对内容提供器的查询访问。如果writePermission属性被设置,它就会控制对内容提供器数据的修改访问。

android:process

这个属性用于定义内容提供器应该运行在那个进程中的进程名称。通常,应用程序的所有组件都运行在给应用程序创建的默认进程中。它有与应用程序包相同的名称。<application>元素的process属性能够给其所有的组件设置一个不同的默认进程。但是每个组件都能够用它们自己的process属性来覆盖这个默认设置,从而允许把应用程序分离到不同的多个进程中。

如果这个属性值是用“:”开头的,那么在需要这个提供器的时候,系统就会给这个应用程序创建一个新的私有进程,并且对应的Activity也要运行在那个私有进程中。如果用小写字母开头,那么Activity则会运行在一个用这个属性值命名的全局进程中,它提供了对内容提供器的访问权限。这样就允许不同应用程序的组件能够共享这个进程,从而减少对系统资源的使用。

android:readPermission

这个属性用于设置查询内容提供器的数据时,客户端所必须要有的权限。

android:syncable

这个属性用于设定内容提供器控制下的数据是否要与服务器上的数据进行同步,如果设置为true,则要同步,否则不需要同步。

android:writePermission

这个属性用于设置修改内容提供器的数据时,客户端所必须要有的权限。

有关注册详解参考:http://blog.csdn.net/winson_jason/article/details/81158464.Uri介绍 Uri 代表了要操作的数据,Uri 主要包含了两部分信息:1、需要操作的 ContentProvider;2、对ContentProvider中的什么数据进行操作。 一个Uri由以 下几部分组成: 


ContentProvider的scheme已经由Android所规定,scheme为content://。 
主机名(authorities)用于唯一标识这个ContentProvider,外部调用者可以根据它找到对应的内容提供者(ContentProvider)。 
路径(Path)可以用来表示我们要操作的数据,路径的构建应该根据业务而定,
5.UriMatcher类介绍

因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。对输入的uri进匹配,如果匹配正确就返回匹配码,匹配码是addUri()方法传入的第三个数。

6.ContentUris介绍

ContentUris用于操作Uri后面的ID 部分,它有两个实用的方法

//在uri后面加上id
Uri resultUri = ContentUris.withAppendedId(uri, 10);
//返回uri的id
long personid = ContentUris.parseId(resultUri);  
7.我们还可以监听ContentProvider数据的变化

如果ContentProvider的访问者需要知道ContentProvider中数据发生变化,可以在ContentProvider中数据发生变化时调用getContentResovler().notifyChange(uri, null)来通知注册在此uri上的访问者,如下:

public class PersonContentProvider extends ContentProvider {   
   public Uri insert(Uri uri, ContentValues values) {   
      db.insert("person", "personid", values);   
      getContext().getContentResolver().notifyChange(uri, null);   
   }   
} 
如果ContentProvider 的访问者需要得到数据变化通知,必须使用 ContentObserver对数据(数据采用Uri描述)进行监听,当监听到数据变化通 知时,系统就会调用ContentObserver的onChange()方法,如下:
getContentResolver().registerContentObserver(Uri.parse("content://com.android.xiong.ConentProviderTestA.firstContentProvider"),   
       true, new PersonObserver(new Handler()));   
public class PersonObserver extends ContentObserver{   
   public PersonObserver(Handler handler) {   
      super(handler);   
   }   
   public void onChange(boolean selfChange) {   
      //此处可以进行相应的业务处理   
   }   
}  

ContentProvider的工作过程:

1.ContentProvider是一种内容共享型组件,它通过Binder向其他组件和其他应用提供数据;当ContentProvider所在的进程启动时,ContentProvider会同时启动并被发布到AMS中。需要说明一下,这个时候ContentProvider的onCreat要先于Application的onCreat执行,这是其他三大组件所不能的。

当应用启动时入口是ActivityThread的main方法,然后通过binder与AMS进行通信,注意它们属于不同的进程,最后在handleBindApplication方法中会创建ActivityThread会创建Application对象并加载ContentProvider。这个地方会先加载ContentProvider,然后再调用Application的onCreat方法。


2.访问ContentProvider需要通过ContentResolver,ContentResolver是一个抽象类,通过Context的getContentResolver方法获取的实际上ApplicationContentResolver

ContextWrapper的调用

@Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }
ContextImpl的调用

@Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }
mContentResolver就是 ApplicationContentResolver,它继承了ContentResolver,并实现了里面的抽象方法

3.通过 ContentProvider四个任何一个方法都可以触发ContentProvider的启动,这里选择query方法进行分析,ContentResolver的query方法实现:

public final @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
            @Nullable String selection, @Nullable String[] selectionArgs,
            @Nullable String sortOrder) {
        return query(uri, projection, selection, selectionArgs, sortOrder, null);
    }
public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection,
            @Nullable String selection, @Nullable String[] selectionArgs,
            @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
        ......
        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        ......
        IContentProvider stableProvider = null;
        Cursor qCursor = null;
        try {
            ......
            try {
                .....
            } catch (DeadObjectException e) {
                ......
                stableProvider = acquireProvider(uri);
                ......
            }
            ......
        } catch (RemoteException e) {
            ......
        } finally {
            ......
        }
    }

代码中无论是acquireUnstableProvider还是acquireProvider方法最终都是调用了 ApplicationContentResolver的acquireProvider方法
@Override
        protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), true);
        }

4.上面代码没有做任何逻辑处理,而是直接调用了ActivityThread的 acquireProvider方法

public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
            return provider;
        }

        // There is a possible race here.  Another thread may try to acquire
        // the same provider at the same time.  When this happens, we want to ensure
        // that the first one wins.
        // Note that we cannot hold the lock while acquiring and installing the
        // provider since it might take a long time to run and it could also potentially
        // be re-entrant in the case where the provider is in the same process.
        IActivityManager.ContentProviderHolder holder = null;
        try {
            holder = ActivityManagerNative.getDefault().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } catch (RemoteException ex) {
        }
        if (holder == null) {
            Slog.e(TAG, "Failed to find provider info for " + auth);
            return null;
        }

        // Install provider will increment the reference count for us, and break
        // any ties in the race.
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }
上面的代码首先会从ActivityThread中查找是否已经存在目标ContentProvider了,如果有就直接返回,ActivityThread中通过mProviderMap来存储已经启动的ContentProvider对象;如果没有启动ContentProvider,那么就会通过AMS启动目标 ContentProvider。
ContentProvider被启动时会伴随着进程的启动,而新进程启动的入口是ActivityThread的main方法。

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        AndroidKeyStoreProvider.install();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

attach的方法实现如下:

private void attach(boolean system) {
        ......
        if (!system) {
            ......
            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }
            .......
        } else {
            ......
            }
        }
        ......
    }
这里调用了AMS的attachApplication,并且将ApplicationThread传递过去

@Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }
然后又调用了attachApplicationLocked,在 attachApplicationLocked里面又调用了ApplicationThread的bindApplication方法,这个过程是进程间的调用。

thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());
bindApplication方法中,会发送一个消息给handler,如下:
public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
                Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
                Bundle coreSettings) {

            if (services != null) {
                // Setup the service cache in the ServiceManager
                ServiceManager.initServiceCache(services);
            }

            setCoreSettings(coreSettings);

            /*
             * Two possible indications that this package could be
             * sharing its runtime with other packages:
             *
             * 1.) the sharedUserId attribute is set in the manifest,
             *     indicating a request to share a VM with other
             *     packages with the same sharedUserId.
             *
             * 2.) the application element of the manifest has an
             *     attribute specifying a non-default process name,
             *     indicating the desire to run in another packages VM.
             *
             * If sharing is enabled we do not have a unique application
             * in a process and therefore cannot rely on the package
             * name inside the runtime.
             */
            IPackageManager pm = getPackageManager();
            android.content.pm.PackageInfo pi = null;
            try {
                pi = pm.getPackageInfo(appInfo.packageName, 0, UserHandle.myUserId());
            } catch (RemoteException e) {
            }
            if (pi != null) {
                boolean sharedUserIdSet = (pi.sharedUserId != null);
                boolean processNameNotDefault =
                (pi.applicationInfo != null &&
                 !appInfo.packageName.equals(pi.applicationInfo.processName));
                boolean sharable = (sharedUserIdSet || processNameNotDefault);

                // Tell the VMRuntime about the application, unless it is shared
                // inside a process.
                if (!sharable) {
                    VMRuntime.registerAppInfo(appInfo.packageName, appInfo.dataDir,
                                            appInfo.processName);
                }
            }

            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableOpenGlTrace = enableOpenGlTrace;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            sendMessage(H.BIND_APPLICATION, data);
        }

收到消息后通过handleBindApplication方法进行处理:

case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

handleBindApplication完成了Application的创建以及ContentProvider的创建:

5.创建ContextImpl和Instrumentation

ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

            try {
                java.lang.ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            } catch (Exception e) {
                throw new RuntimeException(
                    "Unable to instantiate instrumentation "
                    + data.instrumentationName + ": " + e.toString(), e);
            }

            mInstrumentation.init(this, instrContext, appContext,
                   new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
                   data.instrumentationUiAutomationConnection);

6.创建Application对象
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;
7.启动当前进程的ContentProvider并调用onCreate方法

List<ProviderInfo> providers = data.providers;
                if (providers != null) {
                    installContentProviders(app, providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
installContentProviders方法完成了ContentProvider的启动工作,首先遍历ProviderInfo的列表,并且由installProvider的启动他们,然后将已经启动的 ContentProvider发布到AMS中,AMS会把他们存储在ProviderMap中,这样外部调用者就可以通过AMS中获取ContentProvider了。

private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<IActivityManager.ContentProviderHolder> results =
            new ArrayList<IActivityManager.ContentProviderHolder>();

        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(": ");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            ActivityManagerNative.getDefault().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
        }
    }
下面看一下 installProvider是如何启动一个ContentProvider的。

private IActivityManager.ContentProviderHolder installProvider(Context context,
            IActivityManager.ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
        if (holder == null || holder.provider == null) {
            if (DEBUG_PROVIDER || noisy) {
                Slog.d(TAG, "Loading provider " + info.authority + ": "
                        + info.name);
            }
            Context c = null;
            ApplicationInfo ai = info.applicationInfo;
            if (context.getPackageName().equals(ai.packageName)) {
                c = context;
            } else if (mInitialApplication != null &&
                    mInitialApplication.getPackageName().equals(ai.packageName)) {
                c = mInitialApplication;
            } else {
                try {
                    c = context.createPackageContext(ai.packageName,
                            Context.CONTEXT_INCLUDE_CODE);
                } catch (PackageManager.NameNotFoundException e) {
                    // Ignore
                }
            }
            if (c == null) {
                Slog.w(TAG, "Unable to get context for package " +
                      ai.packageName +
                      " while loading content provider " +
                      info.name);
                return null;
            }
            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                <span style="color:#ff0000;">localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();</span>
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
                          info.name + " from sourceDir " +
                          info.applicationInfo.sourceDir);
                    return null;
                }
                if (DEBUG_PROVIDER) Slog.v(
                    TAG, "Instantiating local provider " + info.name);
                // XXX Need to create the correct context for this provider.
                localProvider.attachInfo(c, info);
            } catch (java.lang.Exception e) {
                if (!mInstrumentation.onException(null, e)) {
                    throw new RuntimeException(
                            "Unable to get provider " + info.name
                            + ": " + e.toString(), e);
                }
                return null;
            }
        } else {
            provider = holder.provider;
            if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
                    + info.name);
        }

        IActivityManager.ContentProviderHolder retHolder;

        synchronized (mProviderMap) {
            if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
                    + " / " + info.name);
            IBinder jBinder = provider.asBinder();
            if (localProvider != null) {
                ComponentName cname = new ComponentName(info.packageName, info.name);
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                if (pr != null) {
                    if (DEBUG_PROVIDER) {
                        Slog.v(TAG, "installProvider: lost the race, "
                                + "using existing local provider");
                    }
                    provider = pr.mProvider;
                } else {
                    holder = new IActivityManager.ContentProviderHolder(info);
                    holder.provider = provider;
                    holder.noReleaseNeeded = true;
                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } else {
                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
                if (prc != null) {
                    if (DEBUG_PROVIDER) {
                        Slog.v(TAG, "installProvider: lost the race, updating ref count");
                    }
                    // We need to transfer our new reference to the existing
                    // ref count, releasing the old one...  but only if
                    // release is needed (that is, it is not running in the
                    // system process).
                    if (!noReleaseNeeded) {
                        incProviderRefLocked(prc, stable);
                        try {
                            ActivityManagerNative.getDefault().removeContentProvider(
                                    holder.connection, stable);
                        } catch (RemoteException e) {
                            //do nothing content provider object is dead any way
                        }
                    }
                } else {
                    ProviderClientRecord client = installProviderAuthoritiesLocked(
                            provider, localProvider, holder);
                    if (noReleaseNeeded) {
                        prc = new ProviderRefCount(holder, client, 1000, 1000);
                    } else {
                        prc = stable
                                ? new ProviderRefCount(holder, client, 1, 0)
                                : new ProviderRefCount(holder, client, 0, 1);
                    }
                    mProviderRefCountMap.put(jBinder, prc);
                }
                retHolder = prc.holder;
            }
        }

        return retHolder;
    }

可见是通过类加载器完成的ContentProvider的对象的创建,并且通过attachInfo方法调用onCreate方法

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        mNoPerms = testing;

        /*
         * Only allow it to be set once, so after the content service gives
         * this to us clients can't change it.
         */
        if (mContext == null) {
            mContext = context;
            if (context != null) {
                mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                        Context.APP_OPS_SERVICE);
            }
            mMyUid = Process.myUid();
            if (info != null) {
                setReadPermission(info.readPermission);
                setWritePermission(info.writePermission);
                setPathPermissions(info.pathPermissions);
                mExported = info.exported;
                mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
                setAuthorities(info.authority);
            }
            ContentProvider.this.onCreate();
        }
    }

8.在ActivityThread中 handleBindApplication创建了ContentProvider并且调用了其onCreate方法后,调用Application的onCreate方法

try {
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
这时,ContentProvider已经启动成功,并且其所在进程的Application也已经启动。然后其他应用就可以通过AMS来访问这个ContentProvider了;这拿到的是ContentProvider的Binder类型的对象I ContentProvider,因为这里是进程间通信。

















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值