1

我正在开发一个最近变得很大的应用程序(每个 dex 达到 65535 个方法计数)。当我集成三星 Spen SDK 时,它达到了极限。

我决定使用proguard,它编译成功。但是,当我打开 spen draw 活动时,它崩溃并显示以下日志:

E/AndroidRuntime( 7144): java.lang.UnsatisfiedLinkError: Native method not found: com.bst.HwBeautify.BeautifyNative.nativeCBInitEngine:()I
E/AndroidRuntime( 7144):    at com.bst.HwBeautify.BeautifyNative.nativeCBInitEngine(Native Method)
E/AndroidRuntime( 7144):    at com.bst.HwBeautify.BeautifyNative.cbInitEngine(SourceFile:107)
E/AndroidRuntime( 7144):    at com.bst.HwBeautify.BeautifyManager.b(SourceFile:87)
E/AndroidRuntime( 7144):    at com.bst.HwBeautify.BeautifyManager.a(SourceFile:85)
E/AndroidRuntime( 7144):    at com.bst.HwBeautify.BeautifyManager$1.run(SourceFile:64)
E/AndroidRuntime( 7144):    at java.lang.Thread.run(Thread.java:856)

这个缺失的方法可以在 proguard/seeds.txt 中找到

...
com.bst.HwBeautify.BeautifyNative
com.bst.HwBeautify.BeautifyNative: int nativeCBInitEngine()
com.bst.HwBeautify.BeautifyNative: void nativeCBCloseEngine()
...

并在 proguard/dumps.txt

  + Method:       nativeCBInitEngine()I
    Access flags: 0x108
      = static native int nativeCBInitEngine()

并且 BeautifyNative.class 也在 obfuscated.jar (obfuscated/com/bst/HwBeautify/) 中,所以我不知道为什么它说 UnsatisfiedLinkError。

然后我决定尝试自定义类加载。这也不起作用,但这是一个不同的故事。此外,我将不得不重新编码许多部分以使我的应用程序与辅助 jar 兼容。

调查该问题后,我尝试创建一个与 Spen SDK 一起使用的最小应用程序。慢慢地,我把我的大应用程序的一部分放到了小但工作的应用程序中。

通过添加更多图像,该错误被毫无疑问地复制了。更清楚地说,当我只有 res/drawable-hdpi 上的图像时,应用程序不会崩溃(此处的崩溃与上述相同的日志有关)。但是当我添加 res/drawable-xhdpi 时,它崩溃了。res/drawable-ldpi 和 res/drawable-mdpi 的情况相同。

还有一件事:如果我在安装小应用程序(相同的包名称,相同的应用程序名称)时使用安装大应用程序,那么大应用程序可以正常工作。但是如果我在安装大应用程序之前卸载了小应用程序,后者就会崩溃。adb install -r appname.apk

对于我的问题:

  • 当方法存在时,为什么会说 UnsatisfiedLinkError?还是我在看错误的文件?
  • 我的 proguard.cfg 有什么问题导致了这种原生方法吗?
  • 这可能是一个蚂蚁构建错误吗?

这是我的 proguard.cfg

#################################################################################################
# Standard Configuration for Android App
# See http://proguard.sourceforge.net/index.html#manual/examples.html

# -optimizationpasses 2
-dontoptimize
-dontobfuscate
-dontpreverify
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
# -allowaccessmodification
# -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*,!code/allocation/variable
-keepattributes *Annotation*

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider

-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

-keepclassmembers class * extends android.content.Context {
   public void *(android.view.View);
   public void *(android.view.MenuItem);
}

-keepclassmembers class * implements android.os.Parcelable {
    static android.os.Parcelable$Creator CREATOR;
}

