Replugin ContentProvider实现机制

来源:互联网 发布:淘宝云客服在哪报名 编辑:程序博客网 时间:2024/05/16 09:55

plugin调用ContentProvider是通过plugin-lib的PluginProviderClient类进行的,以PluginProviderClient.query为例,看一下Replugin ContentProvider的实现机制。

先总结一下调用栈

PluginProviderClient.query -- plugin-lib    PluginProviderClient.query -- host-lib(通过反射调用到)        PluginProviderClient.toCalledUri -- 关键代码        PluginPitProviderBase.query            PluginProviderHelper.getProvider().query -- 真正的providerquery

关键代码就在com.qihoo360.replugin.component.provider.PluginProviderClient中,它的几个方法如下

public static Cursor query(Context c, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {    Uri turi = toCalledUri(c, uri);    return c.getContentResolver().query(turi, projection, selection, selectionArgs, sortOrder);}public static Uri toCalledUri(Context c, Uri uri) {    String pn = fetchPluginByContext(c, uri);    if (pn == null) {        return uri;    }    return toCalledUri(c, pn, uri, IPluginManager.PROCESS_AUTO);}/** * 将从插件里的URI转化成系统传过来的URI。可自由指定在哪个进程启动。例如: * Before:  content://com.qihoo360.contacts.abc/people Contacts插件,UI * After:   content://com.qihoo360.mobilesafe.PluginUIP/contacts/com.qihoo360.mobilesafe.contacts.abc/people * * @param context 当前的Context对象,目前暂无用 * @param plugin  要使用的插件 * @param uri     URI对象 * @param process 进程信息,若为PROCESS_AUTO,则根据插件Manifest来指定进程 * @return 转换后可直接在ContentResolver使用的URI */public static Uri toCalledUri(Context context, String plugin, Uri uri, int process) {    if (TextUtils.isEmpty(plugin)) {        throw new IllegalArgumentException();    }    if (uri == null) {        throw new IllegalArgumentException();    }    if (uri.getAuthority().startsWith(PluginPitProviderBase.AUTHORITY_PREFIX)) {        // 自己已填好了要使用的插件名(以PluginUIProvider及其它为开头),这里不做处理        return uri;    }    // content://com.qihoo360.mobilesafe.PluginUIP    if (process == IPluginManager.PROCESS_AUTO) {        // 直接从插件的Manifest中获取        process = getProcessByAuthority(plugin, uri.getAuthority());        if (process == PROCESS_UNKNOWN) {            // 可能不是插件里的,而是主程序的,直接返回Uri即可            return uri;        }    }    String au;    if (process == IPluginManager.PROCESS_PERSIST) {        au = PluginPitProviderPersist.AUTHORITY;    } else if (PluginProcessHost.isCustomPluginProcess(process)) {        au = PluginProcessHost.PROCESS_AUTHORITY_MAP.get(process);    } else {        au = PluginPitProviderUI.AUTHORITY;    }    // from => content://                                                  com.qihoo360.contacts.abc/people?id=9    // to   => content://com.qihoo360.mobilesafe.Plugin.NP.UIP/plugin_name/com.qihoo360.contacts.abc/people?id=9    String newUri = String.format("content://%s/%s/%s", au, plugin, uri.toString().replace("content://", ""));    return Uri.parse(newUri);}

大体说就两步:1、转换uri;2、调用系统方法去query该uri。

  1. uri的转换。首先,根据plugin的context查询到plugin name,再根据plugin info的ComponentList查询到目标provicer配置的进程,最后根据这两个信息把原uri转换成host可以识别的uri
  2. 此时,调用系统方法query转换后的uri,就会在对应的进程中调用的host预置的对应的ContentProvider。然后就调用到了基类的query,也就是PluginPitProviderBase.query

在PluginPitProviderBase.query中,会调用PluginProviderHelper.getProvider去load对应plugin里的ContentProvider然后直接调用

Bug(2.1.3版本):
  1. plugin-lib里的PluginProviderClient.update方法,传递参数漏掉了
  2. host-lib里的PluginProviderClient,在fetchPluginByContext的时候,fetch到的是调用者plugin,相当于plugin_A调用plugin_B,查到的还是plugin_A...这样就调不到plugin_B了。有一个规避方法,uri写成这样content://com.qihoo360.replugin.sample.host.Plugin.NP.UIP/demo1/com.qihoo360.replugin.sample.demo1.provider2,即显示指定坑位和目标plugin
  3. 从FAQ看好像还有其他bug,ContentProvider这块做的还是不完善...
原创粉丝点击