在Setting中切换中英文后,打开最近应用,里面的提示字符不会切换中英文

来源:互联网 发布:页面置换算法例题详解 编辑:程序博客网 时间:2024/05/21 13:56
最近在项目中遇到一个初看比较奇怪的问题:在Setting中进行中英文切换,比如由中文切换到英文,这时别的界面字符都已经由中文变成了英文,但是,在打开最近应用界面的时候,却发现里面的提示字符还是中文,但是其中的最近应用的apk的名字已经变成了中文,而且,关机后开机,显示的字符串就正常了。第一眼看到的感觉是:哇塞(yu men),这怎么可能,一个界面中怎么有的字符会不切换!!!
没办法,出现问题了,肯定是存在问题。只能静下心来仔细查看代码。
下面就记录下心路历程。
首先,查看这部分的源码在哪里。
源码路径:frameworks\base\policy\src\com\android\internal\policy\impl\ RecentApplicationsDialog.java
然后,查看与这个有相似功能的代码是否有问题。
我查看了之前在Framework层弹出Toast的是否有问题。经查看,弹出的Toast中的字符是会跟随Setting的中英文切换而变的。然后去查看了弹出Toast的代码:
Handler handler = new Handler(Looper.getMainLooper());handler.post(new Runnable() {                    public void run(){                        Resources res = Resources.getSystem();                        Toast.makeText(getContext(), res.getText(com.android.internal.R.string.input_again), Toast.LENGTH_SHORT).show();                    }                });
然后查看了RecentApplicationsDialog中获取字符的部分:
getContext ().getResources ().getString (com.android.internal.R.string.tipinfo);
想着会不会是由于字符的获取问题导致的,于是改了RecentApplicationsDialog中获取字符串的方式,发现毫无影响。也是自己太相信自己(傻逼)了一回,竟然没有第一时间打印。以为问题应该出在这里。最后看到真相的我眼泪掉下来。
接着,立刻开始了打印字符、打印周期函数是否执行等。
结果发现,RecentApplicationsDialog的onCreate只会在第一次显示最近应用的时候执行一次,后面再弹出Dialog,都不会执行onCreate,直接从onStart开始执行。
这样就知道为什么提示字符不会跟随切换了,因为我们是在onCreate中设置布局,然后显示相应的字符。而onCreate不是每次都执行,所以里面的提示字符就没有切换。
/** * We create the recent applications dialog just once, and it stays around * (hidden) until activated by the user. * 从这里其实就可以看出来端倪了 * @see PhoneWindowManager#showRecentAppsDialog */    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);Log.d("zmq","RecentApplicationDialog onCreate");        Context context = getContext();        if (sStatusBar == null) {sStatusBar = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);        }        Window window = getWindow();        window.requestFeature(Window.FEATURE_NO_TITLE);        window.setType(WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY);        window.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);        window.setTitle("Recents");        setContentView(com.android.internal.R.layout.recent_apps_dialog);    //设置布局mpPackageManager = getContext().getPackageManager();maActivityManager = (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);mActivityInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(mpPackageManager, 0);mIconUtilities = new IconUtilities(getContext());        final WindowManager.LayoutParams params = window.getAttributes();        params.width = WindowManager.LayoutParams.MATCH_PARENT;        params.height = WindowManager.LayoutParams.MATCH_PARENT;        window.setAttributes(params);        window.setFlags(0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);        mIcons[0] = (TextView)findViewById(com.android.internal.R.id.button0);        mIcons[1] = (TextView)findViewById(com.android.internal.R.id.button1);        mIcons[2] = (TextView)findViewById(com.android.internal.R.id.button2);        mIcons[3] = (TextView)findViewById(com.android.internal.R.id.button3);        mIcons[4] = (TextView)findViewById(com.android.internal.R.id.button4);        mIcons[5] = (TextView)findViewById(com.android.internal.R.id.button5);        mIcons[6] = (TextView)findViewById(com.android.internal.R.id.button6);        mIcons[7] = (TextView)findViewById(com.android.internal.R.id.button7); mNoAppsText = findViewById(com.android.internal.R.id.no_applications_message);     tipinfo = (TextView)findViewById(com.android.internal.R.id.tipinfo);    //要显示提示的控件TextView        ActivityManager activityManager = (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);final List<ActivityManager.RunningTaskInfo> list = activityManager.getRunningTasks(1);mGridView = (GridView) findViewById(com.android.internal.R.id.recent_gridview);mResolveInfos = getResolveInfo();mRecentAdapter = new RecentAdapter(mResolveInfos, context);mGridView.setAdapter(mRecentAdapter);if(list.get(0).topActivity.getPackageName().equals("com.android.zmq")){tipinfo.setText (Resources.getSystem().getText(com.android.internal.R.string.tipsinfo));    }mGridView.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {// TODO Auto-generated method stubtry {if (mRecentAdapter.getmResolveInfos() != null) {RecentTag recentTag = getTag(mRecentAdapter.getmResolveInfos().get(arg2));if (recentTag != null){if(list.get(0).topActivity.getPackageName().equals("com.android.zmq")){isForceStopOne = true;forceStopPackage(recentTag);mRecentAdapter.setmResolveInfos(getResolveInfo());mRecentAdapter.notifyDataSetChanged();isForceStopOne = false;}else{switchTo(recentTag);dismiss();  }  }  }} catch (Exception e) {// TODO: handle exception}}});}
知道了原因,问题就比较好解决了。既然每次都会跑onStart,那么我们就把字符的显示放在onStart中。
 /**     * Set up and show the recent activities dialog.     */    @Override    public void onStart() {        super.onStart();Log.d("zmq","RecentApplicationDialog onStart");mRecentAdapter.setmResolveInfos(getResolveInfo());mRecentAdapter.notifyDataSetChanged();        if (sStatusBar != null) {            sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);        }ActivityManager am = (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);final List<ActivityManager.RunningTaskInfo> runList = am.getRunningTasks(1);        //显示字符,这样就可以每次都切换到啦。if(runList.get(0).topActivity.getPackageName().equals("com.android.zmq")){tipinfo.setText (Resources.getSystem().getText(com.android.internal.R.string.tipsinfo));}else{tipinfo.setText(Resources.getSystem().getText(com.android.internal.R.string.recent_remove_message_pb));}        // receive broadcasts        getContext().registerReceiver(mBroadcastReceiver, mBroadcastIntentFilter);        mHandler.removeCallbacks(mCleanup);mGridView.setFocusable(true);mGridView.setFocusableInTouchMode(true);mGridView.requestFocus();    }
