一.概述
Android系统升级到5.0之后做了不少的变化(5.0变化),开发人员一定要注意这些变化,要不然就有的折腾了.这次最大的变化应该是把Dalvik虚拟机改成了ART(android Runtime),后续会专门讲解这一块.其他的都是一些零碎的问题,例如前段时间发了一篇Android 5.0之后修改了HashMap的实现(传送门).这篇主要讲一下遇到跟Service相关的问题.
二.详情
Service身为Android四大组件之一,它的使用频率还是比较高的,并且现在主要都是运用在比较关键的部位,例如升级推送等.在Android 5.0之后google出于安全的角度禁止了隐式声明Intent来启动Service.也禁止使用Intent filter.否则就会抛个异常出来.
官方的解释如下.
那么google到底是怎么限制的呢?限制的判定条件是什么呢?这些都可以从Android 4.4和Android 5.0的源码中找到区别.
在Android 4.4的ContextImpl源码中,能看到如果启动service的intent的component和package都为空并且版本大于KITKAT的时候只是报出一个警报,告诉开发者隐式声明intent去启动Service是不安全的.再往下看,丫的异常都写好了只是注释掉了,看来google早就想这么干了.
- private void validateServiceIntent(Intent service) {
- if (service.getComponent() == null && service.getPackage() == null) {
- if (true || getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.KITKAT) {
- Log.w(TAG, "Implicit intents with startService are not safe: " + service
- + " " + Debug.getCallers(2, 3));
- //IllegalArgumentException ex = new IllegalArgumentException(
- // "Service Intent must be explicit: " + service);
- //Log.e(TAG, "This will become an error", ex);
- //throw ex;
- }
- }
- }
- private void validateServiceIntent(Intent service) {
- if (service.getComponent() == null && service.getPackage() == null) {
- if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
- IllegalArgumentException ex = new IllegalArgumentException(
- "Service Intent must be explicit: " + service);
- throw ex;
- } else {
- Log.w(TAG, "Implicit intents with startService are not safe: " + service
- + " " + Debug.getCallers(2, 3));
- }
- }
- }
从源码中的逻辑来看的话,判断一个intent是不是显式声明的点就是component和package,只要这两个有一个生效就不算是隐式声明的,接下来继续分析一下Intent的源码,可以看到下面三种构造方式,设置action来声明Intent是没有构建component的,所以显式声明需要用到第一和第二种构造(还有带packagename或component的拷贝构造),或者后面设置package属性.
- public Intent(Context packageContext, Class<?> cls) {
- mComponent = new ComponentName(packageContext, cls);
- }
- public Intent(String action) {
- setAction(action);
- }
- public Intent(String action, Uri uri,
- Context packageContext, Class<?> cls) {
- setAction(action);
- mData = uri;
- mComponent = new ComponentName(packageContext, cls);
- }
- public Intent setPackage(String packageName) {
- if (packageName != null && mSelector != null) {
- throw new IllegalArgumentException(
- "Can't set package name when selector is already set");
- }
- mPackage = packageName;
- return this;
- }
三.案例分析
- private boolean a(Activity paramActivity)
- {
- Context localContext = paramActivity.getApplicationContext();
- Intent mIntent = new Intent("com.sina.weibo.remotessoservice");
- mIntent.setPackage("com.sina.weibo");
- return localContext.bindService(mIntent, this.serviceConnection, Context.BIND_AUTO_CREATE);
- }
reBuild一下,然后去主项目build文件夹里面的中间资源里面找到module打成的jar包,直接把编译好的.class解压出来.
解压某盟的SDK,进入到正确的路径下直接替换刚才编译出来的.class文件.
回到SDK的跟目录下,运行 jar cvf sdk.jar * 命令进行重打包,将重新打好的SDK替换到项目里面.验证bug.BinGo!搞定!这样就可以打个dex补丁包给线上的版本更新过去修复5.0兼容的bug了.