1. 程式人生 > >Android 5.0使用android:onClick屬性出現崩潰的原因及解決方案

Android 5.0使用android:onClick屬性出現崩潰的原因及解決方案

問題及表現

在專案中,對Button設定點選事件監聽時,大多數情況下還是習慣使用setOnClickListener設定監聽,但是最近發現當在佈局檔案中同時使用了android:theme和android:onClick屬性時,在響應點選事件時程式會發生crash,發生Crash的裝置為Android 5.0及以上(7.0未測試),不限機型。在Android 5.0和Android 6.0上發生crash時Log資訊不一致。

Android 6.0 Crash資訊如下:
這裡寫圖片描述
Android 5.0 Crash資訊如下:
這裡寫圖片描述

原因

如果去掉android:theme屬性,則點選事情可以正常響應,並未出現任何崩潰的情況。

查詢資料發現,從Android 5.0開始,支援對單獨的View設定主題。當在佈局檔案中設定了主題之後,ContextThemeWrapper 會被指定為View的Context,因此View的Context不再是Activity了,這時候點選事件的回撥響應也就不存在了。

實驗證明

在使用getContext()獲取view的Context時,如果在佈局檔案中未設定主題,返回值是當前的Activity例項[email protected]
這裡寫圖片描述
在使用getContext()獲取view的Context時,如果在佈局檔案中設定了主題,返回值是ContextThemeWrapper,它的成員變數mBase才是當前的Activity,因此在ContextThemeWrapper無法找到”android:onClick”中設定的方法。
這裡寫圖片描述

原始碼分析

Android M中程式碼如下:

 private void parseInclude(XmlPullParser parser, Context context, View parent,
            AttributeSet attrs) throws XmlPullParserException, IOException {
        int type;

        if (parent instanceof ViewGroup) {
            // Apply a theme wrapper, if requested. This is sort of a weird
// edge case, since developers think the <include> overwrites // values in the AttributeSet of the included View. So, if the // included View has a theme attribute, we'll need to ignore it. final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); final boolean hasThemeOverride = themeResId != 0; if (hasThemeOverride) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); // If the layout is pointing to a theme attribute, we have to // massage the value to get a resource identifier out of it. int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0); if (layout == 0) { final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); if (value == null || value.length() <= 0) { throw new InflateException("You must specify a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); } // Attempt to resolve the "?attr/name" string to an identifier. layout = context.getResources().getIdentifier(value.substring(1), null, null); } ... } ... View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); } // Apply a theme wrapper, if allowed and one is specified. if (!ignoreThemeAttr) { final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); if (themeResId != 0) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); } if (name.equals(TAG_1995)) { // Let's party like it's 1995! return new BlinkLayout(context, attrs); } }

Android K中程式碼如下:

private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs)
            throws XmlPullParserException, IOException {

        int type;

        if (parent instanceof ViewGroup) {
            final int layout = attrs.getAttributeResourceValue(null, "layout", 0);
            if (layout == 0) {
                final String value = attrs.getAttributeValue(null, "layout");
                if (value == null) {
                    throw new InflateException("You must specifiy a layout in the"
                            + " include tag: <include layout=\"@layout/layoutID\" />");
                } else {
                    throw new InflateException("You must specifiy a valid layout "
                            + "reference. The layout ID " + value + " is not valid.");
                }
                ...
}
...
View createViewFromTag(View parent, String name, AttributeSet attrs) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }
        ...
}

對比原始碼可以發現,在M的原始碼中,多出瞭如下幾號程式碼

final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
            final int themeResId = ta.getResourceId(0, 0);
            final boolean hasThemeOverride = themeResId != 0;
            if (hasThemeOverride) {
                context = new ContextThemeWrapper(context, themeResId);
            }
            ta.recycle();

如果對佈局或者View指定了主題,那麼當前的context(activity或fragment或application context)被轉換為 ContextThemeWrapper的例項,因此會出現ContextThemeWrapper找不到對應方法的問題。

問題解決方案

如果是對整個Activity設定主題,儘量不要在佈局檔案中設定,在Manifest配置檔案中設定;
如果要求必須在佈局檔案中設定主題,那麼不要使用屬性android:onClick,使用setOnClickListener替代。

相關推薦

no session 問題出現原因解決方案

lte base 請求 屬性 TP IE session關閉 範圍 關聯對象 session是hibernate框架與數據庫交互的對象,與HttpSession是完全不同的東西。session通常與線程綁定,使用完之後就會關閉。 no session問題的出現與延遲加載有關

機器學習問題中過擬合出現原因解決方案

如果一味的追求模型的預測能力,所選的模型複雜度就會過高,這種現象稱為過擬合。模型表現出來的就是訓練模型時誤差很小,但在測試的時候誤差很大。 一、產生的原因: 1.樣本資料問題 樣本資料太少 樣本抽樣不符合業務場景 樣本中的噪音資料影響 2.模型問題 模型複雜度高,引

Server Application Unavailable出現原因解決方案集錦

在Asp.net站點中經常出現這種提示 Server Application UnavailableThe web application you are attempting to access on this web server is currently unav

Android 5.0使用android:onClick屬性出現崩潰原因解決方案

問題及表現 在專案中,對Button設定點選事件監聽時,大多數情況下還是習慣使用setOnClickListener設定監聽,但是最近發現當在佈局檔案中同時使用了android:theme和android:onClick屬性時,在響應點選事件時程式會發生cra

VS調試_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));崩潰原因解決方法

