記一次Build.gradle引發(fā)的ClassNotFound

前段時間發(fā)過這樣一篇文章 Android Studio 打包Jar,因為任務需要將項目中一個模塊打包成jar包提供給第三方公司使用,實話說打包完,并且提供給N個公司使用,那感覺。。。

不過裝逼過度總是要還的,這不 前兩天打臉的來了。。。

劇情

劇情有點繁瑣,不想看的童鞋可以跳的后面的錯誤原因或錯誤重現(xiàn)那。。。

那是一個挺(熱)悠(成)閑(狗)的早上,剛到公司打開電腦,正準備瀏覽幾篇技術(shù)文章,再開始一天的工作呢。突然 本公司與B公司戰(zhàn)略合作群 里出現(xiàn)對方技術(shù)人員的疑問

B公司-Android:我這邊調(diào)用SDK崩潰 @xxx

看見這個問題,我第一瞬間想到肯定是沒按照步驟進行配置,因為之前給別家接SDK時也遇到過調(diào)用失敗的問題

我:是不是配置出錯了,是按照文檔中的要求配置的嗎?權(quán)限有給嗎?

沒一會

B公司-Android: 都按照文檔中配置了,權(quán)限也都申請了,但還是使用不了。

剛準備質(zhì)問一下是否真的配置全了

啪 。。 啪 。。 啪 。。。

對方接連貼了N張配置的截圖,我仔細看看,的確都是按照文檔中配置的。。。

裝逼第一步 。。。 失敗 。。。

我:能把日志給我看看嗎。。

啪 。。 啪 。。 啪 。。

小伙子挺喜歡啪啊 。。。

我盯著那日志看了半天,沒找到任何問題,連一個紅色的報錯都沒有,這TM什么鬼。。

我:全部日志就這些嗎?沒有看見報錯啊。。你確定出錯了?

還沒等我繼續(xù)廢話呢

啪 。。

小伙子 你真的很喜歡啪啊

哎? 不是圖片啊? 再一看 <font size=5>測試包?。?!</font>我也是服氣的。。。

算了,看在群里這么多老板的份上,我忍了。。。掏出測試機。。。安裝測試包。。

運行。。

果然,程序運行到我的SDK模塊時,軟件崩潰了。。。

打開Studio 日志,翻了一個遍,的確和他剛才給的日志一樣,并沒有找到錯誤點,這TM就很奇怪了。。。

再運行一次,依舊是這樣,不過這次我發(fā)現(xiàn)一點奇怪的地方,APP崩潰后并沒有直接退出程序,而是重啟了一遍程序,難道是這里做的怪?

打開Studio日志,盯著日志打印,運行程序,程序崩潰后果然看見一片紅色的打印??!然而當APP自動重啟后,日志記錄中所有的報錯部分全都沒了!看來的確是這個重啟刷新了日志,導致錯誤信息看不見了。

其實這個問題以我以往的經(jīng)驗,應該是Activity的啟動模式設(shè)置成了android:launchMode="singleTask",所有的Activity都在單獨的任務棧中,如果Activity使用默認啟動模式,都在一個任務棧中,當某個Activity崩潰時會導致整個程序的退出,而使用 singleTask 會導致Activity崩潰,程序重啟到前一個Activity,同時會重啟一個新的進程。

<b>那該怎樣查看崩潰的日志信息呢?</b>

很簡單,Android Studio查看日志的時候可以選擇不同的進程

例如我這里選取的進程是com.lcm.test,而當出現(xiàn)上面的那種情況時,一般情況下我們都會在這里看見一個與當前進程同名的一個進程,不過進程后會多一個[DEAD],例如com.lcm.test[DEAD],我們選取這個進程,就可以看見剛才崩潰的那個進程的日志信息了。

既然能找到錯誤了,我們就來看看是什么錯

很明白直接的一個錯誤 <font color=red> Resources$NotFoundException </font> ,資源文件缺失。

這里先回顧一下:

