1. 程式人生 > >Dialog顯示和消失流程分析

Dialog顯示和消失流程分析

本文所引用的程式碼為Android 5.0(API 22)版本

Dialog類實現了DialogInterface, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback這個五個介面。比較常用到的三個介面:Window.Callback是接收螢幕touch事件;KeyEvent.Callback為接收鍵盤按鍵事件和實體鍵訊息;Window.OnWindowDismissedCallback為接收視窗消失回撥。

本文從Dialog的建立,隱藏,顯示和移除步驟來分析相關的程式碼流程。最後補充了幾個常見的異常分析。

建立

看看構造方法程式碼:

    //提供給外部呼叫人口
    public Dialog(Context context) {
        this(context, 0, true);
    }

    //提供給外部呼叫人口
    public Dialog(Context context, int theme) {
        this(context, theme, true);
    }

    //真正執行初始化的構造方法
    Dialog(Context context, int theme, boolean createContextThemeWrapper) {
        if
(createContextThemeWrapper) { if (theme == 0) { TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme, outValue, true); theme = outValue.resourceId; } mContext = new
ContextThemeWrapper(context, theme); } else { mContext = context; } //獲取WindowManagerImpl物件,這個物件一般為Activity的windowManger mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); //建立PhoneWindow物件 Window w = PolicyManager.makeNewWindow(mContext); //賦值,mWindow即PhoneWindow物件,不是WindowManger mWindow = w; 設定Window.Callback回撥 w.setCallback(this); //window dismiss回撥 w.setOnWindowDismissedCallback(this); //呼叫Window的setWindowManager建立PhoneWindow內部的WindowMangerImpl w.setWindowManager(mWindowManager, null, null); //預設設定為居中 w.setGravity(Gravity.CENTER); //接收非同步訊息的Handler mListenersHandler = new ListenersHandler(this); }

PolicyManager#makeNewWindow相關程式碼:
路徑:/sources/android-22/com/android/internal/policy/PolicyManager.java

    public static Window makeNewWindow(Context context) {
        return sPolicy.makeNewWindow(context);
    }

sPolicy為Policy類的物件:
路徑:/sources/android-22/com/android/internal/policy/impl/Policy.java

    public Window makeNewWindow(Context context) {
        return new PhoneWindow(context);
    }

Window的setWindowManager方法相關程式碼:

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
        setWindowManager(wm, appToken, appName, false);
    }

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

顯示

show方法程式碼:

    public void show() {
        if (mShowing) {//dialog已呼叫過show方法,呼叫dismiss會設定為false
            if (mDecor != null) {
                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                }
                //顯示Dialog的根View,即Dialog便可見
                mDecor.setVisibility(View.VISIBLE);
            }
            return;
        }

        mCanceled = false;

        if (!mCreated) {
            dispatchOnCreate(null);
        }

        onStart();
        //建立DecorView
        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 WindowDecorActionBar(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 {
            //新增到當前顯示的視窗,便可以看見dialog
            mWindowManager.addView(mDecor, l);
            mShowing = true;
            //傳送dialog顯示訊息
            sendShowMessage();
        } finally {
        }
    }

mDecor為DecorView物件,DecorView繼承了FrameLayout。在Android 6.0以下版本,DecorView為PhoneWindow的內部類;6.0及以上版本,則被抽出,為一個獨立的類。

我們在使用Dialog呼叫findViewById和setContentView方法,其實都是PhoneWindow中的方法在執行相應的操作。
Dialog中相關方法的程式碼:

    public View findViewById(int id) {
        return mWindow.findViewById(id);
    }

    public void setContentView(int layoutResID) {
        mWindow.setContentView(layoutResID);
    }

    public void setContentView(View view) {
        mWindow.setContentView(view);
    }

    public void setContentView(View view, ViewGroup.LayoutParams params) {
        mWindow.setContentView(view, params);
    }

    public void addContentView(View view, ViewGroup.LayoutParams params) {
        mWindow.addContentView(view, params);
    }