问题是解决了。但是我们还是要找出根源:即为什么onCreate只会执行一次。
首先看到RecentApplicationsDialog是继承Dialog,在RecentApplicationsDialog中,除了构造函数,还有onCreate、onStart、onStop等函数。
Protected MethodsvoidonCreate(Bundle savedInstanceState)
Similar to onCreate(Bundle), you should initialize your dialog in this method, including calling setContentView(View).
voidonStart()
Called when the dialog is starting.
voidonStop()
Called to tell you that you're stopping. 
RecentApplicationsDialog 中的onCreate只会执行一次,而且在退出dialog的时候会执行onStop,并且并没有看到相关的控制。于是开始在Dialog的源码中寻找根源。
在打开最近应用这个对话框的时候,会先调用RecentApplicationsDialog 的构造函数(如果已经打开过一次,则不会再运行),然后判断这个对话框是否处于showing模式,如果不是的话,则show,否则,dismiss。
void showOrHideRecentAppsDialog(final int behavior) {        mHandler.post(new Runnable() {            @Override            public void run() {                if (mRecentAppsDialog == null) {                    mRecentAppsDialog = new RecentApplicationsDialog(mContext);                }                if (mRecentAppsDialog.isShowing()) {                    switch (behavior) {                        case RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS:                        case RECENT_APPS_BEHAVIOR_DISMISS:                            mRecentAppsDialog.dismiss();                            break;                        case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH:                            mRecentAppsDialog.dismissAndSwitch();                            break;                        case RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW:                        default:                            break;                    }                } else {                    switch (behavior) {                        case RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS:                            mRecentAppsDialog.show();                            break;                        case RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW:                            try {                                mWindowManager.setInTouchMode(false);                            } catch (RemoteException e) {                            }                            mRecentAppsDialog.show();                            break;                        case RECENT_APPS_BEHAVIOR_DISMISS:                        case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH:                        default:                            break;                    }                }            }        });    }
在Dialog中,show方法:
 /**     * Start the dialog and display it on screen.  The window is placed in the     * application layer and opaque.  Note that you should not override this     * method to do initialization when the dialog is shown, instead implement     * that in {@link #onStart}.     */    public void show() {    Log.d("zmq","Dialog show");        if (mShowing) {            if (mDecor != null) {                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);                }                mDecor.setVisibility(View.VISIBLE);            }            return;        }        mCanceled = false;        //mCreated初始值为false,即第一次是会执行的        if (!mCreated) {            dispatchOnCreate(null);        }        onStart();        mDecor = mWindow.getDecorView();        if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {            final ApplicationInfo info = mContext.getApplicationInfo();            mWindow.setDefaultIcon(info.icon);            mWindow.setDefaultLogo(info.logo);            mActionBar = new ActionBarImpl(this);        }        WindowManager.LayoutParams l = mWindow.getAttributes();        if ((l.softInputMode                & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {            WindowManager.LayoutParams nl = new WindowManager.LayoutParams();            nl.copyFrom(l);            nl.softInputMode |=                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;            l = nl;        }        try {            mWindowManager.addView(mDecor, l);            mShowing = true;                sendShowMessage();        } finally {        }    }
可以看到,只有第一次的时候,mCreated为false,才会运行dispatchOnCreate,在dispatchOnCreate中:
// internal method to make sure mcreated is set properly without requiring    // users to call through to super in onCreate    void dispatchOnCreate(Bundle savedInstanceState) {    Log.d("zmq","Dialog dispatchOnCreate");Log.d("zmq","Dialog dispatchOnCreate  mCreated = "+mCreated);if(savedInstanceState!=null){Log.d("zmq","Dialog dispatchOnCreate  savedInstanceState != null");}else{Log.d("zmq","Dialog dispatchOnCreate  savedInstanceState == null");}   //mCreated第一次是false        if (!mCreated) {            //执行RecentApplicationsDialog中的onCreate            onCreate(savedInstanceState);                mCreated = true;          //设置为true,以后就不再执行。这里就是根源啦        }    }
这样我们就找到RecentApplicationsDialog中onCreate只执行一次根源啦。
执行完onCreate ,回到show方法,继续执行onStart,然后回到show方法,最后显示出dialog。
dismiss掉Dialog的时候,最终会调用dimissDialog方法,然后会调用onStop。
/**     * Dismiss this dialog, removing it from the screen. This method can be     * invoked safely from any thread.  Note that you should not override this     * method to do cleanup when the dialog is dismissed, instead implement     * that in {@link #onStop}.     */    @Override    public void dismiss() {      Log.d("zmq","Dialog dismiss");        if (Looper.myLooper() == mHandler.getLooper()) {            dismissDialog();        } else {            mHandler.post(mDismissAction);        }    }    void dismissDialog() {Log.d("zmq","Dialog dismissDialog");        if (mDecor == null || !mShowing) {            return;        }        if (mWindow.isDestroyed()) {            Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");            return;        }        try {            mWindowManager.removeViewImmediate(mDecor);        } finally {            if (mActionMode != null) {                mActionMode.finish();            }            mDecor = null;            mWindow.closeAllPanels();            onStop();            mShowing = false;            sendDismissMessage();        }    }

最后总结一句话就是:因为在Dialog这个类中,show方法中会有标志位mCreated做判断,mCreated初始值为false。在第一次创建这个Dialog的时候会执行onCreate ,后然mCreated被设置为true。再次调用show方法时,就不会去跑onCreate,只会去跑onStart了。
也算是知其所以然了~






0 0