說道 動態 意義 討論 清理 問題 [] all 測試 今天下午對面的老大調試遇到這個問題,大家一起討論好久才解決這個問題 crt源代碼都是可以看到的,為了了解清楚原因,十分有必要查看源碼,源碼一般在你的VS安裝路徑下VC\crt\src下。 點擊重試,定位到崩潰源碼地方

派克斯寬頻撥號出現651原因解決辦法

華科雲商為客戶提供安全、穩定、高效的{換IP軟體}。現有{派克斯}、{ip代理}、{撥號vps}等產品,全國100+城市ip可選,可滿足各類客戶換IP需求,歡迎客戶諮詢測試。 適用於各種網賺、驗證、註冊、投票、掛機、推廣營銷,並且IP重複率最低。我們所有的賬號都是電信直撥賬號,也是真正基於PPP

wordpress更改“固定連結”模式後,頁面出現404原因解決方法

Nginx 解決方案: 在 /etc/nginx/config.d/mysit.conf檔案的 loction \ {} 中新增 if (-f $request_filename/index.html){ rewrite (.*) $1/index.html break; }

Android中TextView居中顯示無效的原因解決方案

今天在寫程式碼的時候,出現一個Bug,最後解決了,現在記錄下。 自定義了一個佈局控制元件,用於PopupWindow提示郵箱型別,結果顯示的郵箱型別無法居中,剛開始的效果如圖所示: 上面所貼圖片沒有經過處理,其中“@126.com”的文字在白色背景中沒有居中。注:白色

_062_Android_OOM出現常見原因解決辦法

轉自https://blog.csdn.net/hudfang/article/details/51781997,感謝作者的無私分享。 Android的虛擬機器是基於暫存器的Dalvik,它的最大堆大小一般是16M,有的機器為24M。我們平常看到的OutOfMemory的錯誤,通常 是堆記憶體溢

[偶爾遇到]找不到mysql.sock的出現原因解決方案和mysql 預設mysql.sock位置預設問題探討 不指定

背景:(1)偶爾會出現mysql的server和mysql的client預設的socke檔案不在一個地方,我們用mysql時會出現一個找不到mysql.sock的情況。(2)因非正常關機出現:/tmp/mysql.sock 不見了,找不到了,如何連線上去的問題?(其他機器通過

單節點Elasticsearch出現unassigned_shards原因解決辦法

檢視單節點Elasticsearch健康狀態使用head外掛檢視叢集狀態從上面截圖可以看出存在5個unassigned的分片,新建索引blog5的時候,分片數為5,副本數為1,新建之後叢集狀態成為yellow,其根本原因是因為叢集存在沒有啟用的副本分片,我們先來看一下官網給出

mysql出現Waiting for table metadata lock的原因解決方案

http://www.cnblogs.com/dyllove98/archive/2013/07/16/3194332.html 最近經常遇到mysql資料庫死鎖,鬱悶死, show processlist; 時 Waiting for table metadata lo

專案出現記憶體溢位的原因解決方案

記憶體溢位是指應用系統中存在無法回收的記憶體或使用的記憶體過多,最終使得程式執行要用到的記憶體大於虛擬機器能提供的最大記憶體。引起記憶體溢位的原因有很多種,常見的有以下幾種:  1.記憶體中載入的資料量過於龐大,如一次從資料庫取出過多資料;  2.集合類中有對物件的引用,使用

unrecognized selector sent to instance出現原因解決方案

造成unrecognized selector sent to instance iphone,大部分情況下是因為物件被提前release了,在你心裡不希望他release的情況下,指標還在,物件已經不在了。很多時候,是因為init初始化函式中,對屬性賦值沒有使用self

StackOverflowError出現原因解決辦法

06-12 10:28:31.750: E/AndroidRuntime(13995): FATAL EXCEPTION: main 06-12 10:28:31.750: E/AndroidRuntime(13995): java.lang.StackOverflowE

CFile在寫入Unicode編碼檔案出現亂碼---原因解決辦法

      這幾天統計一個詞典,為藏文詞典,以Unicode編碼儲存。要對其進行過濾處理,並且要儲存處理後的結果。       在一開始出現了一下問題:      1,把原始檔讀到程式中,重新寫回去,沒

PHP中文亂碼出現原因解決辦法分析

一.首先是PHP網頁的編碼 1.如果欲使用gb2312編碼,那麼php要輸出頭:header(“Content-Type: text/html; charset=gb2312”),靜態頁面新增,所有檔案的編碼格式為ANSI,可用記事本開啟,另存為選擇編碼為AN

Android不能呼叫java.awt的原因解決辦法和思考

android 裡面不能使用awt,底層沒有具體的實現awt android裡面的視窗建立過程決定了介面只能是android裡面的組建。 android的元件都是通過遠端的IPC呼叫完成的,也就是說服務端有什麼功能才能用什麼功能。 不是所有用java寫的程式都能在標準jv

Ajax傳送PUT/DELETE請求時出現錯誤的原因解決方案

本文講什麼? 大家應該都知道.在HTTP中,規定了很多種請求方式,包括POST,PUT,GET,DELETE等.每一種方式都有這種方式的獨特的用處,根據英文名稱,我們能夠很清楚的知道DELETE方法的作用—-刪除請求.而其他的,根據單詞並不能準確的知道他們想表

內存溢出原因解決方案

使用 -xmx 遞歸調用 其它 地址 str 啟動 遞歸 對象 參考地址:http://baike.baidu.com/view/79183.htm 內存溢出是指應用系統中存在無法回收的內存或使用的內存過多,最終使得程序運行要用到的內存大於虛擬機能提供的最大內存