android系统分享分析Intent.createChooser

系统提供的分享实现代码如下:

Intent send = new Intent(Intent.ACTION_SEND); send.setType("text/plain"); send.putExtra(Intent.EXTRA_TEXT, url); send.putExtra(Intent.EXTRA_SUBJECT, title); c.startActivity(Intent.createChooser(send, "share title"));

我们先扒一扒源码这个是怎么实现分享
  • Intent.ACTION_SEND很好理解,一个隐式intent的action,在这里调用会打开对应的暴漏出该action的app对应页面(可以自己写个app,然后暴漏该action 也会出现在分享列表中)
  • 剩下就是通过intent把分享参数title text add进去
重点扒一扒Intent.createChooser
  1. createChooser()方法new Intent(ACTION_CHOOSER),返回的是一个Intent对象,弹出的选择框是一个Activity,在源码中搜索一下Action为com.android.internal.app.ChooserActivity的Activity
/android/frameworks/base/core/res/AndroidManifest.xml

  1. ChooserActivity调用了其父类ResolverActivity的OnCreate(),ResolverActivity相当于一个Activity的选择器,ChooserActivity在onCreate()方法里先获取传递进来的Intent对象,取得其中携带的目标Intent。如果只有一个Activity适配,将直接启动。 ResolverActivity(queryIntentActivities)就是这个选择对话框的Activity,这里获得适配Activity的icon和title等信息显示到对话框。如果没有调用createChooser()方法,如果有多个Activity适配一个intent的话也会打开一个选择对话框,不过这个对话框多了两个选项.
总结:这下大家应该清楚了,原来在调用createChooser()方法时候,系统又创建了一个新的Action为ACTION_CHOOSER的Intent ,并把我们的原始Intent当成了参数传进去 。选择器的title是通过 EXTRA_TITLE传入进去的。
Custom choose intent
产品或者我们自己这个分享列表提出自己的要求,比如是否可以监听这个列表点击自定义跳转、把facebook whatsapp这两个app放在第一个、或者某个app不要出现在分享列表中 等这些需求该如何实现;
  1. 监听这个列表点击自定义跳转操作 答案是 NO,弹框是系统的ChooserActivity,并不存在暴漏出列表item点击的监听回调,而且从隐式intent设计来讲,列表中的app都是与其暴漏出的intent对应匹配上的,它的初衷就是匹配上来就跳转。参考回答 想实现监听跳转只能自定义分享列表list adapter填充列表数据 跳转等,我们上个版本就是这样实现的。核心代码是获取能分享的列表数据
    Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); sharingIntent.setType("text/plain"); PackageManager pm = App.getInstance().getPackageManager(); List> listArrayList = new ArrayList<>(); List activityList = pm.queryIntentActivities(sharingIntent, 0); List newActivityList = new ArrayList<>(); for (Iterator it = activityList.iterator(); it.hasNext(); ) { ResolveInfo info = it.next(); //过滤出facebook google+whatapp分享app单独处理 if (info.activityInfo.packageName.equals("com.facebook.katana") || info.activityInfo.packageName.equals("com.google.android.apps.plus") || info.activityInfo.packageName.equals("com.facebook.orca") || info.activityInfo.packageName.contains("whatsapp")) { it.remove(); newActivityList.add(info); } }

  2. 从上面的核心代码看既然能实现分享列表数据的过滤,那么排序和删除数据都可以简单实现。但也必须是自己自定义分享弹框列表实现。上个版本就是这么做的
  3. 放google+分享列表实现(先谈一个BottomSheetDialog展示特异强调的分享app和一个other按钮,点other再进入系统的分享列表) 1.难点是用过滤的分享数据实现系统的分享。之前系统的分享数据是在ChooserActivity中自己匹配的。可喜的找到了Intent中EXTRA_INITIAL_INTENTS参数。具体看代码源码
    List targetIntents = new ArrayList(); Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("text/plain"); for (ResolveInfo candidate : packages) { String packageName = candidate.activityInfo.packageName; Intent target = new Intent(android.content.Intent.ACTION_SEND); target.setType("text/plain"); if (uri != null) { shareIntent.setType("image/*"); shareIntent.putExtra(Intent.EXTRA_STREAM, uri); } target.putExtra(Intent.EXTRA_TEXT, shareText); target.setComponent(new ComponentName(packageName, candidate.activityInfo.name)); targetIntents.add(target); } Intent chooserIntent = Intent.createChooser(targetIntents.remove(0), title); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[]{})); context.startActivity(chooserIntent);

最终展现给用户的选择包括,符合createChooser第一个参数的应用(其实是指定了packageName和className的Intent),以及由EXTRA_INITIAL_INTENTS指定的应用
疑问
  1. 注意,这里没有使用Intent.setComponent来明确指定要启动的Activity,而是通过setPackageName和setClass来指定。这是因为,在createChooser处理过程中,第一个参数intent指定的component会在ResolverActivity中强制被设置为null。这样的结果是什么呢?当component被设置为null后,targetIntents.remove(0)其实就是emailIntent,那么createChooser会显示所有满足emailIntent的应用,然后在加上由EXTRA_INITIAL_INTENTS指定的应用。你会发现选择界面中出现了重复的应用。
【android系统分享分析Intent.createChooser】2.过滤数据处理后的系统分享弹框,与原声系统分享弹框少了下面两个按钮,也少了上面最佳常用app的列表

    推荐阅读