再看看PhoneWindow中的相關程式碼:

    //獲取DecorView
    @Override
    public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            //如果沒有,則建立
            installDecor();
        }
        return mDecor;
    }

    //建立和初始化DecorView
    private void installDecor() {
        if (mDecor == null) {//如果為null,則建立
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        ...//省略部分程式碼
    }

    //建立DecorView物件
    protected DecorView generateDecor() {
        //建立DecorView,-1為佈局資源id,即不解析xml佈局檔案
        return new DecorView(getContext(), -1);
    }

    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        //在Dialog的mWindow中,cb為Dialog本身
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

    @Override
    public void setContentView(View view) {
        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            mContentParent.addView(view, params);
        }
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

    @Override
    public void addContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            // TODO Augment the scenes/transitions API to support this.
            Log.v(TAG, "addContentView does not support content transitions");
        }
        mContentParent.addView(view, params);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

再看看mWindowManager.addView(mDecor, l)這步操作。從建立這一節的分析可知mWindowManager為WindowMangerImpl的物件。
先介紹下WindowMangerImpl程式碼:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    //
    private final Display mDisplay;
    private final Window mParentWindow;

    private IBinder mDefaultToken;

    ...//省略部分程式碼

    //新增View
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

    private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
        // Only use the default token if we don't have a parent window.
        if (mDefaultToken != null && mParentWindow == null) {
            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }

            // Only use the default token if we don't already have a token.
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            if (wparams.token == null) {
                wparams.token = mDefaultToken;
            }
        }
    }

    ...//省略部分程式碼

    //移除View,後面的dimiss會呼叫到這個方法
    public void removeViewImmediate(View view) {
        //true為立即移除
        mGlobal.removeView(view, true);
    }

    ...//省略部分程式碼
}

繼續看WindowManagerGobal的addView方法程式碼:

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...//省略

        //ViewRootImpl接收接收底層服務傳送來的螢幕觸控事件,然後將這些事件傳遞給DecorView,
        //並返回結果(true或false)給底層服務。
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            //建立物件
            root = new ViewRootImpl(view.getContext(), display);
            //設定佈局屬性
            view.setLayoutParams(wparams);

            //新增到記錄集合,方便以後查詢和移除
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
            //新增DecorView到ViewRootView,執行完這一步,dialog便能接收螢幕事件
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

關於ViewRootImpl如何接收和傳遞螢幕事件,可以閱讀Activity touch事件傳遞流程分析
經歷了setContent和show,漂亮的Dialog便顯示在手機上。

隱藏和消失

隱藏呼叫的是hide方法。如果dialog在當前頁面被頻繁呼叫,則可以用這個方法。它不會將Dialog的根檢視mDecor從當前移除,僅僅是將其設定為不可見。
hide方法程式碼

    public void hide() {
        if (mDecor != null) {
            mDecor.setVisibility(View.GONE);
        }
    }

當我們呼叫程式碼dismiss,點選back鍵或點選dialog外部區域,便可以將dialog隱藏。

    //提供給外部呼叫的方法
    public void dismiss() {
        if (Looper.myLooper() == mHandler.getLooper()) {
            //在主執行緒,則直接呼叫
            dismissDialog();
        } else {
            //非主執行緒,則傳送訊息,讓mListenersHandler處理
            mHandler.post(mDismissAction);
        }
    }

    //真正執行dismiss的方法
    void dismissDialog() {
        //如果mDecor為null或dismiss的,則返回
        if (mDecor == null || !mShowing) {
            return;
        }

        //如果PhoneWindow被銷燬,則返回
        if (mWindow.isDestroyed()) {
            Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
            return;
        }

        try {
            //移除mDecor,其實是mWindowManager的內部WindowManagerGlobal物件執行移除操作
            mWindowManager.removeViewImmediate(mDecor);
        } finally {
            if (mActionMode != null) {
                mActionMode.finish();
            }
            mDecor = null;//置為null,釋放
            mWindow.closeAllPanels();
            onStop();
            mShowing = false;//修改標誌為false

            //傳送dismiss事件
            sendDismissMessage();
        }
    }

這個方法裡最重要的操作為mWindowManager.removeViewImmediate(mDecor),這步操作將Dialog直接從當前檢視移除,並銷燬釋放。所以在頁面中被頻繁顯示的的dialog採用hide比dimiss更高效。

