Replugin ContentProvider实现机制

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这块做的还是不完善...

你可能感兴趣的:(android,replugin)