android 接入USB输入设备后应用重跑onCreate

简介: android 接入USB输入设备后应用重跑onCreate

平台:


RK3288 + Android7.12


问题:


打开测试应用, 并在各个生命周期中加入LOG, 当接入USB外设后, 会导致Activity重走了一次生命周期.


LOG如下:
2017-01-02 11:35:20.189com.test.app I/EntranceActivity: ALog > LIEF[ -> onCreate]
2017-01-02 11:35:20.246com.test.app I/EntranceActivity: ALog > LIEF[onCreate -> onStart]
2017-01-02 11:35:20.249com.test.app I/EntranceActivity: ALog > LIEF[onStart -> onResume]
接上USB外设
2017-01-02 11:35:59.621com.test.app I/EntranceActivity: ALog > LIEF[onResume -> onPause]
2017-01-02 11:35:59.622com.test.app I/EntranceActivity: ALog > LIEF[onPause -> onSaveInstanceState]
2017-01-02 11:35:59.636com.test.app I/EntranceActivity: ALog > LIEF[onSaveInstanceState -> onStop]
2017-01-02 11:35:59.636com.test.app I/EntranceActivity: ALog > LIEF[onStop -> onDestroy]
2017-01-02 11:35:59.686com.test.app I/EntranceActivity: ALog > LIEF[ -> onCreate]
2017-01-02 11:35:59.704com.test.app I/EntranceActivity: ALog > LIEF[onCreate -> onStart]
2017-01-02 11:35:59.706com.test.app I/EntranceActivity: ALog > LIEF[onStart -> onRestoreInstanceState]
2017-01-02 11:35:59.708com.test.app I/EntranceActivity: ALog > LIEF[onRestoreInstanceState -> onResume]


解决方案:


在AndroidManifest.xml中, 指定activity的定义加上:


android:configChanges="screenSize|keyboard|keyboardHidden|navigation"


在对应的Activity中, 新增:


@Override
    public void onConfigurationChanged(Configuration newConfig) {
  //USB 拔插动作, 这个方法都会被调用.
        super.onConfigurationChanged(newConfig);
}


修改后LOG:
2017-01-01 20:10:36.561com.test.app I/EntranceActivity: ALog > LIFE[ -> onCreate]
2017-01-01 20:10:36.627com.test.app I/EntranceActivity: ALog > LIFE[onCreate -> onStart]
2017-01-01 20:10:36.630com.test.app I/EntranceActivity: ALog > LIFE[onStart -> onResume]
拔插USB:
2017-01-01 20:13:15.329com.test.app I/EntranceActivity: ALog > LIFE[onResume -> onConfigurationChanged]


代码跟踪:


|–frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp


mInputManager = new InputManager(eventHub, this, this);


|-- frameworks/native/services/inputflinger/InputDispatcher.cpp


void InputDispatcher::doNotifyConfigurationChangedInterruptible(
        CommandEntry* commandEntry) {
    mLock.unlock();
#ifndef INPUT_BOX
  //-----------mPolicy来自 new InputManager(eventHub, this, this);---------
    mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
#endif
    mLock.lock();
}


|–frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp


void NativeInputManager::notifyConfigurationChanged(nsecs_t when) {
#if DEBUG_INPUT_DISPATCHER_POLICY
    ALOGD("notifyConfigurationChanged - when=%lld", when);
#endif
    JNIEnv* env = jniEnv();
    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyConfigurationChanged, when);
    checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
}


|–frameworks/base/services/core/java/com/android/server/input/InputManagerService.java


// Native callback.
    private void notifyConfigurationChanged(long whenNanos) {
        mWindowManagerCallbacks.notifyConfigurationChanged();
    }


|–frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java


/* Notifies that the input device configuration has changed. */
    @Override
    public void notifyConfigurationChanged() {
        mService.sendNewConfiguration();
        synchronized (mInputDevicesReadyMonitor) {
            if (!mInputDevicesReady) {
                mInputDevicesReady = true;
                mInputDevicesReadyMonitor.notifyAll();
            }
        }
    }


|-- frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java


