现在的位置: 首页 > 综合 > 正文

关于CotentProvider的跨进程调试

2013年10月24日 ⁄ 综合 ⁄ 共 2030字 ⁄ 字号 评论关闭

    在我们日常工作中,有关上层应用与数据库交互的时候通常会用到ContentProvider这一组件。上层应用通过获得ContentResolver对象来调用其封装好的query、insert、delete、update等方法,这时通过跨进程通信会找到对应provider的相对应的query、insert等方法,在这里通过构建数据库对象来实现对SQLIte数据库的交互操作。
本文不会对ContentProvider的详细使用做过多的介绍,但是需要读者有一个大体的了解。应用代码与Provider代码都隶属于package层,不过前者在apps包下,后者在provider包下。
   下面结合一个实际问题来分析一下如何跨进程追查代码:
    DDMS的日志打印处显示出一个无效列名参数的异常,经排查发现是CallLogProvider中的query方法里传入的projection参数不正确,这时需要找到这个projection参数是如何传进来的。我们知道,上层应用通常并不是获取某个provider对象再调用其重写的query方法,而是通过ContentResolver对象,它们并不在同一个进程中,这样该如何找到时哪个进程调用了resolver的query方法进而将参数内容跨进程通信到provider的呢?
    通过查看源代码不难发现,其实CallLogProvider中的query方法是通过ContentProvider的内部类Transport中的query方法调用的,具体代码如下:
    public Cursor query(Uri uri, String[] projection,
                String selection, String[] selectionArgs, String sortOrder) {
            enforceReadPermission(uri);
            return ContentProvider.this.query(uri, projection, selection,
                    selectionArgs, sortOrder);
        }
    其中最后一句的ContentProvider.this.query就会根据java的多态机制找到CallLogProvider的query,而Transport里的query就是被ContentResolver调用的,代码如下:
    public final Cursor query(Uri uri, String[] projection,
            String selection, String[] selectionArgs, String sortOrder) {
        IContentProvider provider = acquireProvider(uri);
        if (provider == null) {
            return null;
        }
        try {
            long startTime = SystemClock.uptimeMillis();
            Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder);
   ……
}
    首先会通过acquireProvider(uri)获得一个provider对象,其实是从ActivityThread下的acquireExistingProvider方法层层调用,它会根据uri找到在AndroidMenifest.xml文件中注册的provider,然后provider.query()就会进入指定的Provider的对应方法了。
因此,要先确定是哪个进程调用了CallLogProvider的query方法,可以在Transport的query方法中加入下面这两句:
    long threadId = this.getCallingPid();
    System.out.println(threadId);
    这样我们就可以通过打印知道是哪个进程调用了这个query方法了,然后监听该进程和CallLogProvider所在的进程,在ContextWrapper类的getContentResolver()成员函数中添加断点,这样断电停下时,即可通过堆栈显示指导是哪调用了ContentResolver的query方法了,这样也就能判断它的参数是如何传进去的。
关于eclipse设置断点调试的时候,有时会遇到断点没有停下的问题,一般可能会有两点原因:一是打断点的位置并没有被执行,二是代码所在进程没有被监听。所以我们需要先打印出调用进程的id并对其监听,这样再打断点调试就会方便许多了。

抱歉!评论已关闭.