繼續看WindowManagerGlobal程式碼:

    public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }

        synchronized (mLock) {
            //查詢當前View在記錄集合中的位置
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            //移除ViewRootImpl
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }

            //如果要移除的View與記錄中的不同,則丟擲異常
            throw new IllegalStateException("Calling with view " + view
                    + " but the ViewAncestor is attached to " + curView);
        }
    }

    //查詢位置
    private int findViewLocked(View view, boolean required) {
        final int index = mViews.indexOf(view);
        if (required && index < 0) {
            //如果該View已被移除,則丟擲異常
            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
        }
        return index;
    }

    private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();

        if (view != null) {//斷開鍵盤與當前View的關聯
            InputMethodManager imm = InputMethodManager.getInstance();
            if (imm != null) {
                imm.windowDismissed(mViews.get(index).getWindowToken());
            }
        }
        //ViewRootImpl執行die操作
        boolean deferred = root.die(immediate);
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }

經過以上三個步驟的分析,我們清楚知道了Dialog是如何建立,顯示和消失移除。

補充:常見的異常分析

工作中可能會出現的一些異常,現在就分析下它們出現的原因。

WindowLeaked

異常資訊:

android.view.WindowLeaked: Activity xx.xxActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{3e7fbc08 V.E..... R.....I. 0,0-960,231} that was originally added here
at android.view.ViewRootImpl.<init>(ViewRootImpl.java:462)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:278)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
at android.app.Dialog.show(Dialog.java:311)

這個是在Activity執行destroy時,dialog還沒有被執行dismiss操作而丟擲的。需要在onPause()或onStop()中呼叫dismiss釋放dialog視窗。

如果跟著日誌顯示的類去查詢,估計會轉暈。其實,它在ActivityThread執行handleDestroyActivity操作時丟擲的。如果跟著程式碼顯示的日誌,該異常出現在onDestroy的日誌後面。
相關程式碼:

    //ActivityThread的handleDestroyActivity方法程式碼
    private void handleDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance) {
        ActivityClientRecord r = performDestroyActivity(token, finishing,
                configChanges, getNonConfigInstance);
        if (r != null) {
            cleanUpPendingRemoveWindows(r);
            WindowManager wm = r.activity.getWindowManager();
            View v = r.activity.mDecor;
            if (v != null) {
                if (r.activity.mVisibleFromServer) {
                    mNumVisibleActivities--;
                }
                //當前Activity的window token,
                IBinder wtoken = v.getWindowToken();
                if (r.activity.mWindowAdded) {
                    if (r.onlyLocalRequest) {
                        // Hold off on removing this until the new activity's
                        // window is being added.
                        r.mPendingRemoveWindow = v;
                        r.mPendingRemoveWindowManager = wm;
                    } else {
                        wm.removeViewImmediate(v);
                    }
                }
                if (wtoken != null && r.mPendingRemoveWindow == null) {
                    //關閉所有與wtoken相關的所有DecorView
                    WindowManagerGlobal.getInstance().closeAll(wtoken,
                            r.activity.getClass().getName(), "Activity");
                }
                r.activity.mDecor = null;
            }
            if (r.mPendingRemoveWindow == null) {
                // If we are delaying the removal of the activity window, then
                // we can't clean up all windows here.  Note that we can't do
                // so later either, which means any windows that aren't closed
                // by the app will leak.  Well we try to warning them a lot
                // about leaking windows, because that is a bug, so if they are
                // using this recreate facility then they get to live with leaks.
                //關閉所有與token相關的所有DecorView
                WindowManagerGlobal.getInstance().closeAll(token,
                        r.activity.getClass().getName(), "Activity");
            }

            …//省略程式碼
        }
        …//省略程式碼
    }

    //WindowManagerGlobal的closeAll方法
    public void closeAll(IBinder token, String who, String what) {
        synchronized (mLock) {
            int count = mViews.size();
            //Log.i("foo", "Closing all windows of " + token);
            //遍歷DecorView,對比token
            for (int i = 0; i < count; i++) {
                //Log.i("foo", "@ " + i + " token " + mParams[i].token
                //        + " view " + mRoots[i].getView());
                //如果當前傳入的token為null,或DecorView的token與之相同
                if (token == null || mParams.get(i).token == token) {
                    ViewRootImpl root = mRoots.get(i);

                    //Log.i("foo", "Force closing " + root);
                    //當前傳入的who為‘Activity’
                    if (who != null) {
                        //即這個視窗沒有被移除,出現洩漏了
                        WindowLeaked leak = new WindowLeaked(
                                what + " " + who + " has leaked window "
                                + root.getView() + " that was originally added here");
                        leak.setStackTrace(root.getLocation().getStackTrace());
                        Log.e(TAG, "", leak);
                    }

                    //將其移除
                    removeViewLocked(i, false);
                }
            }
        }
    }