/*
     * 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) {
        }
    }
  public Configuration computeNewConfiguration() {
        synchronized (mWindowMap) {
            return computeNewConfigurationLocked();
        }
    }


|-- frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

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();
            }
            if (mWindowManager != null) {
                mProcessList.applyDisplaySize(mWindowManager);
            }
            final long origId = Binder.clearCallingIdentity();
            if (values != null) {
                Settings.System.clearConfiguration(values);
            }
            updateConfigurationLocked(values, null, false);
            Binder.restoreCallingIdentity(origId);
        }
    }


|-- frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java


public Configuration computeNewConfiguration() {
        synchronized (mWindowMap) {
            return computeNewConfigurationLocked();
        }
    }
    private Configuration computeNewConfigurationLocked() {
        if (!mDisplayReady) {
            return null;
        }
        Configuration config = new Configuration();
        config.fontScale = 0;
        computeScreenConfigurationLocked(config);
        return config;
    }
    /** Do not call if mDisplayReady == false */
    void computeScreenConfigurationLocked(Configuration config) {
        final DisplayInfo displayInfo = updateDisplayAndOrientationLocked(
                config.uiMode);
        final int dw = displayInfo.logicalWidth;
        final int dh = displayInfo.logicalHeight;
        config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :
                Configuration.ORIENTATION_LANDSCAPE;
        config.screenWidthDp =
                (int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation, config.uiMode) /
                        mDisplayMetrics.density);
        config.screenHeightDp =
                (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation, config.uiMode) /
                        mDisplayMetrics.density);
        final boolean rotated = (mRotation == Surface.ROTATION_90
                || mRotation == Surface.ROTATION_270);
        computeSizeRangesAndScreenLayout(displayInfo, rotated, config.uiMode, dw, dh,
                mDisplayMetrics.density, config);
        config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
                | ((displayInfo.flags & Display.FLAG_ROUND) != 0
                        ? Configuration.SCREENLAYOUT_ROUND_YES
                        : Configuration.SCREENLAYOUT_ROUND_NO);
        config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
        config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
        config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, config.uiMode,
                mDisplayMetrics, dw, dh);
        config.densityDpi = displayInfo.logicalDensityDpi;
        // Update the configuration based on available input devices, lid switch,
        // and platform configuration.
        config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
        config.keyboard = Configuration.KEYBOARD_NOKEYS;
        config.navigation = Configuration.NAVIGATION_NONAV;
        int keyboardPresence = 0;
        int navigationPresence = 0;
        final InputDevice[] devices = mInputManager.getInputDevices();
        final int len = devices.length;
        for (int i = 0; i < len; i++) {
            InputDevice device = devices[i];
            if (!device.isVirtual()) {
                final int sources = device.getSources();
                final int presenceFlag = device.isExternal() ?
                        WindowManagerPolicy.PRESENCE_EXTERNAL :
                                WindowManagerPolicy.PRESENCE_INTERNAL;
                if (mIsTouchDevice) {
                    if ((sources & InputDevice.SOURCE_TOUCHSCREEN) ==
                            InputDevice.SOURCE_TOUCHSCREEN) {
                        config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
                    }
                } else {
                    config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
                }
                if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {
                    config.navigation = Configuration.NAVIGATION_TRACKBALL;
                    navigationPresence |= presenceFlag;
                } else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
                        && config.navigation == Configuration.NAVIGATION_NONAV) {
                    config.navigation = Configuration.NAVIGATION_DPAD;
                    navigationPresence |= presenceFlag;
                }
                if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
                    config.keyboard = Configuration.KEYBOARD_QWERTY;
                    keyboardPresence |= presenceFlag;
                }
            }
        }
        if (config.navigation == Configuration.NAVIGATION_NONAV && mHasPermanentDpad) {
            config.navigation = Configuration.NAVIGATION_DPAD;
            navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL;
        }
        // Determine whether a hard keyboard is available and enabled.
        boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
        if (hardKeyboardAvailable != mHardKeyboardAvailable) {
            mHardKeyboardAvailable = hardKeyboardAvailable;
            mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
            mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
        }
        boolean dualscreenconfig = Settings.System.getInt(mContext.getContentResolver(),Settings.DUAL_SCREEN_MODE,0) != 0;
        config.dualscreenflag= dualscreenconfig ? Configuration.ENABLE_DUAL_SCREEN:Configuration.DISABLE_DUAL_SCREEN;
        // Let the policy update hidden states.
        config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
        config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;
        config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
        mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
    }


