Android 应用程序升级到 5.0 需要注意的问题

  Android 5.0,代号 Lollipop,源码终于在2014年12月3日放出,国内一大批厂商跟进。最大的改变是默认使用 ART(Android Runtime) ,替换了之前的 Dalvik 虚拟机,提出了 Material Design 界面风格。之前发布的 app 可能需要作一些改动,暂时收集了一些问题,希望对大家有所帮助。

1. Intent/Service
  在低于 Android 5.0 版本,程序运行正常。用户抱怨在新的 Android 5.0 设备上崩溃,我们还没有最新的设备,所以暂时用 Android 模拟器调试。
在输出的 log 中可以看到这样的记录:

E/AndroidRuntime(26479): java.lang.RuntimeException: Unable to start activity ComponentInfo{PACKAGE_NAME/.ACTIVITY_NAME}: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.bda.controller.IControllerService }

E/GameActivity(18333): Caused by: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.bda.controller.IControllerService }
E/GameActivity(18333): at android.app.ContextImpl.validateServiceIntent(ContextImpl.java:1982)
E/GameActivity(18333): at android.app.ContextImpl.startServiceCommon(ContextImpl.java:2020)
E/GameActivity(18333): at android.app.ContextImpl.startService(ContextImpl.java:1995)
E/GameActivity(18333): at android.content.ContextWrapper.startService(ContextWrapper.java:533)
E/GameActivity(18333): at com.bda.controller.a.d(Unknown Source)

  通过查看堆栈崩溃信息,我们看到使用了第三方的 controller.jar 包导致错误。Controller 是在设备屏幕上模拟游戏手柄功能的包,下载最新的 Moga developers SDK ,下载了 controller-sdk-std-1.3.1.zip,2013 Feb 01 发布的,有点旧了。里面有 com.bda.controller.jar,没有源码。

尝试 zip 解压 controller.jar 文件,反编译 .class 文件 com/bda/controller/BaseController.class
想查看 bytecode,使用 javap -c BaseController.class

public final boolean init();
Code:
  0: aload_0
  1: getfield      #113                // Field mIsBound:Z
  4: ifne          48
  7: new           #193                // class android/content/Intent
 10: dup
 11: ldc           #165                // class com/bda/controller/IControllerService
 13: invokevirtual #195                // Method java/lang/Class.getName:()Ljava/lang/String;
 16: invokespecial #201                // Method android/content/Intent."<init>":(Ljava/lang/String;)V
 19: astore_1
 20: aload_0
 21: getfield      #142                // Field mContext:Landroid/content/Context;
 24: aload_1
 25: invokevirtual #204                // Method android/content/Context.startService:(Landroid/content/Intent;)Landroid/content/ComponentName;
 28: pop
 29: aload_0
 30: getfield      #142                // Field mContext:Landroid/content/Context;
 33: aload_1
 34: aload_0
 35: getfield      #132                // Field mServiceConnection:Lcom/bda/controller/Controller$ServiceConnection;
 38: iconst_1
 39: invokevirtual #208                // Method android/content/Context.bindService:(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z
 42: pop
 43: aload_0
 44: iconst_1
 45: putfield      #113                // Field mIsBound:Z
 48: aload_0
 49: getfield      #113                // Field mIsBound:Z
 52: ireturn

init

  你当然想查看源代码,用反编译工具 jad,或者临时用网络在线版 Show My Code,这个网站可以查看 Zend Guard 加密过的 .php 文件、Java 的 .class 文件、Adobe Flash 的 .swf 文件、.NET 程序 .exe, .dll 或者 QR 二维码,可以收藏一下。

public final boolean init()
{
    if(!mIsBound)
    {
        Intent intent = new Intent(com.bda.controller.IControllerService.getName());
        mContext.startService(intent);
        mIsBound = mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
    }
    return mIsBound;
}

  根据上面的错误和代码看出,这里需要使用显式的 Intent(通过 setComponent(ComponentName) 或者 setClass(Context, Class) 设置了 Component 的 Intent),上面的一句需要改成 Intent intent = new Intent(mContext, IControllerService.class);
或者 Intent intent = new Intent("com.bda.controller.IControllerService").setPackage("com.bda.controller");

官方文档 也提到使用显式的 Intent 来 startService/bindService 以确保安全。

Caution: To ensure your app is secure, always use an explicit intent when starting a Service and do not declare intent filters for your services. Using an implicit intent to start a service is a security hazard because you cannot be certain what service will respond to the intent, and the user cannot see which service starts. Beginning with Android 5.0 (API level 21), the system throws an exception if you call bindService() with an implicit intent.

