Android之動態設置App語言環境

首先,爲什麼會寫這篇文章呢?那就是今天端午節,苦日子終於熬出頭了,app版本正式交付。O(∩_∩)O哈哈~

進入正題,開發過程中需要動態改變app的語言環境,這個時候就需要用到Android自帶的Locale類。也許會說,app的語言的環境不是系統自動尋找values-的文件嗎?要知道,這只是系統根據自帶語言設置應用資源的搜索策略,即values- → valuse的搜索順序。但是,要考慮到有時候應用的語言環境要區別於系統語言環境,這個時候,代碼中動態改變app語言環境就很有必要。

直接上代碼:

 /**
     * 切換應用語言環境
     *
     * @param context
     */
    private void loadAppLanguage(Context context, Locale targtLocal) {
        //todo 模擬器無法跟隨指定語言環境
        if (context == null) {
            throw new NullPointerException("loadAppLanguage failed because context is null");
        }
        lg.e("loadAppLanguage:" + targtLocal);

        Configuration config = context.getResources().getConfiguration();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            config.setLocale(targtLocal);
        } else {
            //noinspection deprecation
            config.locale = targtLocal;
        }
        context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
        Locale.setDefault(targtLocal);
    }

so 簡單,有木有~~

好,扔給妹子測試去,我先喝杯吃個水果拼盤先。話說,公司福利還是不錯的哈~
然後,~~~咦,怎麼屏幕旋轉的時候都變成中文了?還有,系統切換語言成繁文的時候,app怎麼也跟着變了?
翻閱相關資料後,發現屏幕旋轉時或者系統語言變更後都需要重新設置。好,上代碼:

//註冊語言變更監聽器
        context.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                lg.e("監聽到語言變化");
                loadAppLanguage(context, locale);
            }
        }, new IntentFilter(Intent.ACTION_LOCALE_CHANGED));

關於監聽屏幕旋轉切換時的監聽操作,網上多半是直接重寫Activity的onConfigurationChanged方式,但說實話,這種方法弊端很明顯,如果是老舊代碼遺留的歷史債務,改起來就相當噁心。而且我很討厭到處改代碼,信奉的原則是怎麼偷懶怎麼來。(Application也可以在onConfigurationChanged設置 - 用了,接下來還怎麼裝*?O(∩_∩)O~ 其實在後面會說明,一個單獨的模塊,最好不要讓開發人員做太多的控制,模塊內部把所有的工作都做完最好!)
仔細想想,爲什麼不直接Hook系統的狀態切換呢?分析下,我們只要拿到UI進程的狀態通信類,就可以做自己的工作了。Hook UI線程的原理這裏就按下不表了,感興趣的自己去網上查資料,代碼貼在下面。

    /**
     * 監聽主線程的Handler對象
     */
    public static void hookActivityThreadHandler(Handler.Callback callback) {
        if (callback == null) {
            lg.e("not support hookActivityThreadHandler because Handler.Callback not define");
            return;
        }
        try {
            //由於ActivityThread無法直接訪問
            Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
            currentActivityThreadMethod.setAccessible(true);
            //獲取主線程對象
            Object activityThreadObj = currentActivityThreadMethod.invoke(null);
            //獲取mH字段q
            Field mH = activityThreadClass.getDeclaredField("mH");
            mH.setAccessible(true);
            //獲取Handler
            Handler handler = (Handler) mH.get(activityThreadObj);
            //獲取原始的mCallBack字段
            Field mCallBack = Handler.class.getDeclaredField("mCallback");
            mCallBack.setAccessible(true);
            //這裏設置自定義實現的CallBack接口對象
            mCallBack.set(handler, callback);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

好了,入口代碼如下,

public void setAppLocale(final Context context, final Locale locale) {
        //避免多進程重複調用
        if (!TempUtils.isUIPid(context)) {
            return;
        }
        loadAppLanguage(context, locale);
        //註冊語言變更監聽器
        context.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                lg.e("監聽到語言變化");
                loadAppLanguage(context, locale);
            }
        }, new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
        //監聽橫豎屏切換
        HookUtils.hookActivityThreadHandler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                //118:@link ActivityThread - CONFIGURATION_CHANGED
                if (msg.what == 118) {
                    if (msg.obj instanceof Configuration) {
                        lg.e("檢測到屏幕旋轉,重置app語言環境");
                        loadAppLanguage(context, locale);
                    }

                }
                return false;
            }
        });
    }

只需要在Application的入口地方直接調用

new LanguageManager().setAppLocale(this,Locale.ENGLISH);

,剩下的所有工作全部自動幫你管理。

小Tips:
1.其實動態設置語言這個東西可以用的很活。比如現在需要把頁面佈局改下樣式然後快速上線,但是又不想對原有的佈局以及資源文件進行改動,這個時候完全可以自定義莫須有的語言環境,然後。。。你懂得!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章