解决android2.1中Youku每次都要选择默认播放器的问题


转载自:http://hi.baidu.com/jfojfo/item/96bafb4c18a2de2311ee1e7b
使用Android手机一段时间了,安装Youku的apk,用起来还不错,唯一有个麻烦的地方是,每次选择播放某个在线视频,都要选择一次默认播放器(我装了两个播放器),默认播放器的checkbox已经打勾了,每次还是要重新选择,应该是系统的一个bug。

前段时间自己编译过Android2.1内核,现成的源代码放在那儿,不去改改可惜了。于是决定深入到内核当中,fix掉这个bug。

每次在youku的客户端点击播放某个视频时,用adb logcat查看输出的log,可以发现很多有趣的东西,其中就显示了关于播放视频时系统产生的intent信息:

I/ActivityManager( 1185): Starting activity: Intent { act=android.intent.action.VIEW dat=http://mf.youku.com/player/getFlvPath/sid/127781412835562_01/st/mp4/fileid/03002001004C24259FEC0F03A2FF2D6D3F3E0F-67A5-7C17-DFA2-581C1828F34C?K=df0c5f6fb0f460af1825e99e&videoId=45892805 typ=video/* cmp=android/com.android.internal.app.ResolverActivity (has extras) }

可以看到这条intent对于的action是android.intent.action.VIEW,data是http://mf.youku.com/player/getFlvPath/sid... ,type为video/*,对应的compoment为ResolverActivity。
根据这一信息,找到源代码下的frameworks/base/core/java/com/android/internal/app/ResolverActivity.java,发现ResolverActivity是framework下的一个app应用,与普通的activity没什么区别,专门用于解析某个intent时选择某个特定activity进行响应的。可以找到它的layout文件always_use_checkbox.xml,和我们看到弹出来的选择对话框相一致。

为了方便调试,将该文件从framework中取出,单独建了一个工程,稍作修改也是可以运行的。
修改后的onCreate()函数,这样可以重现整个过程:
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
      
        Intent intent = new Intent();
        intent.setAction("android.intent.action.VIEW");
        Uri uri = Uri.parse("http://mf.youku.com/player/getFlvPath/sid/127754383561552_01/st/3gp/fileid/03001001004C2424CC7EF403A2FF2DF4DCB55E-B01A-02FF-289B-F9F29748954B?K=06e8287de192546a161b131e");
        intent.setDataAndType(uri, "video/*");
//        startActivity(intent);
        
        onCreate(savedInstanceState, intent,
                "which application",
                null, true);
    }

从代码中可以看到,当checkbox被选中并点击选择某个应用打开时,会执行onClick()中if语句的内容。这部分最后会调用到
getPackageManager().addPreferredActivity(filter, bestMatch, set, intent.getComponent());
将用户选择的默认程序添加进系统。

但是为什么每次还是要选择默认打开程序呢?

为此,我们一步一步跟踪到startActivity时的intent解析过程。
startActivity()最终会通过IBinder调用到ActivityManagerService服务端对应的函数,IBinder是android框架实现的一种IPC机制。
而ActivityManagerService.java中的startActivity()会调用resolveIntent()对intent解析,
resolveIntent()由PackageManagerService提供。
这两个service实现文件都位于frameworks/base/services/java/com/android/server下。

resolveIntent()的实现就两行代码:
        List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags);
        return chooseBestActivity(intent, resolvedType, flags, query);

一步步跟踪到findPreferredActivity()中,发现
List<PreferredActivity> prefs =
                    mSettings.mPreferredActivities.queryIntent(intent, resolvedType,
                            (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);
返回的结果prefs.size()为0,进一步跟踪,
IntentResolver.java:queryIntent() > buildResolveList() > match()
match()位于frameworks/base/core/java/android/content/IntentFilter.java,
    matchData()
在matchData()中,发现被match的types为video时,schemes为null,而我们要匹配的intent的type为video,scheme为http,因此返回了NO_MATCH_TYPE而失败。
可以在matchData()中加入log,可以看到更详细的types和schemes信息。

这里找到了问题所在,就是在addPreferredActivity()时,filter的设置不对,filter只添加了type(filter.addDataType(mimeType)),对于http这类scheme未被添加进filter中。
加上filter.addDataScheme(data.getScheme())后一切ok。

实际上在Android2.2中已经fix了这个问题,可以对比Android2.2的ResolverActivity.java。

Android2.1的这个bug将导致,当设置了type时,除了file、content这类scheme的intent可以解析出默认打开应用,其他的如http,就会每次都提示用户选择默认打开应用,如前面bug所示。
如果安装了UCweb,点击系统自带的Browser中的http链接地址时为什么不会出现这个问题?
用logcat查看输出信息时,可以看到点击http链接时产生的intent中只设置了scheme,type信息未被设置,因此不会出现这个bug。

花费了两天时间,终于解决这个问题!

你可能感兴趣的:(解决android2.1中Youku每次都要选择默认播放器的问题)