Note: When starting a Service, you should always specify the component name. Otherwise, you cannot be certain what service will respond to the intent, and the user cannot see which service starts.

很多第三方的库都暴露出这种问题,需要更新一下。我们也用了 Google 的 Analytics tracking 库 libGoogleAnalyticsV2.jar。
E/GameActivity( 1137): java.lang.RuntimeException: Unable to start activity ComponentInfo{PACKAGE_NAME/ACTIVITY_NAME}: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.google.android.gms.analytics.service.START (has extras) }
尝试更新了到 v3 (现在有 v4 了)解决问题,需要改动一些代码。这里有文档迁移需要修改什么,EasyTracker: v2.x to v3

2. MD5 符号找不到了。

MD5_CTX context;
MD5_Init(&context);
const char* text = "Hello, world!";
MD5_Update(&context, text, sizeof(text));
MD5_Final(md5_result, &context);

崩溃的 log 如下

E/art(21678): dlopen("/data/app/PACKAGE_NAME/lib/arm/libsixguns.so", RTLD_LAZY) failed: dlopen failed: cannot locate symbol "MD5_Final" referenced by "libXYZ.so"...
E/GAME(21678): native code library failed to load.
E/GAME(21678): java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "MD5_Final" referenced by "libXYZ.so"...

这是因为 Google 修改了底层 bionic libc 库的实现 ,一些隐藏的 API 移除了。一些依赖这些函数的代码可能无法运行。
修改方案,可以自行导入 MD5 库,反正代码也简短。
或者添加 --whole-archive 静态链接 crypto 库。因为 OpenSSL 也提供了 MD5 的实现,可以借用。openssl/crypto/md32_common.h
为了在最终的 .so 库中包含这些定义,添加 ld 链接命令 -Wl,-whole-archive crypto -Wl,-no-whole-archive。
--whole-archive 将在链接中包含归档文件 .a 所有的 .o 文件,而不是只在需要时才搜索,用来转 .a 文件成 .so 文件。gcc 不识别这个命令,所以需要使用 -Wl,-whole-archive,用好后需要添加  -Wl,-no-whole-archive 结束,因为 gcc 会在链接中会添加自己的 .a,以免受影响。

3. ART 模式下随机崩溃。
Dalvik 对于 native 代码和 Java 代码提供各自的栈,默认 native 栈有 1MB、Java 栈有 32KB。而在 ART 模式下,提供统一的栈。按说,ART 线程栈的大小应该与 Dalvik 的一样。如果你显式设置栈的大小,你可能需要在 ART 模式下运行的 app 里重新访问这些值。

Java 的 Thread 类有一个构造函数 Thread(ThreadGroup group, Runnable runnable, String threadName, long stackSize) 提供栈大小参数的设置,如果运行中出现 StackOverflowError 错误,可能需要手动增大 stackSize 值了。

C/C++ 需要调用 POSIX thread 的函数 pthread_attr_setstack() 和 pthread_attr_setstacksize()。如果 pthread 栈太小, 调用 JNI AttachCurrentThread() 方法会打印如下 log:

F/art: art/runtime/thread.cc:435] Attempt to attach a thread with a too-small stack (16384 bytes)

使用 JNI 的时候,出于效率因素,可能需要缓存一些方法 FindClass/GetFieldID/GetMethodID 返回的 ID,千万不要缓存 JNIEnv*,也不要在应用程序的整个生命周期将 native 线程附加到 Java 线程。用 adb shell setprop debug.checkjni 1 命令可以调试一些 JNI 错误,它不会影响已经运行的应用程序。

   ART 模式下的 JNI 可能会抛出一些 Dalvik 不会抛的错误,可以用 CheckJNI,也就是上面的命令行捕捉错误。比如,在 proguard 混淆代码的时候脱掉了一些 native 方法,运行时候会抛 NoSuchMethodError 异常。现在 GetFieldID() 和 GetStaticFieldID() 方法抛出 NoSuchMethodError 异常,而不是返回 null。

E/AndroidRuntime: FATAL EXCEPTION: main
E/AndroidRuntime: java.lang.NoSuchMethodError: no static or non-static method "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I"
E/AndroidRuntime:     at java.lang.Runtime.nativeLoad(Native Method)
E/AndroidRuntime:     at java.lang.Runtime.doLoad(Runtime.java:421)
E/AndroidRuntime:     at java.lang.Runtime.loadLibrary(Runtime.java:362)
E/AndroidRuntime:     at java.lang.System.loadLibrary(System.java:526)
时间: 2024-12-05 09:58:48

Android 应用程序升级到 5.0 需要注意的问题的相关文章