IllegalArgumentException

這個異常呼叫dismiss時出現的,常出現在WindowLeaked之後。在長時間的非同步操作後,Activity可能被回收或呼叫finish釋放,再在主執行緒呼叫dismiss時可能會出現這個異常。主要出現在網路請求結束關閉載入框時。解決方法:在dismiss前使用isFinishing()判斷當前Activity是否被finish,或者在onStop()或onPaus()方法中呼叫dismiss,再在onStart方法中判斷是否需要顯示。
異常資訊:

java.lang.IllegalArgumentException: View=com.android.internal.policy.impl.PhoneWindow$DecorView{3e7fbc08 V.E..... R.....I. 0,0-960,231} not attached to window manager
at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:416)
at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:342)
at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:116)
at android.app.Dialog.dismissDialog(Dialog.java:354)
at android.app.Dialog.dismiss(Dialog.java:337)

丟擲異常的程式碼在隱藏和消失這節的WindowManagerGlobal的findViewLocked方法中。因為mDecor已經被移除了,而dialog又沒有收到關閉訊息,因此dialog認為mDecor還在當前檢視中,所以再次remove便報錯了。

BadTokenException

這個異常是使用new建立Dialog是沒有傳入Activity物件,而是其它的Context。導致token驗證沒有通過,而丟擲異常。如果具有系統許可權,則可以以其它的Context建立。

android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:685)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:289)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
at android.app.Dialog.show(Dialog.java:311)

ViewRootImpl.setView的程式碼:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;

                …//省略
                int res; /* = WindowManagerImpl.ADD_OKAY; *///省略
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    //驗證token
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
                } catch (RemoteException e) {
                    …//省略
                } finally {
                    …//省略
                }

                …//省略

                //判斷token是否可用
                if (res < WindowManagerGlobal.ADD_OKAY) {
                    mAttachInfo.mRootView = null;
                    mAdded = false;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    switch (res) {
                        case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
                        case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not valid; is your activity running?");
                        …//省略
                    }
                    throw new RuntimeException(
                            "Unable to add window -- unknown error code " + res);
                }

                …//省略
            }
        }
    }

相關推薦

Dialog顯示消失流程分析

本文所引用的程式碼為Android 5.0(API 22)版本 Dialog類實現了DialogInterface, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, Window

popupwindow 顯示消失的動畫

前段時間搞android 動畫,在頁面中使用動畫後效果還是很不錯的,大大提升了使用者的體驗感。 但是,偶然的機會,要在popupwindow 中,新增一個 顯示和消失的動畫,我用之前的動畫效果,居然顯示效果不對了。 同樣是相同的程式碼,在頁面上,就可以很好的顯示,

控制元件漸變式顯示消失動畫,AlphaAnimation

實現控制元件漸變式的顯示和消失,這裡我們用到的是alphaAnimation  這裡不做api的介紹,很簡單,度娘一堆一堆的,我這邊僅僅是把程式碼寫出來,大家一看就會懂 第一步:建立動畫物件 private AlphaAnimation appearAnimator; p

android 對軟鍵盤的顯示消失監聽

