原标题:Android开发之桌面快捷键使用细则
国家市场监管总局网监司高度重视媒体反映的拼多多平台上销售侵权假冒商品等问题,已经要求上海市工商局约谈平台经营者,并要求上海市和其他相关地方工商、市场监管部门,对媒体反映的以及消费者、商标权利人投诉举报的拼多多平台上销售山寨产品、傍名牌等问题,认真开展调查检查,不管是第三方平台还是平台内经营者,只要构成违法,都将依法严肃处理。
作者简介
本篇来自 骑小猪看流星的投稿,分享了 Android 开发中桌面快捷键的使用细则。一起来看看!希望大家喜欢。
骑小猪看流星的博客地址:
https://www.jianshu.com/u/0111a7da544b
前言
首先声明,标题里的快捷键不是指开发人员使用频率极高的 Ctrl+C 和 Ctrl+V;也不是 IDE里 Ctrl+D、Ctrl+F 等常用快捷键。这里的快捷键,其实想要表达的是 Android 应用生成桌面快捷方式的细则。
试想,现在有一Windows 用户想进入 D 盘——my 文件夹里面的子文件去找文件(因为藏了些晦涩资源所以层级较深)。那么更加便利省力的操作是:点击选中文件夹——右键:发送到——桌面快捷方式,即可帮我们将快捷方式生成到桌面。该用户下次想使用这个文件夹,直接点击桌面上的快捷方式即可。这样做的好处在于,用户可以快速定位到某一应用具体的功能、干净利落。
当然,谷歌 Android 团队也考虑了这一点,给我们设计了原生 API,方便我们开发人员更加便利的(Ctrl+C、V)生成桌面快捷方式。这样做的好处我想有以下几点,首先,提高了用户留存率,试想一个 APP 通过某种媒介生成了2个 icon,这样是很容易吸引人的,因为生成桌面快捷方式的 icon 以及点击事件都是代码可控的,比如你的快捷方式的 icon 是一个萝莉或者御姐;正太或是直男?毕竟图片总有人会喜欢的嘛。其次,快捷方式的点击事件是控制的,跳转的界面控制在开发者(产品)手中等等。
分析一波
言归正传,既然是生成桌面快捷方式,那么肯定需要权限,必要的权限如下:
接着,因为 Android 难以言表的碎片化和厂商定制,所以还需要加一些权限来增加健壮性,下面直接 copy 就行:
代码实现
权限已经添加完毕,下面就可以上代码了,首先是创建桌面快捷方式:
//创建桌面快捷方式
privatevoidcreateShortCut(){
//创建Intent对象
Intent shortcutIntent = newIntent();
//设置点击快捷方式,进入指定的Activity
//注意:因为是从Lanucher中启动,所以这里用到了ComponentName
//其中new ComponentName这里的第二个参数,是Activity的全路径名,也就是包名类名要写全。
shortcutIntent.setComponent( newComponentName( this.getPackageName(), "这里是包名.类名"));
//给Intent添加 对应的flag
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS|Intent.FLAG_ACTIVITY_NEW_TASK);
Intent resultIntent = newIntent();
// Intent.ShortcutIconResource.fromContext 这个就是设置快捷方式的图标
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext( this,
R.drawable.yuanbao));
//启动的Intent
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
//这里可以设置快捷方式的名称
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "快捷名称");
//设置Action
resultIntent.setAction( "com.android.launcher.action.INSTALL_SHORTCUT");
//发送广播、通知系统创建桌面快捷方式
sendBroadcast(resultIntent);
}
创建桌面快捷方式的代码,理论上就是上面这些,ComponentName 这个类用的较少,简单理解 ComponentName 的作用是,可以启动其他应用的 Activity、Service(前提是要知道包名),然后搭配 Intent 使用,完成跳转。关于 ComponentName 与 Activity、Service 的参考代码如下:
ComponentName componentName = newComponentName(param1,param2);
param1:Activity、Service 所在应用的包名
//获取应用的包名可以通过 this.getPackageName(); this代表当前的Activity
param2:Activity、Service的包名+类名
//这里是全路径名:对应的就是 this.getPackageName()+"YourActivity"
//this.getPackageName()+"YourService"
//ComponentName结合Activity的写法如下:
ComponentName componentName = newComponentName( this.getPackageName(), this.getPackageName()+ "YourActivity");
Intent intent = newIntent();
intent.setComponent(componentName);
startActivity(intent);
//ComponentName结合Service的写法如下:
ComponentName componentName = newComponentName( this.getPackageName(), this.getPackageName()+ "YourService");
Intent intent = newIntent();
intent.setComponent(componentName);
startService(intent);
另外,还需要在清单配置文件里面,对应的 Activity 和 Service 需要加上 android:exported = "true",这个 android:exported 标签,是用来指示该服务是否能够被其他应用程序组件调用或跟它交互。设置成 true,则能够被调用或交互;设置 false,也就意味不能被其他组件交互。APP 入口的 Activity 不声明默认就是 android:exported="true"。关于 ComponentName 的说明就到此为止,还不是很好理解的可以自行谷歌百度。综上,还需要在清单文件配置一些代码,参考如下:
android:exported= "true"
android:excludeFromRecents= "true"
android:name= ".TestCreatIconActivity">
android:name= ".MainActivity"
android:exported= "true"
android:excludeFromRecents= "true"
>
这里的 MainActivity,就是我点击快捷方式进入的 Activity。可能你会说,我该如何传值给,快捷方式点进去的Activity?那么也是传统套路根据Intent来传值,也就是上面的 shortcutIntent,参考代码如下:
//传参到指定界面,通过标识和具体数据执行
shortcutIntent.putExtra( "key1", "xxx");
shortcutIntent.putExtra( "key2", true);
shortcutIntent.putExtra( "key3", 233);
...
好了,创建快捷方式的代码是通过 sendBroadcast(resultIntent);来实现的,因此可以判断这是系统的广播。
删除桌面快捷方式
首先,用户可以直接在手机桌面拖拽快捷方式,进行删除。另外,上面的代码也实现了当删除 APK 以后,自动删除快捷方式(因为你跳转没有了目标,所以需要删除)由于代码删除快捷方式,网上的一些代码都有问题(又是令人无语的碎片化和厂商定制),所以这里不提供删除快捷方式的代码,理论上,上面2种手动删除实现即可。值得注意的是,如果用户没有手动打开权限,也会创建失败,也需要提示用户手动对应用进行快捷方式权限授权设置。
Android 7.1 系统快捷方式的变化
从Android 7.1(API 25,也就是 minSdkVersion 25 )开始,新增了ShortcutManager,顾名思义,翻译过来就是快捷方式管理。这个类可以对桌面久按应用图标弹出的快捷方式进行管理。那么ShortcutManager的实现方式有2种:
第一种:XML注册
首先, 需要在 res/xml 目录下创建一个新的 xml 文件,根标签是 shortcuts,参考代码如下:
android:shortcutId= "setting"
android:enabled= "true"
android:icon= "@drawable/yuanbao"
android:shortcutShortLabel= "@string/set_short_name"
android:shortcutLongLabel= "@string/set_long_name"
android:shortcutDisabledMessage= "@string/set_disable_msg">
android:action= "android.intent.action.VIEW"
android:targetPackage= "com.share.shortcut"
android:targetClass= "com.share.shortcut.TargetActivity"/>
嗯,可以看到新增了很多标签,这些标签代表什么意思咧?首先是 shortcut 的外部标签:
shortcutId, 快捷方式的id
enabled:表示这个shortcut是否可用,一般设置为true即可
shortcutShortLabel:配置快捷方式的短名称, 如果长名称显示不下, 就显示短名称
shortcutLongLabel: 配置快捷方式的长名称, launcher会优先选择长名称显示(优先级高于shortcutShortLabel)
shortcutDisabledMessage:这个标签的意思是指:当我们点击一个不可用的shortcut时,给用户一个有效提示
内部还有个intent标签:
android:action,这里的action需要配置,否则会崩溃,默认写法是android.intent.action.VIEW
targetPackage:目标应用的包名
targetClass:点击快捷方式要跳转的目标类, 这里要注意的是android:action一定要配置, 否则会崩溃
categories:这个标签谷歌团队仅提供了android.shortcut.conversation 这一种,所以直接复制即可
好了,现在通过 XML 已经写好了快捷方式,那现在该如何使用,让快捷方式生效?那么,只需要在清单文件中启动 Activity 的里面,加入以下2行代码(也就是通过 meta-data 配置进去)即可看到效果:
android:launchMode= "singleInstance"
android:name= ".MainActivity"
android:excludeFromRecents= "true"
>
静态注册
android:name= "android.app.shortcuts"
android:resource= "@xml/shortcut"/>
这里的 meta-data 就是配置了桌面快捷方式的一些信息。代码运行后,执行创建快捷方式的操作即可看到效果:
第二种注册:代码动态注册
首先,获取系统服务,固定写法:getSystemService(ShortcutManager.class);获取 ShortcutManager 类对象。接着,获取完 ShortcutManager 对象以后,通过 setDynamicShortcuts( List )方法去设置 Shortcut 快捷方式具体的名字、icon 以及逻辑。既然,现在要通过 ShortcutInfo 去完成信息填充,那么我们就先看下 ShortcutInfo 的基本写法:
//获取系统服务得到ShortcutManager对象
ShortcutManager systemService = getSystemService(ShortcutManager.class);
if(Build.VERSION.SDK_INT >= 25) {
//设置Intent跳转逻辑
Intent intent = newIntent(ShortCut7_0Activity. this, TargetActivity.class);
intent.setAction(Intent.ACTION_VIEW);
//设置ID
ShortcutInfo shortcutInfo = newShortcutInfo.Builder( this, "onlyId")
//设置短标题
.setShortLabel( "雅蠛蝶")
//设置长标题
.setLongLabel( "江山留胜迹")
//设置icon
.setIcon(Icon.createWithResource( this, R.drawable.yuanbao))
//设置Intent
.setIntent(intent)
.build();
//这样就可以通过长按图标显示出快捷方式了
systemService.setDynamicShortcuts(Arrays.asList(shortcutInfo));
以上代码就可以创建桌面快捷方式,通过 ShortcutInfo 的 Builder 写法可以动态的插入快捷方式的名称、icon、以及 Intent 逻辑。值得一提的是,ShortcutManager 最大可以创建5个,但桌面上只能最多只能显示4个快捷方式,可能是谷歌团队基于何种情况的考虑进行的限制,这里就不做深入研究。另外,快捷方式的顺序排列是:前面添加的显示在下方,后面添加的显示在上面。
另外,移除快捷方式可以调用如下 API:
voidremoveDynamicShortcuts(@NonNull List shortcutIds);
Android 8.0 系统快捷方式的变化
可能是谷歌出新版本需要优化增加既有版本的一些功能从 Android 8.0(API 26,也就是 minSdkVersion 26,也就是Android O )开始,对 ShortcutManager 这个类新增了更多的管理措施。那么 O 系统主要的扩充说明有那些?我这里就简单说明下。
APP 可以使用 requestPinShortcut(ShortcutInfo,IntentSender)将现有的快捷方式(静态或动态)或全新的快捷方式固定到支持的启动器。这俩参数的意思是:
ShortcutInfo 对象 - 如果快捷方式已存在,则该对象仅包含快捷方式的 ID。如果快捷方式不存在,新的 ShortcutInfo 对象必须包含新快捷方式的 ID,意图和短标签。
PendingIntent 对象 - 此意图表示如果快捷方式成功固定到设备的启动器,您的应用程序将收到回调。
分析:也就是说,谷歌要求新的快捷方式需要编写的严谨,然后该快捷方式是否固定到设备加了新的判断标准
由于Android O 中引入的后台执行限制,最好使用清单声明的接收器来接收回调。
另外,为了防止其他应用程序调用接收器,需要将属性赋值 android:exported ="false" 添加到接收者的清单条目中。
分析:针对 O 系统对于服务的严格限制,这是对快捷方式功能的扩充
并不是所有的启动器都支持固定快捷方式!!!所以,如果要确定该 APP 是否支持国定快捷方式,可以使用 isRequestPinShortcutSupported() 这个方法的返回值。
根据返回值,可以决定隐藏 App 中允许,用户固定快捷方式的选项。该方法返回 TRUE,则意味着,桌面支持 requestPinShortcut;
但是用户也可能会更改,更改选项以后,再次启动 APP 返回值可能就会发生变化。另外,系统版本低于 Android O,那么它支持使用旧的私有意图com.android.launcher.action.INSTALL_SHORTCUT。requestPinShortcut 这个方法请求创建固定的快捷方式。默认启动器将收到该请求,并要求用户批准。如果用户批准,将创建快捷方式,并且将发送 resultIntent。但是,如果请求被用户拒绝,则不会向呼叫者发送任何响应。需要注意的是,只有具有前台活动或前台服务的应用程序才能调用此方法。否则,它将抛出 IllegalStateException。
分析:也就是说,这种启动器的固定快捷方式能否成功很大程度跟用户的操作密切相关(这个可以跟产品吹一波了)。另外可以看到O系统对于服务的严格控制,严格控制后台服务需要使用前台服务的举措在于让应用更加安全(因此 IntentService 在新版本上要慎用。当然谷歌在开发者文档上针对后台服务这一问题也给了对应的解决办法),这也看到了谷歌技术团队对后续版本使用应用安全的决心。
综上:可以有以下代码
@RequiresApi(api = Build.VERSION_CODES.O)
publicstaticvoidtestShortCut(Context context){
ShortcutManager shortcutManager = (ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
booleanrequestPinShortcutSupported = shortcutManager.isRequestPinShortcutSupported();
Log.i(TAG, "启动器是否支持固定快捷方式: "+requestPinShortcutSupported);
if(requestPinShortcutSupported) {
Intent shortcutInfoIntent = newIntent(context, TargetActivity.class);
shortcutInfoIntent.setAction(Intent.ACTION_VIEW);
ShortcutInfo info = newShortcutInfo.Builder(context, "tzw")
.setIcon(Icon.createWithResource(context, R.drawable.yuanbao))
.setShortLabel( "O系统短")
.setLongLabel( "O系统长")
.setIntent(shortcutInfoIntent)
.build();
//当添加快捷方式的确认弹框弹出来时,将被回调CallBackReceiver里面的onReceive方法
PendingIntent shortcutCallbackIntent = PendingIntent.getBroadcast(context, 0, newIntent(context, CallBackReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
shortcutManager.requestPinShortcut(info, shortcutCallbackIntent.getIntentSender());
}
}
classCallBackReceiverextendsBroadcastReceiver{
@Override
publicvoidonReceive(Context context, Intent intent){
Log.i(TAG, "onReceive: 固定快捷方式的回调");
}
}
下面是应用启动后,Android Studio 自带 8.0 模拟器,首先创建快捷方式的逻辑顺序:
点击 ADD...以后,我们的应用就显示这样了,可以看到快捷方式向卫星一样挂靠在应用icon 上面:
如果点击 CANCEL,那么就没有创建,退出以后 icon 还是原样,当我们第二次打开应用,选择申请桌面快捷方式的时候,又会弹出上面的申请界面。
总结
这篇博客花了一些时间,因为要考虑三种不同情况(如果想要看到三种情况的效果,需要手动更改 minSdkVersion 版本号、以及对应版本的模拟器资源),当然也参考了一些作者的文章。源码里面有所有代码,如果写的不好或者有任何问题可以直接在评论区或者github 指出。
文章用到的源码:
https://github.com/zuowutan/ShareShortcut
如果这篇文章对你有帮助,希望各位看官留下宝贵的 star,谢谢。返回搜狐,查看更多
责任编辑: