編輯:關於Android編程
private void initPolicy() { UiThread.getHandler().runWithScissors(new Runnable() { @Override public void run() { WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); } }, 0); }在WMS構造方法中就調用initPolicy方法。在這個方法中,很明顯是在初始化mPolicy:WindowManagerPolicy這個變量。 final WindowManagerPolicy mPolicy = new PhoneWindowManager(); 在WMS中,該變量實質是PhoneWindowManager對象。PhoneWindowManager類實現了WindowManagerPolicy接口類。 PhoneWindowManager.java (android-6.0\frameworks\base\services\core\java\com\android\server\policy)
/** {@inheritDoc} */ @Override public void init(Context context, IWindowManager windowManager, WindowManagerFuncs windowManagerFuncs) { mContext = context; mWindowManager = windowManager; mWindowManagerFuncs = windowManagerFuncs; ...... mOrientationListener = new MyOrientationListener(mContext, mHandler); try { mOrientationListener.setCurrentRotation(windowManager.getRotation()); } catch (RemoteException ex) { } ...... }發現在初始化的過程中,實例化一個方位監聽對象MyOrientationListener,並且給它設置了初始值。該初始值從WMS中獲取,默認為0。 MyOrientationListener類是PhoneWindowManager的一個內部類,它繼承了WindowOrientationListener類。 class MyOrientationListener extends WindowOrientationListener { WindowOrientationListener.java (android-6.0\frameworks\base\services\core\java\com\android\server\policy)
private WindowOrientationListener(Context context, Handler handler, int rate) { mHandler = handler; mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); mRate = rate; mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER); if (mSensor != null) { // Create listener only if sensors do exist mSensorEventListener = new SensorEventListenerImpl(context); } }看到這邊,一下子就明白旋屏事件上報的大致流程。首先由傳感器計算數據確認是否上報,然後通過Handler或者回調方法來處理。這麼想是因為構造器中傳遞進來一個Handler對象,另外本身就是通過其子類調用才進入WindowOrientationListener。具體是怎麼一個流程,還要分析接下來的代碼。 在這裡,默認采用的傳感器是加速度傳感器,USE_GRAVITY_SENSOR:false 在Android7.0中,默認采用的是方向傳感器。 WindowOrientationListener構造器中,mSensorManager、mSensor、mSensorEventListener對象。在後面的enable方法中,通過mSensorManager調用registerListener為mSensor注冊監聽事件mSensorEventListener。
* Enables the WindowOrientationListener so it will monitor the sensor and call * {@link #onProposedRotationChanged(int)} when the device orientation changes. */ public void enable() { ...... mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler); mEnabled = true; } } }這裡不關注這些個點,還是去看傳感器監聽處理這一塊內容。 SensorEventListenerImpl是WindowOrientationListener的內部類,它實現了接口。 final class SensorEventListenerImpl implements SensorEventListener {
@Override public void onSensorChanged(SensorEvent event) { int proposedRotation; int oldProposedRotation; synchronized (mLock) { // The vector given in the SensorEvent points straight up (towards the sky) under // ideal conditions (the phone is not accelerating). I'll call this up vector // elsewhere. float x = event.values[ACCELEROMETER_DATA_X]; float y = event.values[ACCELEROMETER_DATA_Y]; float z = event.values[ACCELEROMETER_DATA_Z]; } } ...... // Tell the listener. if (proposedRotation != oldProposedRotation && proposedRotation >= 0) { if (LOG) { Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation + ", oldProposedRotation=" + oldProposedRotation); } onProposedRotationChanged(proposedRotation); } }傳感器數據計算過程這裡省略了,在onSensorChanged方法的最後通過函數回調上報旋屏事件。回顧上面的內容,驗證的確如此。
/** * Called when the rotation view of the device has changed. * * This method is called whenever the orientation becomes certain of an orientation. * It is called each time the orientation determination transitions from being * uncertain to being certain again, even if it is the same orientation as before. * * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants. * @see android.view.Surface */ public abstract void onProposedRotationChanged(int rotation);onProposedRotationChanged方法是聲明在WindowOrientationListener類的一個抽象方法,它具體實現在PhoneWindowManager的一個內部類,即MyOrientationListener。 PhoneWindowManager.java (android-6.0\frameworks\base\services\core\java\com\android\server\policy)
@Override public void onProposedRotationChanged(int rotation) { if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation); updateRotation(false); }
void updateRotation(boolean alwaysSendConfiguration) { try { //set orientation on WindowManager mWindowManager.updateRotation(alwaysSendConfiguration, false); //false、false } catch (RemoteException e) { // Ignore } }很明顯,是通知WMS更新rotation。 WindowManagerService.java (android-6.0\frameworks\base\services\core\java\com\android\server\wm)
/** * Recalculate the current rotation. * * Called by the window manager policy whenever the state of the system changes * such that the current rotation might need to be updated, such as when the * device is docked or rotated into a new posture. */ @Override public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) { updateRotationUnchecked(alwaysSendConfiguration, forceRelayout); }
public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) { ...... boolean changed; synchronized(mWindowMap) { changed = updateRotationUncheckedLocked(false); ...... if (changed || alwaysSendConfiguration) { sendNewConfiguration(); } ...... }一般情況,rotation都是發生變化的,也就是說updateRotationUncheckedLocked返回值通常為true,故會調用sendNewConfiguration。
/* * Instruct the Activity Manager to fetch the current configuration and broadcast * that to config-changed listeners if appropriate. */ void sendNewConfiguration() { try { mActivityManager.updateConfiguration(null); } catch (RemoteException e) { } }mActivityManager本質是AMS Server端,這裡從WMS運行至AMS, mActivityManager = ActivityManagerNative.getDefault(); ActivityManagerService.java (android-6.0\frameworks\base\services\core\java\com\android\server\am)
public void updateConfiguration(Configuration values) { enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, "updateConfiguration()"); ...... synchronized(this) { if (values == null && mWindowManager != null) { // sentinel: fetch the current configuration from the window manager values = mWindowManager.computeNewConfiguration(); } updateConfigurationLocked(values, null, false, false); ...... } }AMS中,第一步:檢查權限,沒有權限則拋一個異常;第二步:從WMS中獲取Configuration值;第三步:去真正更新Configuration值。
/** * 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 starting is being destroyed to match the new * configuration. * @param persistent TODO */ boolean updateConfigurationLocked(Configuration values, ctivityRecord starting, boolean persistent, boolean initLocale) { int changes = 0; if (values != null) { Configuration newConfig = new Configuration(mConfiguration); changes = newConfig.updateFrom(values); ...... 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_CONFIGURATION, "Sending to proc " + app.processName + " new config " + mConfiguration); app.thread.scheduleConfigurationChanged(configCopy); } } catch (Exception e) { } } ...... boolean kept = true; final ActivityStack mainStack = mStackSupervisor.getFocusedStack(); // mainStack is null during startup. if (mainStack != null) { 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 = mainStack.topRunningActivityLocked(null); } if (starting != null) { kept = mainStack.ensureActivityConfigurationLocked(starting, changes); // And we need to make sure at this point that all other activities // are made visible with the correct configuration. mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes); } } if (values != null && mWindowManager != null) { mWindowManager.setNewConfiguration(mConfiguration); } return kept; }通常當發生橫豎屏切換的時候,Activity的生命周期通常是:onConfigureationChanged --> onDestroy --> onCreate --> onStart --> onResume。一看這裡就分為兩步驟,一:通知Configuration已經改變;二:獲取棧頂的Activity,重新運行該Activity,以適配新的Configuration。 app:ProcessRecord IApplicationThread thread; // the actual proc... may be null only if // 'persistent' is true (in which case we // are in the process of launching the app) IApplicationThread是一個aidl文件。這裡最終調用的是ApplicationThread對象,而ApplicationThread類是ActivityThread的一個內部類。 private class ApplicationThread extends ApplicationThreadNative { ActivityThread.java (android-6.0\frameworks\base\core\java\android\app)
public void scheduleConfigurationChanged(Configuration config) { updatePendingConfiguration(config); sendMessage(H.CONFIGURATION_CHANGED, config); }直接通過Handler機制與ActivityThread進行通信。
public void handleMessage(Message msg) { ...... case CONFIGURATION_CHANGED: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged"); mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi; handleConfigurationChanged((Configuration)msg.obj, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); }
final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) { ...... ArrayListcallbacks = collectComponentCallbacks(false, config); freeTextLayoutCachesIfNeeded(configDiff); if (callbacks != null) { final int N = callbacks.size(); for (int i=0; i
private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) { // Only for Activity objects, check that they actually call up to their // superclass implementation. ComponentCallbacks2 is an interface, so // we check the runtime type and act accordingly. Activity activity = (cb instanceof Activity) ? (Activity) cb : null; ...... if (shouldChangeConfig) { cb.onConfigurationChanged(config); ...... }很明顯,在這裡調用了onConfigurationChanged方法。也就是常說在橫豎屏切換的時候先調用Activity的onConfigurationChanged,通常會重寫這個方法,做一些保存參數之類的操作。 在調用完onConfigurationChanged後,Activity會重新創建。所以就回到AMS的updateConfigurationLocked方法中。 kept = mainStack.ensureActivityConfigurationLocked(starting, changes); mainStack:ActivityStack。 ActivityStack.java (android-6.0\frameworks\base\services\core\java\com\android\server\am)
/** * Make sure the given activity matches the current configuration. Returns * false if the activity had to be destroyed. Returns true if the * configuration is the same, or the activity will remain running as-is * for whatever reason. Ensures the HistoryRecord is updated with the * correct configuration and all other bookkeeping is handled. */ final boolean ensureActivityConfigurationLocked(ActivityRecord r, int globalChanges) { ...... if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) { // Aha, the activity isn't handling the change, so DIE DIE DIE. r.configChangeFlags |= changes; r.startFreezingScreenLocked(r.app, globalChanges); r.forceNewConfig = false; if (r.app == null || r.app.thread == null) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is destroying non-running " + r); destroyActivityLocked(r, true, "config"); } else if (r.state == ActivityState.PAUSING) { // A little annoying: we are waiting for this activity to // finish pausing. Let's not do anything now, but just // flag that it needs to be restarted when done pausing. if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is skipping already pausing " + r); r.configDestroy = true; return true; } else if (r.state == ActivityState.RESUMED) { // Try to optimize this case: the configuration is changing // and we need to restart the top, resumed activity. // Instead of doing the normal handshaking, just say // "restart!". if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is relaunching resumed " + r); relaunchActivityLocked(r, r.configChangeFlags, true); r.configChangeFlags = 0; } else { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is relaunching non-resumed " + r); relaunchActivityLocked(r, r.configChangeFlags, false); r.configChangeFlags = 0; } // All done... tell the caller we weren't able to keep this // activity around. return false; } ...... return true; }
private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume) { ...... try { if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, "Moving to " + (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + r); r.forceNewConfig = false; r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes, !andResume, new Configuration(mService.mConfiguration), new Configuration(mOverrideConfig)); // Note: don't need to call pauseIfSleepingLocked() here, because // the caller will only pass in 'andResume' if this activity is // currently resumed, which implies we aren't sleeping. } catch (RemoteException e) { if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, "Relaunch failed", e); } ...... return true; }一眼就看明白,同樣是在ApplicationThread內部通過Handler與ActivityThead進行通信,然後真正去執行relaunch操作。 總結: 旋屏事件上報流程: 1、傳感器(默認為加速度傳感器)計算數據,決定是否上報旋屏事件。 2、上報是通過回調函數實現的,在PhoneWindowManger中實現指定接口。 3、PhoneWindowManger與WMS進行交互,通知其更新rotation。 4、WMS更新rotation後,發現的確發生改變了,去通知AMS處理。 5、AMS獲取WMS中rotation數據,然後更新處理。通常流程是通過ApplicationThread與ActivityThread交互。最後調用流程是 onConfigurationChanged --> Activity重新創建。
大家對這些功能都是看的多了,然後對上拉刷新和下拉加載的原理都是非常清楚的,所以實現這功能其實也就是為了讓大家能夠從眾多的同行們來進行比較學習而已,雖然即使是這樣,但是面試
用到的兩個png圖片首先是自定義theme,不能用默認的主題,會報錯;you cannot combined....。修改res/values/styles.xml:上面
在android中,文本控件主要包括TextView控件和EditView控件,本節先對TextView控件的用法進行詳細介紹。 &n
開發工具Android Studio今天公司UI要求軟件對話框改成加圖片的,以前沒有做過,所以就學習了一下,廢話不多說,看效果:創建XML文件dialog_lsit_it