android opensource: Settings 研究_android 組件如何響應語言變化 – Android移動開發技術文章_手機開發 Android移動開發教學課程

這裡所說的 android 組件,主要是指 android 中 Activity、Service、ContentProvider 以及 BroadcastReceiver.

在 android 源碼開發的過程中,大傢拿到手的都是一樣的 android 源碼,但是硬件平臺卻是大相徑庭,所以會引發各種各樣的問題,於是乎,android 開發越發精彩!

這篇博客主要是在研究 Settings 源碼時所激發的,把自己的經驗拿出來分享一番!

我在設置語言之後,發現有些地方的語言還是沒有改變,這個時候想起瞭 onConfigurationChanged 方法,先來看看這個方法。

public interface ComponentCallbacks

這個接口包括兩個方法,其中一個就是onConfigurationChanged 方法。
Activity、Service、ContentProvider 都實現瞭這個接口,所以在代碼中,我們可以重寫這個方法,便於回調處理。那麼,這個方法何時才會被回調呢?

abstract voidonConfigurationChanged(Configuration newConfig)
Called by the system when the device configuration changes while your component is running.

設備配置發生變化的時候,就會回調。你可能實在憋不住要問,設備配置指哪些?
android:configChanges=["mcc", "mnc", "locale",
                      "touchscreen", "keyboard", "keyboardHidden",
                      "navigation", "screenLayout", "fontScale", "uiMode",
                      "orientation", "screenSize", "smallestScreenSize"]
這裡需要提醒一下,如果使用 Activity 配合 onConfigurationChanged 方法,需要在其 menifest.xml 中添加:
android:configChanges 屬性。

所以,如果你有需要可以在上面的三大組件中重寫該方法,做你自己的邏輯處理!

如果,在 Settings 裡面改變語言之後,在我們其它的 App 中可以註冊某個廣播就可以接收到這種變化,就更好瞭!
恩,當然可以!

註冊一個廣播:
[java]
<span style="font-size:18px;"><span style="font-family:'Comic Sans MS';">IntentFilter filter = new IntentFilter(); 
        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); 
        registerReceiver(receiver, filter);</span></span> 

接收廣播:
[java]
<span style="font-size:18px;"><span style="font-family:'Comic Sans MS';">private BroadcastReceiver receiver = new BroadcastReceiver() { 
         
        @Override 
        public void onReceive(Context context, Intent intent) { 
            String action = intent.getAction(); 
            if("android.intent.action.CONFIGURATION_CHANGED".equals(action)) { 
                // to do 
            } 
        } 
    };</span></span> 

別忘記在合適的位置取消註冊:
unregisterReceiver(receiver);

這樣做的話,要比直接重寫onConfigurationChanged 方法,更加靈活,因為有些沒有實現這個接口的android組件一大把,但是接收廣播對於大多數組件來說還還是比較簡單的!在以後的博客中,我和大傢交流一下關於自定義的View如何接收廣播!

關於 Intent.ACTION_CONFIGURATION_CHANGED,可以參考 sdk 文檔。

有的人說,我現在隻關心設備語言變化的那個 action,到底有木有?有!

Intent.ACTION_LOCALE_CHANGED