Android Studio中Android Support Library升级到 23.0.0 报错!

Android Support Library升级到 23.0.0  报错解决方案 不得不说,真能折腾!将Android Studio中SDK下的 Android Support Library 升级到了23.0.0,结果倒好了,创建项目出问题了,还比较奇葩: 卧槽你大爷!尼玛还能不能好好玩耍了.各种卸载重装,就差重装系统了. 后来在神器stackoverflow中找到了这么一篇: http://stackoverflow.com/questions/32092511/resource-erro

Android studio 程序升级和sdk manager 升级方法

在中国使用android有点郁闷,经常被屏蔽.使用一下方法 android studio升级时提示 Connection failed. Please check your network connection and try again 2013年02月16日 ⁄ 综合 ⁄ 共 322字 ⁄ 字号 小 中 大 ⁄ 评论关闭 原文地址 http://www.eyeapk.com/android-studio-update.html Mac OSX中修改文件路径为 bin/idea.vmoptio

CMS .NET 程序框架 从2.0/3.5升级到4.0 版本后 需要调整的地方

问题一: document.forms1.action 不可使用 需要修改程 document.forms[0] .NET 程序框架 从2.0/3.5升级到4.0 版本后,document.forms1  不可使用 也就是说所有浏览器都是支持document.forms["form1"]这样的写法的. 微软4.0之前是document.form1也能获取, 升级到4.0之后 取值方式为document.forms["form1"] 或者document.forms[

ArcGIS for Android入门程序之DrawTool2.0

来自:http://blog.csdn.net/arcgis_mobile/article/details/8084763 GISpace博客<ArcGIS for Android入门程序之DrawTool>http://blog.csdn.net/gispace/article/details/6723459 在ArcGIS Android SDK 0.9版本实现绘制各种几个图形.ArcGIS Android SDK目前版本为2.0,较之前版本变化较大,故将之前版本移植到2.0版本下.源代码

android studio 1.5升级到2.0或2.1报错

早一周前,事情终于不多了,没事升级下新版本,有1.5升级到2.0. 升级完成安装后,本来能够在android studio 1.5中好好运行的代码,在升级后,通过clean->make->run后, 应用居然崩溃了,崩溃了...哭死我了...好好的代码居然升级版本后崩溃了... 然后查看崩溃日志,发现是这样: 大概的错误是欢迎界面的xml布局第7行报错... 再看具体的,居然是在不到IamgeView的id,吓死宝宝了,clean.make都没有报错,居然还会找不到id?特么打死我,你看我信不

android应用程序签名(转)

概述 Android系统要求,所有的程序经过数字签名后才能安装.Android系统使用这个证书来识别应用程序的作者,并且建立程序间的信任关系.证书不是用于用户控制哪些程序可以安装.证书不需要授权中心来签名:Android应用程序上使用自己签名的证书是完全允许且普遍的. 理解Android应用程序签名有以下几个重要点: 所有的应用程序都必须签名.系统不会安装任何一个不签名的程序. 你可以使用自己的证书来签名.不需要任何授权中心. 当你要为最终用户发布你的应用程序的时候,你必须签入一个合适的密钥.你

开始第一个Android应用程序

Android应用程序建立在应用程序框架之上,所以Android编程就是面向应用程序框架API编程---与编写普通的Java SE没有太大区别,只是增加了一些API. 1.使用eclipse开发第一个Android应用 开发Android应用三步:a.创建一个Android项目.b.在XML布局文件中定义应用程序的用户界面.c.在java代码中编写业务实现. 开发一个简单的helloworld应用 1.eclipse->file->new->android application pro

【转】android应用程序签名

概述 Android系统要求,所有的程序经过数字签名后才能安装.Android系统使用这个证书来识别应用程序的作者,并且建立程序间的信任关系.证书不是用于用户控制哪些程序可以安装.证书不需要授权中心来签名:Android应用程序上使用自己签名的证书是完全允许且普遍的. 理解Android应用程序签名有以下几个重要点: 所有的应用程序都必须签名.系统不会安装任何一个不签名的程序. 你可以使用自己的证书来签名.不需要任何授权中心. 当你要为最终用户发布你的应用程序的时候,你必须签入一个合适的密钥.你

Android应用程序请求SurfaceFlinger服务创建Surface的过程分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/7884628 前面我们已经学习过Android应用程序与SurfaceFlinger服务的连接过程了.连接上SurfaceFlinger服务之后,Android应用程序就可以请求SurfaceFlinger服务创建Surface.而当有了Surface后,Android应用程序就可以用来渲染自己的UI了.在本文中,我们将详细分析Android应用