SDK中包含一個Activity,而Activity的Layout文件以及一些資源文件是單獨提供給第三方的,第三方將jar包以及資源文件放到項目的相關(guān)目錄下,SDK中通過反射獲取第三方APP資源文件對應的ID,然后再加載相應的資源文件。

所以看到 <font color=red> Resources$NotFoundException </font>,我立馬就懷疑是不是對方?jīng)]有加入我提供的資源文件。

我:我這邊看見是資源文件未找到的錯誤,你那邊使用SDK時有拷貝提供的資源文件到項目中嗎?

發(fā)完這句話我就后悔了,文檔中說的很清楚,一般人不會忘記這一步吧,果然

B公司-Android: 都拷貝過來了,你看

啪 。。

果然不會犯這么低級的錯誤,繼續(xù)研究日志,在 Warn 級別的日志中發(fā)現(xiàn)這樣一個警告

難道是 R 文件沒有找到?

這里貼上SDK中反射獲取資源文件的代碼

    /**
     *
     * @param context 上下文
     * @param className 資源文件的類型 layout、id、drawable
     * @param name 資源文件的名字
     * @return
     */
    public static int getIdByName(Context context, String className, String name) {
        String packageName = context.getPackageName();
        Class r = null;
        int id = 0;
        try {
            r = Class.forName(packageName + ".R");
            Class[] classes = r.getClasses();
            Class desireClass = null;
            for (int i = 0; i < classes.length; ++i) {
                if (classes[i].getName().split("\\$")[1].equals(className)) {
                    desireClass = classes[i];
                    break;
                }
            }
            if (desireClass != null)
                id = desireClass.getField(name).getInt(desireClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return id;
    }

通過代碼 我們知道,我們是通過 Class.forname(包名+R) 來獲取APP的R文件,然后在R文件中找到我們所需要的資源文件對應的ID,具體可以看我之前的文章 Android Studio 打包Jar

關(guān)于 <font color=red> ClassNotFoundException </font>的錯誤,不管百度還是google常見的有幾種可能的原因。

  1. jar包未引入,相應的類無法找到
  2. Manifest.xml 中注冊Activity時,類名寫錯
  3. App混淆時,未保留相關(guān)類,導致混淆后無法加載相關(guān)類

這里能正確的調(diào)用SDK中的方法,說明jar包是正常引入的,所以排除第一種可能。

讓對方再次檢查了一遍Manifest 文件,確定配置注冊的Activity完整類名填寫沒問題,排除第二種可能性。

剩第三種,詢問后得知,對方的確開啟了APP代碼混淆,立馬想到讓他在代碼混淆配置文件中添加保留R文件代碼

-keep class **.R$* {  
 *;  
}

以防萬一,還讓他添加了保留我的SDK代碼的邏輯,雖然我的jar包已經(jīng)做過代碼混淆了。

讓他再次測試運行

B公司-Android:還是一樣的結(jié)果

啪 。。

順手還貼了個測試包過來。。。

安裝 運行,的確錯誤信息依然存在,真是xxxx 。。。

突然,我想起以前遇到的一個坑 multiDex導致NoClassDefFoundError錯誤 ,大概就是Android 打包時遇到 65535 錯誤,采取 multiDex 進行分包,但是在分包后程序運行過程中會遇到 NoClassDefFoundError 的錯誤,也是類加載失敗。我突然想會不會是這個原因呢?

我:你的項目中是不是開啟了 multiDexEnabled true 配置

B公司-Android:嗯嗯 是的

啊哈!果然有進行分包處理!肯定是這里的錯!

為了避免又被打臉的尷尬,我強裝冷靜道

我:我懷疑是這個分包導致的錯,這樣,你按照我說的進行配置。。

大致配置情況,在我的這篇博客中有寫 multiDex導致NoClassDefFoundError錯誤 ,大致原理就是在進行分包的時候,手動將自己需要的類保留到主要的包中,使其在APP啟動時就加載。為了避免太裝逼,我沒有直接把自己的博客地址給他 ??。

這回應該沒錯了吧,哈哈,喝口水休息下。。??匆幌聲r間,都快到中午了。。。

但是,沒過五分鐘。。

B公司-Android:還是不行啊,還是一樣的錯。。

我擦嘞?。。≌娴募俚模。。?/p>

趕緊讓他又發(fā)了個測試包過來,安裝運行,果然錯誤信息連變都沒變。。。

不甘心的我

我 :你確定是安照我說的配置了嗎?

啪 。。啪 。。 啪 。。 啪 。。

朋友!你體驗過絕望嗎? 我體驗過??!

接下來的一天,基本上就是陪著他檢查各種可能的情況,一遍的調(diào)試,一遍遍的被打臉。。

我都準備讓他把源碼發(fā)過來,自己運行檢查,但是一般公司怎么可能輕易把代碼外流啊。。

錯誤原因

萬萬沒想到我最終還是解決了這個BUG!(咋突然跳到了王大錘的節(jié)奏呢。。哈哈??)

正當我們一籌莫展的時候,我突然發(fā)現(xiàn)一個奇怪的地方,對方的build.gradle中配置的applicationId = aa.bb.cc 和AndroidManifest.xml 中

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="aa.bb.dd.cc">

package 配置的包名不一致。

我:你們這Build.gradle 和 manifest.xml 中配置的包名為什么不一致呢?

B公司-Android:這個項目以前是從Ecipse轉(zhuǎn)過來的,中間有次改過包名,Android Studio 改包名只要修改 build.gradle中的 applicationId 就可以了。

哦?是嗎?

我再回頭看看錯誤原因 java.lang.ClassNotFoundException:aa.bb.cc.R,
這里尋找的是 build.gradle 中配置的包名對應的R文件,我靈機一動

我:你能看看項目 build/intermediates/classes/debug/項目包名/R 目錄下的R文件是否存在嗎?

B公司-Android:存在的

我:那你看看這個目錄中的項目包名是什么?

B公司-android:是 aa.bb.dd.cc

我擦,難道真的是這里的原因,項目編譯時產(chǎn)生的R文件存在的位置是與Manifest 中配置包名也就是項目的工程目錄相對應的目錄中,而代碼中獲取的項目包名是 build.gradle 中配置的applicationId對應的包名,如果再使用這個包名去反射獲取R文件當然是失敗的了??!

我不是很自信的跟他說到

我: 你把這兩個地方的包名改成一致的試試看。死馬當活馬醫(yī)了。。

B公司-android:。。。。。。好吧

然后。。就沒有然后了。。。。問題就這么解決了。。。

錯誤重現(xiàn)

創(chuàng)建工程

正常創(chuàng)建一個工程,在一個Activity中加載一張圖片,這里我們使用反射獲取資源文件

public class MainActivity extends AppCompatActivity {
    private ImageView ivImg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(MResource.getIdByName(getApplicationContext(), "layout", "activity_main"));

        ivImg = (ImageView) findViewById(R.id.image);

        int imgId = MResource.getIdByName(getApplicationContext(), "drawable", "iv_img");

        ivImg.setImageResource(imgId);
    }
}

build.gradle 以及 Manifest中配置包名都為 com.lcm.classNotFound

build 目錄結(jié)構(gòu)如下

正常顯示結(jié)果如下

修改包名

修改 build.gradle 中的 applicationId 為 com.lcm.test

運行

出現(xiàn) ClassNotFoundException 錯誤,且反射R文件包名對應為build.gradle中配置包名。

小結(jié)

雖然是友方出現(xiàn)的問題,但也實實在在的鍛煉了我的解決錯誤的能力,我記錄下整個過程,是為了給自己一個好的示范,真正解決過程中,還是走了一些彎路的,只不過這里沒有記錄。這里記錄下的是我認為正確的過程,遇到BUG不要怕,靜下心來,分享日志,分析代碼,一步步排出可能出現(xiàn)的原因。既然出現(xiàn)問題,肯定有導致問題的原因,發(fā)現(xiàn)根源,然后解決它?。。?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內(nèi)容