-keepclassmembers class * implements java.io.Serializable
{
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

-keepclassmembers class **.R$* {
    public static <fields>;
}

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# adding this in to preserve line numbers so that the stack traces
# can be remapped
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable


#################################################################################################
# For RoboSpice
# See https://groups.google.com/forum/?fromgroups=#!topic/robospice/xGLRbGkLwQU
#Request classes purged by Proguard as they are "empty", others are kept
-keep class com.limbocitizen.android.playground.model.**

#RoboSpice requests and Results must be kept as they are used by reflection via Jackson
-keepclassmembers class com.limbocitizen.android.playground.request.** {
  public void set*(***);
  public *** get*();
  public *** is*();
}


### XML SERIALIZER SETTINGS

-keepclassmembers,allowobfuscation class * {
    @org.simpleframework.xml.* <fields>;
    @org.simpleframework.xml.* <init>(...);
}


### Json SERIALIZER SETTINGS
-keepclassmembers,allowobfuscation class * {
    @org.codehaus.jackson.annotate.* <fields>;
    @org.codehaus.jackson.annotate.* <init>(...);
}

-keepclasseswithmembers class * {
    native <methods>;
}

#Warnings to be removed. Otherwise maven plugin stops, but not dangerous
-dontwarn android.support.**
-dontwarn com.sun.xml.internal.**
-dontwarn com.sun.istack.internal.**
-dontwarn org.codehaus.jackson.**
-dontwarn org.springframework.**
-dontwarn java.awt.**
-dontwarn javax.security.**
-dontwarn java.beans.**
-dontwarn javax.xml.**
-dontwarn java.util.**
-dontwarn org.w3c.dom.**
-dontwarn com.octo.android.robospice.persistence.**

-dontwarn org.bouncycastle.**
-dontwarn com.nostra13.**
-dontwarn com.opentok.**
-dontwarn com.pubnub.api.**
-dontwarn com.google.gson.**
-dontwarn com.google.android.**
-dontwarn chesspresso.**
-dontwarn com.parse.**
-dontwarn com.testflightapp.**
-dontwarn org.msgpack.**
-dontwarn com.bugsense.**
-dontwarn biz.source_code.base64Coder.**
-dontwarn org.codehaus.jackson.**
-dontwarn com.bst.**
-dontwarn com.google.common.**
-dontwarn com.samsung.**
-dontwarn org.apache.commons.pool.**
-dontwarn org.ccil.cowan.tagsoup.**
-dontwarn com.opentok.**
-dontwarn com.tokbox.**
-dontwarn main.java.tokbox.org.**
-dontwarn tokbox.org.**
-dontwarn com.google.android.youtube.player.**
-dontwarn org.slf4j.**
-dontwarn org.codehaus.jackson.**
-dontwarn com.facebook.**

-keep class android.support.v4.content.Loader
-keep class com.google.android.gms.maps.GoogleMapOptions
-keep class android.support.v4.content.Loader$OnLoadCompleteListener
-keep class com.icannhas.qualitytime.utils.parse.objects.ParseChatMessage
-keep class com.tokbox.rumor.client.message.Message
-keep class org.codehaus.jackson.node.ObjectNode

#################################################################################################
# For Actionbarsherlock
# See http://actionbarsherlock.com/faq.html

-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep class com.actionbarsherlock.** { *; }
-keep interface com.actionbarsherlock.** { *; }

-keepattributes *Annotation*


#################################################################################################

我的 build.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
    Copyright 2011 Google Inc.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->
<project name="appname" default="help">

    <!-- The local.properties file is created and updated by the 'android' tool.
         It contains the path to the SDK. It should *NOT* be checked into
         Version Control Systems. -->
    <property file="local.properties" />

    <!-- The ant.properties file can be created by you. It is only edited by the
         'android' tool to add properties to it.
         This is the place to change some Ant specific build properties.
         Here are some properties you may want to change/update:

         source.dir
             The name of the source directory. Default is 'src'.
         out.dir
             The name of the output directory. Default is 'bin'.

         For other overridable properties, look at the beginning of the rules
         files in the SDK, at tools/ant/build.xml

         Properties related to the SDK location or the project target should
         be updated using the 'android' tool with the 'update' action.

         This file is an integral part of the build system for your
         application and should be checked into Version Control Systems.

         -->
    <property file="ant.properties" />

    <!-- The project.properties file is created and updated by the 'android'
         tool, as well as ADT.

         This contains project specific properties such as project target, and library
         dependencies. Lower level build properties are stored in ant.properties
         (or in .classpath for Eclipse projects).

         This file is an integral part of the build system for your
         application and should be checked into Version Control Systems. -->
    <loadproperties srcFile="project.properties" />

    <!-- quick check on sdk.dir -->
    <fail
            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
            unless="sdk.dir"
    />

    <!--
        Import per project custom build rules if present at the root of the project.
        This is the place to put custom intermediary targets such as:
            -pre-build
            -pre-compile
            -post-compile (This is typically used for code obfuscation.
                           Compiled code location: ${out.classes.absolute.dir}
                           If this is not done in place, override ${out.dex.input.absolute.dir})
            -post-package
            -post-build
            -pre-clean
    -->
    <import file="custom_rules.xml" optional="true" />

    <!-- Import the actual build file.

         To customize existing targets, there are two options:
         - Customize only one target:
             - copy/paste the target into this file, *before* the
               <import> task.
             - customize it to your needs.
         - Customize the whole content of build.xml
             - copy/paste the content of the rules files (minus the top node)
               into this file, replacing the <import> task.
             - customize to your needs.

         ***********************
         ****** IMPORTANT ******
         ***********************
         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
         in order to avoid having your file be overridden by tools such as "android update project"
    -->
    <!-- version-tag: 1 -->
    <import file="${sdk.dir}/tools/ant/build.xml" />
</project>

和 custom_rules.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules">
    <!--
    Suppress "Warning: can't write resource [META-INF/MANIFEST.MF] (Duplicate zip entry [classes.jar:META-INF/MANIFEST.MF])".
    Per http://www.dancartoon.com/2012/01/14/fixing-proguard-warning-cant-write-resource-meta-infmanifest-mf/
    Target "-obfuscate" copied from ${sdk.dir}/tools/ant/build.xml v21.
    -->
    <target name="-obfuscate">
        <if condition="${proguard.enabled}">
            <then>
                <property name="obfuscate.absolute.dir" location="${out.absolute.dir}/proguard" />
                <property name="preobfuscate.jar.file" value="${obfuscate.absolute.dir}/original.jar" />
                <property name="obfuscated.jar.file" value="${obfuscate.absolute.dir}/obfuscated.jar" />
                <!-- input for dex will be proguard's output -->
                <property name="out.dex.input.absolute.dir" value="${obfuscated.jar.file}" />

                <!-- Add Proguard Tasks -->
                <property name="proguard.jar" location="${android.tools.dir}/proguard/lib/proguard.jar" />
                <taskdef name="proguard" classname="proguard.ant.ProGuardTask" classpath="${proguard.jar}" />

                <!-- Set the android classpath Path object into a single property. It'll be
                     all the jar files separated by a platform path-separator.
                     Each path must be quoted if it contains spaces.
                -->
                <pathconvert property="project.target.classpath.value" refid="project.target.class.path">
                    <firstmatchmapper>
                        <regexpmapper from='^([^ ]*)( .*)$$' to='"\1\2"'/>
                        <identitymapper/>
                    </firstmatchmapper>
                </pathconvert>

                <!-- Build a path object with all the jar files that must be obfuscated.
                     This include the project compiled source code and any 3rd party jar
                     files. -->
                <path id="project.all.classes.path">
                    <pathelement location="${preobfuscate.jar.file}" />
                    <path refid="project.all.jars.path" />
                </path>
                <!-- Set the project jar files Path object into a single property. It'll be
                     all the jar files separated by a platform path-separator.
                     Each path must be quoted if it contains spaces.
                -->
            <!--
            Old:
            <pathconvert property="project.all.classes.value" refid="project.all.classes.path">
            New:
            -->
                <pathconvert property="project.all.classes.value" refid="project.all.jars.path" pathsep=" ">
                    <firstmatchmapper>
                    <!--
                    Old:
                        <regexpmapper from='^([^ ]*)( .*)$$' to='"\1\2"'/>
                        <identitymapper/>
                    New:
                    -->
                    <regexpmapper from='^([^ ]*)( .*)$$' to='-injars "\1\2"(!META-INF/MANIFEST.MF)'/>
                    <regexpmapper from='(.*)' to='-injars \1(!META-INF/MANIFEST.MF)'/>                        
                    </firstmatchmapper>
                </pathconvert>

                <!-- Turn the path property ${proguard.config} from an A:B:C property
                     into a series of includes: -include A -include B -include C
                     suitable for processing by the ProGuard task. Note - this does
                     not include the leading '-include "' or the closing '"'; those
                     are added under the <proguard> call below.
                -->
                <path id="proguard.configpath">
                    <pathelement path="${proguard.config}"/>
                </path>
                <pathconvert pathsep='" -include "' property="proguard.configcmd" refid="proguard.configpath"/>

            <echo>proguard: $${project.all.classes.value}=${project.all.classes.value}</echo>                

                <mkdir   dir="${obfuscate.absolute.dir}" />
                <delete file="${preobfuscate.jar.file}"/>
                <delete file="${obfuscated.jar.file}"/>
                <jar basedir="${out.classes.absolute.dir}"
                    destfile="${preobfuscate.jar.file}" />
                <proguard>
                    -include      "${proguard.configcmd}"
                    -include      "${out.absolute.dir}/proguard.txt"
                -injars       ${preobfuscate.jar.file}
                    ${project.all.classes.value}
                    -outjars      "${obfuscated.jar.file}"
                    -libraryjars  ${project.target.classpath.value}
                    -dump         "${obfuscate.absolute.dir}/dump.txt"
                    -printseeds   "${obfuscate.absolute.dir}/seeds.txt"
                    -printusage   "${obfuscate.absolute.dir}/usage.txt"
                    -printmapping "${obfuscate.absolute.dir}/mapping.txt"
                </proguard>
            </then>
        </if>
    </target>
</project>
4

0 回答 0