http://www.360doc.com/content/12/0925/16/9462341_238114797.shtml在我们日常工作中,有关上层应用与数据库交互的时候通常会用到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并对其监听,这样再打断点调试就会方便许多了。
sadf
最新推荐文章于 2024-01-28 08:24:42 发布