未接入:I/ActivityManager: Config changes=60 {1.0 dualscreenflag=DISABLE ?mcc?mnc [zh_CN] ldltr sw1080dp w1920dp h1000dp 160dpi xlrg long land finger qwerty/v/h -nav/h s.9}
接入后:I/ActivityManager: Config changes=60 {1.0 dualscreenflag=DISABLE ?mcc?mnc [zh_CN] ldltr sw1080dp w1920dp h1000dp 160dpi xlrg long land finger qwerty/v/h dpad/v s.8}
ActivityManager: Configuration changes for ActivityRecord{2f76229 u0com.test.app/.EntranceActivity t46} ; taskChanges={}, allChanges={CONFIG_KEYBOARD_HIDDEN, CONFIG_NAVIGATION}


|-- frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java


private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
            boolean initLocale, boolean persistent, int userId, boolean deferResume) {
        int changes = 0;
  ...
                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) {
                    }
                }
  ...
  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();
            }
            if (starting != null) {
                kept = mainStack.ensureActivityConfigurationLocked(starting, changes, false);
                // And we need to make sure at this point that all other activities
                // are made visible with the correct configuration.
                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
                        !PRESERVE_WINDOWS);
            }
        }
  ...
  }


调用 onConfigurationChanged


|-- frameworks/base/core/java/android/app/ActivityThread.java