如果你有興趣,可以參考 /frameworks/base/services/java/com/android/server/am/ActivityManagerService.java,關鍵代碼如下:
[java]
<span style="font-size:18px;"><span style="font-family:'Comic Sans MS';"> /**
     * Do either or both things: (1) change the current configuration, and (2)
     * make sure the given activity is running with the (now) current
     * configuration.  Returns true if the activity has been left running, or
     * false if <var>starting</var> is being destroyed to match the new
     * configuration.
     */ 
    public boolean updateConfigurationLocked(Configuration values, 
            ActivityRecord starting) { 
        int changes = 0; 
         
        boolean kept = true; 
         
        if (values != null) { 
            Configuration newConfig = new Configuration(mConfiguration); 
            changes = newConfig.updateFrom(values); 
            if (changes != 0) { 
                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) { 
                    Slog.i(TAG, "Updating configuration to: " + values); 
                } 
                 
                EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes); 
 
                if (values.locale != null) { 
                    saveLocaleLocked(values.locale,  
                                     !values.locale.equals(mConfiguration.locale), 
                                     values.userSetLocale); 
                } 
 
                mConfigurationSeq++; 
                if (mConfigurationSeq <= 0) { 
                    mConfigurationSeq = 1; 
                } 
                newConfig.seq = mConfigurationSeq; 
                mConfiguration = newConfig; 
                Slog.i(TAG, "Config changed: " + newConfig); 
                 
                AttributeCache ac = AttributeCache.instance(); 
                if (ac != null) { 
                    ac.updateConfiguration(mConfiguration); 
                } 
 
                if (Settings.System.hasInterestingConfigurationChanges(changes)) { 
                    Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG); 
                    msg.obj = new Configuration(mConfiguration); 
                    mHandler.sendMessage(msg); 
                } 
         
                for (int i=mLruProcesses.size()-1; i>=0; i–) { 
                    ProcessRecord app = mLruProcesses.get(i); 
                    try { 
                        if (app.thread != null) { 
                            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " 
                                    + app.processName + " new config " + mConfiguration); 
                            app.thread.scheduleConfigurationChanged(mConfiguration); 
                        } 
                    } catch (Exception e) { 
                    } 
                } 
                Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); 
                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY 
                        | Intent.FLAG_RECEIVER_REPLACE_PENDING); 
                broadcastIntentLocked(null, null, intent, null, null, 0, null, null, 
                        null, false, false, MY_PID, Process.SYSTEM_UID); 
                if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) { 
                    broadcastIntentLocked(null, null, 
                            new Intent(Intent.ACTION_LOCALE_CHANGED), 
                            null, null, 0, null, null, 
                            null, false, false, MY_PID, Process.SYSTEM_UID); 
                } 
            } 
        } 
         
        if (changes != 0 && starting == null) { 
            // If the configuration changed, and the caller is not already 
            // in the process of starting an activity, then find the top 
            // activity to check if its configuration needs to change. 
            starting = mMainStack.topRunningActivityLocked(null); 
        } 
         
        if (starting != null) { 
            kept = mMainStack.ensureActivityConfigurationLocked(starting, changes); 
            if (kept) { 
                // If this didn't result in the starting activity being 
                // destroyed, then we need to make sure at this point that all 
                // other activities are made visible. 
                if (DEBUG_SWITCH) Slog.i(TAG, "Config didn't destroy " + starting 
                        + ", ensuring others are correct."); 
                mMainStack.ensureActivitiesVisibleLocked(starting, changes); 
            } 
        } 
         
        if (values != null && mWindowManager != null) { 
            mWindowManager.setNewConfiguration(mConfiguration); 
        } 
         
        return kept; 
    }</span></span> 

代碼中有關於,上面提到的兩個 Intent Action.

另外,保存設置 locale 信息的源碼:
[java]
<span style="font-size:18px;"><span style="font-family:'Comic Sans MS';">  /**
     * Save the locale.  You must be inside a synchronized (this) block.
     */ 
    private void saveLocaleLocked(Locale l, boolean isDiff, boolean isPersist) { 
        if(isDiff) { 
            SystemProperties.set("user.language", l.getLanguage()); 
            SystemProperties.set("user.region", l.getCountry()); 
        }  
 
        if(isPersist) { 
            SystemProperties.set("persist.sys.language", l.getLanguage()); 
            SystemProperties.set("persist.sys.country", l.getCountry()); 
            SystemProperties.set("persist.sys.localevar", l.getVariant()); 
        } 
    }</span></span> 

android 系統會發出很多廣播,但是這麼多 action,如何記住?不需要記,好好看看 Intent 類的常量定義,那裡面講的很清楚,如果你足夠勤奮和鉆研,這些就不是問題瞭!

摘自 mark in working

發佈留言