1.首先在配置檔案中新增 android:windowSoftInputMode="adjustResize" 意思是activity會根據軟鍵盤的顯示和消失始終調節佈局的大小 2. @Override protected void onCreate(Bundle sa

SDL實現overlay方式雙屏顯示的應用流程分析(thinkvd開發日誌)

由於在參與開發 thinkvd video converter 遇到釋放SDL記憶體的問題,應用中需要在預覽、全屏、雙屏及CLIP之間來回切換,再次看了SDL相關的原始碼,把SDL實現的過程簡單說一下。 SDL開發包中自帶的樣例:testsprite2.c中就是一個實現多屏顯

關於S3C2440儲存器地址分配啟動流程分析

學習嵌入式,最開始應該瞭解就是地址空間的分配,真正搞清楚每個地址代表的位置,才有了入門的基礎。 1、地址分配(27根線如何尋找1G空間) S3C2440集成了豐富了外設控制器(LCD控制器、USB Device控制器、USB Host控制器、NAND FLASH控制器、I2

qemu翻譯執行流程分析

一.qemu簡介          qemu是使用動態二進位制翻譯的cpu模擬器,它支援兩種執行模式:全系統模擬和使用者態模擬。在全系統模擬下,qemu可以模擬處理器和各種外設,可以執行作業系統。使用者態可以執行為另外一種cpu編譯的程序,前提是兩者執行的os要一致

JS側邊欄的顯示消失

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <st

Java多執行緒總結(6)— 執行緒池的基本使用執行流程分析

1 執行緒池的實現原理及基本類結構   合理利用執行緒池能夠帶來三個好處。 降低資源消耗。通過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。 提高響應速度。當任務到達時,任務可以不需要等到執行緒建立就能立即執行。 提高執行緒的可管理性。執行緒是稀缺

okhttp異步請求流程源碼分析

源碼分析 syn -s CA 執行 font IE AS style 1、創建一個OkHttpClient對象。 2、構建一個Request對象,通過OkHttpClient和Request對象,構建出Call對象。 3、執行Call的enqueue方

大資料入門環境搭建整理、大資料入門系列教程合集、大資料生態圈技術整理彙總、大資料常見錯誤合集、大資料的離線實時資料處理流程分析

本篇文章主要整理了筆者學習大資料時整理的一些文章,文章是從環境搭建到整個大資料生態圈的常用技術整理,環境希望可以幫助到剛學習大資料到童鞋,大家在學習過程中有問題可以隨時評論回覆! 大資料生態圈涉及技術: Hadoop、MapReduce、HDFS、Hive、Hbase、Spark、Scala

Android進階3:Activity原始碼分析(2) —— Activity啟動銷燬流程(8.0)

上篇文章講述了app從啟動建立Activity呼叫onCreate,onStart, onResume方法,這篇文章講述一下Activity啟動的另一個切入點:startActivity方法,啟動Activity。 通過上一篇文章,我們總結一下: 1:A

供應鏈管理01——導論製造與服務流程分析

專業課是供應鏈管理,導師們的研究也多集中在庫存和物流的優化,所以還是好好學習專業課,要不然到兩年研究生好像白上。到這個十一就差不多上了一個月的專業課,感覺課堂上教授的內容有限,一些課題需要自己課後進行探索性的分析,多多鑽研。就像導師說的,時間真的是海綿一樣,做好

Android顯示vsync訊號的虛擬化處理流程

android系統在4.4之後加入了黃油計劃,surfaceflinger對顯示的處理也變得複雜起來。由於添加了vsyn虛擬化機制,app將要顯示的內容和surfaceflinger對顯示內容的合成分成了兩個部分,而兩者開始的訊號都是從vsync發出的。這裡就涉及vsync訊

dialog 彈窗顯示關閉

最近在公司改別人的一個專案  發現一個難點  其實這個點其實很簡單 但是也讓我想了很久,所以寫一下  記住這個事件。事件主要來源自己瞭解的太少了。功底不是很好。   現在來了解一下  彈窗  彈窗自己做專案發現有三種提示性彈窗 dialog動態提示就 Prodialog自定義

Android Dialog點選按鈕不關閉,控制視窗的顯示關閉

想象一下這樣的一個情景:彈出一個對話方塊,裡面可以編輯sql語句,編輯好以後點選確定按鈕,執行sql,此時對話方塊也會消失。如果sql順利執行,這種互動還是很不錯的。但是一旦sql執行出錯,對話方塊還是會關閉,對話方塊中的sql也就沒有了,只能重新編輯。如果sql很複雜,那

Android系統載入Apk檔案的時機流程分析(1)--Android 4.4.4 r1的原始碼

Android系統在啟動時安裝應用程式的過程,這些應用程式安裝好之後,還需要有一個Home應用程式來負責把它們在桌面上展示出來,在Android系統中,這個預設的Home應用程式就是Launcher了。Android系統的Home應用程式Launcher是由Activit

android 自定義dialog彈出消失動畫

自定義dialog視窗,根據座標可隨意設定dialog顯示位置,實現了視窗彈出動畫 Java程式碼: package com.sunxu.org.IndividualityDialog; import android.app.Activity; import and

Android狀態列顯示電池狀態程式碼流程分析

BatteryController.java 註冊廣播接收器,接收Intent.ACTION_BATTERY_CHANGED廣播 之後呼叫BatteryStateChangeCallback cb.onBatteryLevelChanged(level, plugged)來

android之MTP框架流程分析

1 static void android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage) 2 { 3 Mutex::Autolock autoLock(sMutex); 4 5