private class ApplicationThread extends ApplicationThreadNative {
        public void scheduleConfigurationChanged(Configuration config) {
            updatePendingConfiguration(config);
            sendMessage(H.CONFIGURATION_CHANGED, config);
        }
  }
    private class H extends Handler {
           public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {             
                case CONFIGURATION_CHANGED:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
                    mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
                    mUpdatingSystemConfig = true;
                    handleConfigurationChanged((Configuration)msg.obj, null);
                    mUpdatingSystemConfig = false;
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
    }
    final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
  ...
        if (callbacks != null) {
            final int N = callbacks.size();
            for (int i=0; i<N; i++) {
                ComponentCallbacks2 cb = callbacks.get(i);
                if (cb instanceof Activity) {
                    // If callback is an Activity - call corresponding method to consider override
                    // config and avoid onConfigurationChanged if it hasn't changed.
                    Activity a = (Activity) cb;
                    performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
                            config, REPORT_TO_ACTIVITY);
                } else {
                    performConfigurationChanged(cb, null, config, null, REPORT_TO_ACTIVITY);
                }
            }
        }
    }
  private void performConfigurationChangedForActivity(ActivityClientRecord r,
                                                        Configuration newBaseConfig,
                                                        boolean reportToActivity) {
        r.tmpConfig.setTo(newBaseConfig);
        if (r.overrideConfig != null) {
            r.tmpConfig.updateFrom(r.overrideConfig);
        }
        performConfigurationChanged(r.activity, r.token, r.tmpConfig, r.overrideConfig,
                reportToActivity);
        freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
    }
    private void performConfigurationChanged(ComponentCallbacks2 cb,
                                             IBinder activityToken,
                                             Configuration newConfig,
                                             Configuration amOverrideConfig,
                                             boolean reportToActivity) {
  ...
  boolean shouldChangeConfig = false;
        if ((activity == null) || (activity.mCurrentConfig == null)) {
            shouldChangeConfig = true;
        } else {
            // If the new config is the same as the config this Activity is already
            // running with and the override config also didn't change, then don't
            // bother calling onConfigurationChanged.
            int diff = activity.mCurrentConfig.diff(newConfig);
            if (diff != 0 || !mResourcesManager.isSameResourcesOverrideConfig(activityToken,
                    amOverrideConfig)) {
                // Always send the task-level config changes. For system-level configuration, if
                // this activity doesn't handle any of the config changes, then don't bother
                // calling onConfigurationChanged as we're going to destroy it.
    //---------------若AndroidManifest中, 已定义了对应的配置项, 则不重启, 并交由activity中的onConfigurationChanged去处理------------------------
                if (!mUpdatingSystemConfig
                        || (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
                        || !reportToActivity) {
                    shouldChangeConfig = true;
                }
            }
        }
        if (shouldChangeConfig) {
    ...
                cb.onConfigurationChanged(configToReport);
    ...
        }
    }


调用重启:


|-- frameworks/base/services/core/java/com/android/server/am/ActivityStack.java


boolean ensureActivityConfigurationLocked(
            ActivityRecord r, int globalChanges, boolean preserveWindow) {
        if (mConfigWillChange) {
            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                    "Skipping config check (will change): " + r);
            return true;
        }
        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                "Ensuring correct configuration: " + r);
        // Short circuit: if the two configurations are equal (the common case), then there is
        // nothing to do.
        final Configuration newConfig = mService.mConfiguration;
        r.task.sanitizeOverrideConfiguration(newConfig);
        final Configuration taskConfig = r.task.mOverrideConfig;
        if (r.
相关文章
|
2天前
|
移动开发 Java Android开发
构建高效Android应用:采用Kotlin协程优化网络请求
【4月更文挑战第24天】 在移动开发领域,尤其是对于Android平台而言,网络请求是一个不可或缺的功能。然而,随着用户对应用响应速度和稳定性要求的不断提高,传统的异步处理方式如回调地狱和RxJava已逐渐显示出局限性。本文将探讨如何利用Kotlin协程来简化异步代码,提升网络请求的效率和可读性。我们将深入分析协程的原理,并通过一个实际案例展示如何在Android应用中集成和优化网络请求。
|
2天前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第24天】随着移动开发技术的不断演进,提升应用性能和用户体验已成为开发者的核心任务。在Android平台上,Kotlin语言凭借其简洁性和功能性成为主流选择之一。特别是Kotlin的协程功能,它为异步编程提供了一种轻量级的解决方案,使得处理并发任务更加高效和简洁。本文将深入探讨Kotlin协程在Android开发中的应用,通过实际案例分析协程如何优化应用性能,以及如何在项目中实现协程。
|
3天前
|
存储 缓存 安全
Android系统 应用存储路径与权限
Android系统 应用存储路径与权限
6 0
Android系统 应用存储路径与权限
|
3天前
|
存储 安全 Android开发
Android系统 自定义系统和应用权限
Android系统 自定义系统和应用权限
19 0
|
4天前
|
Java Android开发
Android Mediatek 应用层重置USB设备功能
Android Mediatek 应用层重置USB设备功能
11 0
|
4天前
|
Android开发
Android Mediatek USB 核心驱动中增加设备 PID/VID 检查
Android Mediatek USB 核心驱动中增加设备 PID/VID 检查
3 0
|
8天前
|
缓存 移动开发 Android开发
构建高效Android应用:从优化用户体验到提升性能表现
【4月更文挑战第18天】 在移动开发的世界中,打造一个既快速又流畅的Android应用并非易事。本文深入探讨了如何通过一系列创新的技术策略来提升应用性能和用户体验。我们将从用户界面(UI)设计的简约性原则出发,探索响应式布局和Material Design的实践,再深入剖析后台任务处理、内存管理和电池寿命优化的技巧。此外,文中还将讨论最新的Android Jetpack组件如何帮助开发者更高效地构建高质量的应用。此内容不仅适合经验丰富的开发者深化理解,也适合初学者构建起对Android高效开发的基础认识。
9 0
|
8天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
14 0
|
10天前
|
缓存 移动开发 Java
构建高效的Android应用:内存优化策略
【4月更文挑战第16天】 在移动开发领域,尤其是针对资源有限的Android设备,内存优化是提升应用性能和用户体验的关键因素。本文将深入探讨Android应用的内存管理机制,分析常见的内存泄漏问题,并提出一系列实用的内存优化技巧。通过这些策略的实施,开发者可以显著减少应用的内存占用,避免不必要的后台服务,以及提高垃圾回收效率,从而延长设备的电池寿命并确保应用的流畅运行。
|
Android开发
关于Android中的onCreate()多次被调用导致bindService被多次调用的问题
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010046908/article/details/50511776                 最近在做公司项目的时候,遇到了一个很怪的问题,就是Application的onCreate()方法多次被调用,    导致bindService被多次调用,以致于程序出现ANR。
1376 0