Окончание Android-интервью (с ответами)

Android
Окончание Android-интервью (с ответами)

Интервью — это не что иное, как задавание вышеперечисленных вопросов (их довольно много — —!), и при найме Android-разработчиков среднего и старшего звена они зададут их подробно, и они зададут один и два. Вот несколько ключевых моментов, которые я выдвину в первую очередь, это основные вопросы, которые должен задать интервьюер. Пожалуйста, обязательно поймите!

  • Базовые знания — четыре компонента (жизненный цикл, сценарии использования, с чего начать)

  • основы java - структуры данных, потоки, mvc framework

  • Связь — сетевое подключение (HttpClient, HttpUrlConnection), Socket

  • Сохранение данных — SQLite, SharedPreferences, ContentProvider

  • Оптимизация производительности — оптимизация компоновки, оптимизация памяти, оптимизация батареи

  • Безопасность — шифрование данных, обфускация кода, вызовы WebView/Js, https

  • Пользовательский интерфейс — анимация

  • Другие — JNI, AIDL, Handler, Intent и т. д.

  • Фреймворки с открытым исходным кодом — Volley, Gilde, RxJava и т. д.

  • Расширение — функции Android6.0/7.0/8.0/9.0, язык kotlin, конференция ввода-вывода

Если вы торопитесь подавать резюме и спешить на собеседования, лучше остановиться на день-два и еще раз пройти вышеописанный контент. Если вы хотите безопасно получить предложение, лучше всего понимать принцип реализации и знать сценарии использования. Не возвращайся! Чтобы понять! Интервьюер очень устал слушать этот контент в течение дня, лучше всего высказать несколько собственных мнений.

Отличие ссылочных типов в Java, конкретные сценарии использования

В Java существует четыре типа ссылочных типов: сильные ссылки, мягкие ссылки, слабые ссылки и виртуальные ссылки.

Сильная ссылка: сильная ссылка относится к ссылке, созданной новым объектом, и сборщик мусора не будет восстанавливать объект, на который указывает сильная ссылка, даже если памяти недостаточно.

Мягкая ссылка: Мягкая ссылка реализована через SoftRefrence, и ее жизненный цикл короче, чем у строгой ссылки.До того, как памяти не хватит и будет выброшено OOM, сборщик мусора переработает объект, на который ссылается мягкая ссылка. Обычный вариант использования мягких ссылок — хранение некоторого кэша, чувствительного к памяти, который будет высвобождаться при нехватке памяти.

Слабая ссылка: Слабая ссылка реализуется через WeakRefrence, ее жизненный цикл короче, чем у мягкой ссылки, и GC будет перезапускаться до тех пор, пока сканируется объект со слабой ссылкой. Распространенным вариантом использования слабых ссылок является хранение некоторых кэшей, чувствительных к памяти.

Виртуальная ссылка: Виртуальная ссылка реализована через FanttomRefrence, которая имеет самый короткий жизненный цикл и может быть переработана в любое время. Если на объект ссылаются только фантомные ссылки, мы не можем получить доступ к каким-либо свойствам и методам этого объекта через фантомные ссылки. Его роль заключается только в том, чтобы гарантировать, что объект что-то сделает после финализации. Распространенным вариантом использования фантомных ссылок является отслеживание активности объектов, подвергаемых сборке мусора, и получение системного уведомления, когда объект, связанный с фантомной ссылкой, собирается сборщиком мусора.

Разница между исключением и ошибкой

И Exception, и Error наследуются от Throwable.В Java могут быть брошены или перехвачены только объекты типа Throwable, что является основным типом компонента механизма обработки исключений.

Исключение и ошибка отражают классификацию Java различных нештатных ситуаций. Исключение – это непредвиденная ситуация, которую можно ожидать при нормальной работе программы, которую можно и нужно отловить и соответствующим образом обработать.

Ошибка относится к ситуации, которая маловероятна при нормальных обстоятельствах.Большинство ошибок переводят программу в ненормальное и неисправимое состояние. Так как это ненормально, это неудобно и не нужно перехватывать.Распространенный OutOfMemoryError является подклассом Error.

Исключение делится на проверенное исключение и непроверенное исключение. Проверяемые исключения должны быть явно перехвачены в коде как часть проверки компилятором. Unchecked Exception также является исключением времени выполнения, таким как исключение нулевого указателя, выход за границы массива и т. Д., Которые обычно являются логическими ошибками, которых можно избежать.Определяется, нужно ли его перехватывать в соответствии с требованиями, и это не требуется компилятором.

volatile

Вообще говоря о volatile, мы должны упомянуть понятия, связанные с моделью памяти. Все мы знаем, что при выполнении программы каждая инструкция выполняется процессором, и выполнение инструкции обязательно включает чтение и запись данных. Данные при выполнении программы хранятся в основной памяти, поэтому возникнет проблема.Поскольку скорость выполнения ЦП намного выше, чем скорость чтения и записи основной памяти, чтение и запись данных непосредственно из оперативная память снизит эффективность процессора. Чтобы решить эту проблему, существует концепция кеша, в каждом ЦП есть кеш, он будет заранее считывать данные из основной памяти и сбрасывать их в основную память в соответствующее время после работы ЦП.

С этим режимом работы в одном потоке проблем нет, но в многопоточности это вызовет проблемы когерентности кэша. Простой пример: i=i+1, выполните этот код в два потока, предполагая, что начальное значение i равно 0. Мы ожидаем, что два потока получат 2 после запуска, тогда возникает ситуация, когда оба потока читают i из основной памяти в свои соответствующие кэши, а i в обоих потоках равно 0. После выполнения потока 1 получается i=1, а после его обновления в основную память начинает выполняться поток 2. Поскольку i в потоке 2 равен 0 в кеше, i обновляется в основную память после выполнения потока 2. все еще это 1.

Таким образом, это приводит к проблеме когерентности кеша для общих переменных.Чтобы решить эту проблему, предлагается протокол когерентности кеша: когда ЦП записывает данные, если он обнаруживает, что работает с общими переменными, он уведомляет другие ЦП. чтобы скопировать их.Эта внутренняя общая переменная установлена ​​в недопустимое состояние.Когда другие процессоры читают общую переменную в кэше и обнаруживают, что переменная недействительна, он считывает последнее значение из основной памяти.

В многопоточной разработке Java есть три важных понятия: атомарность, видимость и упорядоченность. Атомарность: одна или несколько операций либо не выполняются, либо выполняются обе. Видимость: изменения общих переменных (переменных-членов или статических переменных в классе) в одном потоке немедленно видны в других потоках. Упорядоченный: порядок, в котором выполняется программа, выполняется в порядке кода. Объявление переменной как volatile фактически гарантирует видимость и упорядоченность. Видимость, как я уже говорил выше, необходима в многопоточной разработке. Надо сказать об этой упорядоченности.Для эффективности выполнения иногда происходит перестановка инструкций.Вывод после перестановки инструкций в одном потоке согласуется с логическим выводом нашего кода. Однако при многопоточности могут возникать проблемы, и volatile может в определенной степени избежать перестановки инструкций.

Принцип volatile заключается в добавлении инструкции префикса блокировки в сгенерированный ассемблерный код. Эта инструкция префикса эквивалентна барьеру памяти. Этот барьер памяти выполняет три функции:

  • Убедитесь, что инструкции после барьера не ставятся в очередь перед барьером при переупорядочивании инструкций, и убедитесь, что инструкции перед барьером не ставятся в очередь после барьера.

  • Общая переменная в кеше сбрасывается в основную память сразу после изменения.

  • Кэши в других процессорах становятся недействительными при выполнении операции записи.

Вопросы для интервью, связанные с сетью

HTTP-код состояния

Разница между http и https? Как работает https?

http — это протокол передачи гипертекста, а https можно просто понимать как безопасный протокол http. HTTPS обеспечивает безопасность, добавляя уровень протокола ssl к протоколу http для шифрования данных. Есть две основные функции https: установление защищенного канала передачи информации для обеспечения безопасности передачи данных, подтверждение подлинности веб-сайта.

Основные различия между http и https заключаются в следующем:

  • https необходимо подать заявку на сертификат от ЦС, который редко бывает бесплатным, поэтому требует определенной платы

  • http — это передача открытого текста с низким уровнем безопасности, в то время как https шифруется ssl на основе http с высоким уровнем безопасности

  • Порты по умолчанию у них разные: порт по умолчанию, используемый http — 80, порт по умолчанию, используемый https — 443.

Рабочий процесс https

Когда дело доходит до https, мы должны сначала поговорить об алгоритмах шифрования, Существует два типа алгоритмов шифрования: симметричное шифрование и асимметричное шифрование.

Симметричное шифрование: для шифрования и дешифрования используется один и тот же ключ.Преимуществом является высокая скорость, но недостатком является низкая безопасность. Общие алгоритмы симметричного шифрования включают DES, AES и так далее.

Асимметричное шифрование. Асимметричное шифрование имеет пару ключей, которая делится на открытый ключ и закрытый ключ. Вообще говоря, закрытый ключ хранится сам по себе, а открытый ключ может быть раскрыт другой стороне.Преимущество заключается в том, что безопасность выше, чем у симметричного шифрования, а недостаток в том, что эффективность передачи данных ниже, чем у симметричного шифрования. симметричного шифрования. Информация, зашифрованная открытым ключом, может быть расшифрована только соответствующим закрытым ключом. Обычное асимметричное шифрование включает в себя RSA и так далее.

В формальных сценариях использования симметричное шифрование и асимметричное шифрование обычно используются в комбинации.Асимметричное шифрование используется для завершения передачи секретного ключа, а затем симметричный секретный ключ используется для шифрования и дешифрования данных. Их сочетание не только обеспечивает безопасность, но и повышает эффективность передачи данных.

Конкретный процесс https выглядит следующим образом:

  1. Клиент (обычно браузер) сначала делает запрос на зашифрованную связь с сервером.
  • Поддерживаемые версии протокола, такие как TLS версии 1.0.

  • Генерируемое клиентом случайное число random1, которое позже используется для генерации «ключа разговора».

  • Поддерживаемые методы шифрования, такие как шифрование с открытым ключом RSA.

  • Поддерживаемые методы сжатия

  1. Сервер получает запрос и затем отвечает
  • Подтвердите используемую версию протокола зашифрованной связи, например версию TLS 1.0. Если браузер не соответствует версии, поддерживаемой сервером, сервер отключает шифрованную связь.

  • Генерируемое сервером случайное число random2, которое позже используется для генерации «сеансового ключа».

  • Подтвердите метод шифрования, такого как шифрование открытого ключа RSA

  • сертификат сервера

  1. После того, как клиент получит сертификат, он сначала проверит
  • Сначала проверьте безопасность сертификата

  • После того, как проверка будет пройдена, клиент сгенерирует предварительно мастер-секрет случайным числом, затем зашифрует его с помощью открытого ключа в сертификате, а затем передаст на сервер.

  1. Сервер получает контент, зашифрованный открытым ключом, получает предварительно мастер-секрет случайного числа после его расшифровки закрытым ключом на стороне сервера, а затем получает симметричный зашифрованный секретный ключ по определенному алгоритму в соответствии с radom1, radom2, и предварительный главный секрет, поскольку симметричные ключи используются в последующих взаимодействиях. В то же время клиент также сгенерирует симметричный ключ, используя тот же алгоритм, что и radom1, radom2, pre-master secret и тот же алгоритм.

  2. Затем при последующих взаимодействиях симметричный ключ, сгенерированный на предыдущем шаге, используется для шифрования и дешифрования передаваемого контента.

Процесс трехстороннего рукопожатия TCP

Android вопросы интервью

Каковы методы межпроцессного взаимодействия

AIDL, трансляция, файл, сокет, канал

Разница между статической регистрацией вещания и динамической регистрацией

  1. Широковещательная рассылка динамической регистрации не является резидентной широковещательной рассылкой, что означает, что широковещательная рассылка следует жизненному циклу Действия. Обратите внимание, что до завершения действия удалите широковещательный приемник. Статическая регистрация является резидентной, то есть при закрытии приложения, если идет информационная трансляция, программа также будет автоматически запускаться системным вызовом.

  2. Когда рассылка является упорядоченной: сначала принимается та, у которой более высокий приоритет (независимо от статической и динамической). Широковещательные приемники с одинаковым приоритетом, динамический имеет приоритет над статическим

  3. Похожие широковещательные приемники с одинаковым приоритетом, статические: тот, кто сканирует первым, имеет приоритет над тем, кто сканирует позже, динамические: тот, кто регистрируется первым, имеет приоритет над тем, кто регистрируется позже.

  4. Когда широковещательная рассылка является широковещательной рассылкой по умолчанию: независимо от приоритета динамические широковещательные приемники имеют приоритет над статическими широковещательными приемниками. Один и тот же тип широковещательного приемника с тем же приоритетом, статический: тот, кто сканирует первым, имеет приоритет над тем, кто сканирует позже, динамический: тот, кто регистрируется первым, имеет приоритет над тем, кто регистрируется позже.

Использование инструментов оптимизации производительности Android (рекомендуется согласовать этот вопрос с оптимизацией производительности в Android)

Обычно используемые инструменты оптимизации производительности в Android включают в себя: Android Profiler, LeakCanary, BlockCanary, который поставляется с Android Studio.

Android Profiler, поставляемый с Android, на самом деле очень полезен: Android Profiler может обнаруживать проблемы с производительностью в трех аспектах: ЦП, ПАМЯТЬ и СЕТЬ.

LeakCanary — это сторонняя библиотека для обнаружения утечек памяти.После интеграции нашего проекта LeakCanary будет автоматически обнаруживать утечки памяти во время работы приложения и выводить их нам.

BlockCanary также является сторонней библиотекой для обнаружения зависаний пользовательского интерфейса.После интеграции проекта Block также автоматически обнаружит зависания пользовательского интерфейса во время работы приложения и выведет их нам.

Загрузчик классов в Android

PathClassLoader, может загружать только apks, уже установленные в системе DexClassLoader, может загружать jar/apk/dex, может загружать удаленные apk с SD-карты

Какие бывают виды анимации в Android, в чем их особенности и отличия?

Анимация в Android условно делится на три категории: покадровая анимация, анимация движения (анимация просмотра) и анимация свойств (анимация объектов).

  • Кадровая анимация: настроить группу картинок через xml и воспроизводить их динамически. Редко используемый.

  • Анимация Tween (View Animation): грубо делится на четыре типа операций: вращение, прозрачность, масштабирование и смещение. Редко используемый.

  • Анимация свойств (анимация объектов): анимация свойств является наиболее часто используемым видом анимации, она более мощная, чем анимация движения. Анимация свойств грубо делится на два типа использования, а именно ViewPropertyAnimator и ObjectAnimator. Первый подходит для некоторых общих анимаций, таких как вращение, смещение, масштабирование и прозрачность.Использование также очень просто.Вы можете получить ViewPropertyAnimator через View.animate(), а затем выполнить соответствующую операцию анимации. Последний подходит для добавления анимации в наши пользовательские элементы управления.Конечно, в первую очередь мы должны добавить в пользовательский вид методы getter и setter соответствующих свойств getXXX() и setXXX(). Здесь следует отметить, что в методе установки. После изменения свойств в пользовательском представлении вызовите invalidate(), чтобы обновить рисунок представления. Затем вызовите ObjectAnimator.of свойство type(), чтобы вернуть ObjectAnimator, и вызовите метод start(), чтобы запустить анимацию.

Разница между анимацией движения и анимацией свойства:

  • Твин-анимация заключается в том, что родительский контейнер непрерывно отрисовывает представление, что выглядит как эффект перемещения, но представление не изменилось и остается на месте.

  • Это действительно меняет представление, постоянно меняя значения свойств внутри представления.

Механизм обработчика

Когда дело доходит до Handler, мы должны упомянуть эти тесно связанные классы: Message, MessageQueue, Looper.

  • Сообщение. В Message есть две переменные-члены, заслуживающие внимания: target и callback. На самом деле целью является объект Handler, который отправляет сообщение, а обратный вызов — это задача типа Runnable, которая передается при вызове handler.post(runnable). Суть события публикации заключается в создании сообщения и назначении исполняемого объекта, который мы передали, переменной-члену обратного вызова созданного сообщения.

  • Очередь сообщений. Очередь сообщений, очевидно, является очередью для хранения сообщений, стоит обратить внимание на метод next() в MessageQueue, который возвращает следующее ожидающее сообщение.

  • Петли Совместное сообщение Looper - это фактически ядро ​​соединения между обработчиком и очередью сообщений. Прежде всего, мы все знаем, что если вы хотите создать обработчик в потоке, вы должны сначала создать петров через Looper.Prepare (), а затем вам нужно позвонить Looper.loop (), чтобы начать опрос. Давайте сосредоточимся на этих двух методах.

подготовить(). Этот метод делает две вещи: во-первых, получает Looper в текущем потоке через ThreadLocal.get().Если он не пустой, будет выброшено RunTimeException, что означает, что поток не может создать 2 Loopers. Если он нулевой, перейдите к следующему шагу. Второй шаг — создать Looper и передать ThreadLocal.set(looper). Привяжите Looper, который мы создали, к текущему потоку. Здесь необходимо упомянуть, что создание очереди сообщений фактически происходит в конструкторе Looper.

петля(). Этот метод запускает опрос всего механизма событий. Суть его в том, чтобы открыть бесконечный цикл и непрерывно получать сообщения через метод next() MessageQueue. После получения сообщения для обработки будет вызвана функция msg.target.dispatchMessage(). На самом деле, как мы упоминали, когда говорили о Message, msg.target на самом деле является обработчиком, который отправляет это сообщение. Суть этого кода заключается в вызове метода dispatchMessage() обработчика.

  • обработчик. Сделав столько предзнаменований выше, мы, наконец, добрались до самой важной части. Анализ Handler сосредоточен на двух частях: отправке сообщений и обработке сообщений.

отправлять сообщения. На самом деле помимо sendMessage существуют разные способы отправки сообщений, такие как sendMessageDelayed, post и postDelayed. Но суть их в вызове sendMessageAtTime. enqueueMessage вызывается в методе sendMessageAtTime. В методе enqueueMessage выполняются две вещи: привязка сообщения к текущему обработчику достигается через msg.target = this. Затем постановка сообщения в очередь реализуется через queue.enqueueMessage.

Обработайте сообщение. Ядром обработки сообщений на самом деле является метод dispatchMessage(). Логика в этом методе очень проста, сначала определите, является ли msg.callback нулевым, если нет, выполните runnable. Если он пуст, будет выполнен наш метод handleMessage.

Оптимизация производительности Android

На мой взгляд, оптимизация производительности в Android делится на следующие аспекты: оптимизация памяти, оптимизация макета, оптимизация сети и оптимизация установочного пакета.

Оптимизация памяти: следующий вопрос.

Оптимизация макета: Суть оптимизации макета заключается в снижении уровня просмотра. Общие схемы оптимизации компоновки следующие:

  • В случае, когда и LinearLayout, и RelativeLayout могут завершить компоновку, RelativeLayout предпочтительнее, что может снизить уровень View

  • Извлечение общих компонентов макета с помощью тегов

  • Загружайте необычные макеты через тег

  • Используйте тег , чтобы уменьшить уровень вложенности макета.

Оптимизация сети. Общие схемы оптимизации сети следующие.

  • Минимизируйте сетевые запросы и объединяйте как можно больше

  • Избегайте разрешения DNS. Запрос в соответствии с доменным именем может занять сотни миллисекунд, и может возникнуть риск перехвата DNS. Вы можете добавлять динамические обновления IP-адресов в соответствии с потребностями бизнеса или переключаться на доступ к доменному имени, когда доступ по IP-адресу невозможен.

  • Загрузка большого количества данных с помощью пейджинга

  • Передача данных по сети с использованием сжатия GZIP

  • Добавьте кеш сетевых данных, чтобы избежать частых сетевых запросов

  • При загрузке изображений сжимайте изображения, когда это необходимо

Оптимизация установочного пакета: в основе оптимизации установочного пакета лежит уменьшение размера apk.

  • Использование обфускации может в определенной степени уменьшить размер apk, но фактический эффект минимален.

  • Уменьшите ненужные файлы ресурсов в приложении, такие как изображения, и попробуйте сжать изображения, не влияя на эффект приложения, что имеет определенный эффект.

  • При использовании библиотеки SO предпочтительно резервируется версия библиотеки SO v7, а другие версии библиотеки SO удаляются. Причина в том, что в 2018 году версия библиотеки SO v7 может удовлетворить большинство требований на рынке.Это может быть невозможно для мобильных телефонов восьми-девятилетней давности, но нам нет необходимости адаптироваться к устаревшим мобильные телефоны. В реальной разработке эффект уменьшения размера apk очень значителен.Если вы используете много библиотек SO, например, версия библиотеки SO имеет в общей сложности 10M, то сохраняется только версия v7, а Версии библиотеки SO для armeabi и v8 удалены, а общий объем может быть уменьшен до 20 МБ.

Оптимизация памяти Android

На мой взгляд, оптимизация памяти Android делится на два пункта: предотвращение утечек памяти и расширение памяти, которая на самом деле является открытым исходным кодом и троттлингом.

По сути, суть утечки памяти в том, что объект с более длительным жизненным циклом ссылается на объект с более коротким жизненным циклом.

Распространенные утечки памяти:

  • Утечка памяти, вызванная одноэлементным шаблоном. Наиболее распространенным примером является то, что для создания этого одноэлементного объекта необходимо передать контекст. В это время передается контекст типа действия. Из-за статических свойств одноэлементного объекта его жизненный цикл начинается с загрузки одноэлементный класс в конец приложения. Пока что, даже если входящее действие было завершено, поскольку наш одноэлементный объект все еще содержит ссылку на действие, это вызывает утечку памяти. Решение также очень простое, не используйте Контекст типа Действия, используйте Контекст типа Приложения, чтобы избежать утечек памяти.

  • Утечки памяти, вызванные статическими переменными. Статические переменные размещены в области методов, а их жизненный цикл — от загрузки класса до конца программы, видно, что жизненный цикл статических переменных очень долог. Наиболее распространенным примером утечек памяти, вызванных статическими переменными, является то, что мы создаем статическую переменную в действии, и создание этой статической переменной необходимо передать в ссылке this действия. В этом случае, даже если вызовы Activity завершатся, это вызовет утечку памяти. Причина в том, что жизненный цикл этой статической переменной почти такой же, как жизненный цикл всего приложения, она всегда содержит ссылку на Activity, что приводит к утечке памяти.

  • Утечки памяти, вызванные нестатическими внутренними классами. Причина, по которой нестатические внутренние классы вызывают утечку памяти, заключается в том, что нестатические внутренние классы содержат ссылки на внешние классы.Наиболее распространенным примером является использование Handler и Thread в Activity. Обработчики и потоки, созданные с помощью нестатических внутренних классов, всегда будут содержать ссылку на текущее действие при выполнении отложенной операции.Если действие завершается при выполнении отложенной операции, это приведет к утечке памяти. Есть два решения: первое — использовать статический внутренний класс и использовать слабую ссылку для вызова Activity в статическом внутреннем классе. Второй метод — вызвать handler.removeCallbacksAndMessages в onDestroy действия, чтобы отменить отложенное событие.

  • Утечки памяти, вызванные использованием незакрытых вовремя ресурсов. Типичными примерами являются: работа с различными потоками данных, не закрывающаяся во времени, работа с растровым изображением без своевременного повторного использования и т. д.

  • Использование сторонних библиотек вовремя не удалось отвязать. Некоторые сторонние библиотеки предоставляют функции регистрации и отвязки.Самая распространенная EventBus.Все мы знаем,что используя EventBus,нужно регистрироваться в onCreate и отвязываться в onDestroy. Если он не несвязан, EventBus на самом деле является одноэлементным режимом, он всегда будет содержать ссылку на Activity, что приводит к утечке памяти. Это также распространено в RxJava.После использования оператора Timer для выполнения некоторых операций с задержкой обратите внимание на вызов disposable.dispose() в методе onDestroy для отмены операции.

  • Утечка памяти, вызванная анимацией свойства. Типичным примером является выход из Activity во время выполнения анимации свойств.В это время объект View все еще содержит ссылку на Activity, что приводит к утечке памяти. Решение состоит в том, чтобы вызвать метод отмены анимации в onDestroy, чтобы отменить анимацию свойства.

  • Утечка памяти, вызванная WebView. WebView особенный, даже если вызывается его метод destroy, он все равно вызывает утечку памяти. На самом деле, лучший способ избежать утечек памяти, вызванных WebView, — это сделать Activity, где WebView находится в другом процессе.Когда Activity заканчивается, убить процесс, в котором находится текущий WebView.Я помню, что WebView Али Дингдинга это еще один, который открыт.Процесс также должен использовать этот метод, чтобы избежать утечек памяти.

Расширить память, зачем расширять нашу память? Иногда мы неизбежно используем много сторонних коммерческих SDK в наших реальных разработках. Эти SDK на самом деле бывают хорошими и плохими. SDK крупных производителей могут иметь меньше утечек памяти, но качество SDK некоторых мелких производителей не очень надежное. . Лучший способ справиться с этой ситуацией, которую мы не можем изменить, — это расширить память.

Обычно есть два способа расширить память: один — добавить атрибут largeHeap="true" в Application в файле манифеста, а другой — открыть несколько процессов в одном приложении, чтобы увеличить общий объем памяти приложения. Второй способ на самом деле очень распространен, например, я использовал SDK Getui, а сервис Getui фактически находится в отдельном процессе.

Оптимизация памяти в Android, как правило, с открытым исходным кодом и регулированием. Открытый исходный код предназначен для расширения памяти, а регулирование — для предотвращения утечек памяти.

Механизм связующего

В Linux, чтобы избежать вмешательства одного процесса в другие процессы, процессы независимы друг от друга. В процессе он фактически разделен на пространство пользователя и пространство ядра. Изоляция здесь делится на две части: изоляцию между процессами и изоляцию внутри процесса.

Поскольку между процессами существует изоляция, на самом деле существует взаимодействие. Взаимодействие между процессами — это IPC, а взаимодействие между пространством пользователя и пространством ядра — это системные вызовы.

Для обеспечения независимости и безопасности в Linux процессы не могут напрямую обращаться друг к другу.Android основан на Linux, поэтому также необходимо решить проблему межпроцессного взаимодействия.

На самом деле существует множество способов взаимодействия между процессами Linux, например каналы, сокеты и т. д. Почему межпроцессное взаимодействие Android использует Binder вместо существующих методов Linux, в основном из-за двух соображений: производительность и безопасность

представление. Требования к производительности на мобильных устройствах более строгие. Традиционное взаимодействие между процессами в Linux, такое как каналы, сокеты и т. д., требует двойного копирования данных, в то время как Binder нужно использовать только один раз. Таким образом, Binder лучше, чем традиционная коммуникация процессов, по производительности.

Безопасность. Традиционное взаимодействие процессов в Linux не включает аутентификацию обеих сторон, что может привести к некоторым проблемам с безопасностью. Механизм Binder имеет собственную аутентификацию, которая эффективно повышает безопасность.

Binder основан на архитектуре CS и состоит из четырех основных компонентов.

  • Клиент. клиентский процесс.

  • Сервер. Процесс сервера.

  • Сервис-менеджер. Предоставляет функции для регистрации, запроса и возврата объектов службы прокси.

  • Биндер за рулем. В основном отвечает за установление соединений Binder между процессами, взаимодействие данных между процессами и другие базовые операции.

Основной процесс механизма Биндера выглядит следующим образом:

  • Сервер регистрирует наш сервис в ServiceManager через драйвер Binder.

  • Клиент запрашивает службы, зарегистрированные в ServiceManager, через драйвер Binder.

  • ServiceManager возвращает прокси-объект сервера через драйвер Binder.

  • После того, как клиент получает прокси-объект сервера, может осуществляться межпроцессное взаимодействие.

Принцип LruCache

Основным принципом LruCache является эффективное использование LinkedHashMap, и внутри него есть переменная-член LinkedHashMap. Внимания достойны четыре метода: конструктор, получить, поставить, обрезать по размеру.

Метод построения: в методе построения LruCache выполняются две вещи: устанавливается maxSize и создается LinkedHashMap. Здесь стоит отметить, что LruCache устанавливает для accessOrder LinkedHashMap значение true, а accessOrder — это выходной порядок обхода этого LinkedHashMap. true означает вывод в порядке доступа, false означает вывод в порядке добавления, поскольку обычно он выводится в порядке добавления, поэтому атрибут accessOrder по умолчанию равен false, но наш LruCache должен выводить в порядке доступа, поэтому явно установите для accessOrder значение правда .

метод get: по сути, он вызывает метод get LinkedHashMap. Поскольку мы устанавливаем для accessOrder значение true, каждый раз, когда вызывается метод get, текущий элемент, к которому мы обращаемся, будет помещен в конец этого LinkedHashMap.

метод put: по сути, он также вызывает метод put LinkedHashMap.Из-за характеристик LinkedHashMap каждый раз, когда вызывается метод put, вновь добавленный элемент будет помещен в конец LinkedHashMap. После добавления будет вызван метод trimToSize, чтобы убедиться, что добавленная память не превышает maxSize.

Метод trimToSize: Внутри метода trimToSize фактически запускается бесконечный цикл while(true), который непрерывно удаляет элементы из заголовка LinkedHashMap до тех пор, пока объем памяти после удаления не станет меньше maxSize, и использует break для выхода из цикла.

Собственно, здесь мы можем резюмировать, почему этот алгоритм называетсяНаименее недавно использованныйЧто с алгоритмом? Принцип очень прост, каждый раз, когда мы помещаем или получаем, можно рассматривать как доступ, из-за характеристик LinkedHashMap элементы, к которым осуществляется доступ каждый раз, будут помещены в конец. Когда наша память достигает порога, будет запущен метод trimToSize для удаления элементов заголовка LinkedHashMap до тех пор, пока текущая память не станет меньше maxSize. Причина удаления первого элемента очевидна: элементы, которые мы часто посещаем в последнее время, будут помещены в конец, а первый элемент должен быть удален.Наименее недавно использованныйЭлементы , поэтому эти элементы должны быть удалены в первую очередь при нехватке памяти.

Принцип DiskLruCache

Разработка фреймворка для асинхронной загрузки изображений

При проектировании фреймворка загрузки картинок необходимо использовать идею кэша L3 для загрузки картинок. Кэш третьего уровня делится на кеш памяти, локальный кеш и сетевой кеш.

Кэш памяти: Кэш Bitmap в памяти, который работает быстро, но имеет небольшой объем памяти. Локальный кэш: кэшируйте изображение в файл, который работает медленнее, но имеет большую емкость. Сетевой кэш: Получайте изображения из сети, и скорость зависит от сети.

Если мы разработаем фреймворк для загрузки изображений, процесс должен быть таким:

  • После получения URL-адреса изображения сначала найдите растровое изображение в памяти и загрузите его напрямую, если оно найдено.

  • Если он не найден в памяти, он будет найден в локальном кеше, и если локальный кеш будет найден, он будет загружен напрямую.

  • Ни память ни локалка не найдены.В это время будет скачиваться образ из сети.После скачивания образ будет загружен,и загруженный образ будет помещен в кеш памяти и локальный кеш.

Выше приведены некоторые основные понятия, если для достижения конкретного кода, вероятно, потребуется файл таких аспектов:

  • Сначала нам нужно определить кэш памяти, там обычно используется LruCache.

  • Для определения локального кеша обычно используется DiskLruCache.Здесь следует отметить, что имя файла кеша изображения, как правило, представляет собой строку, URL-адрес которой зашифрован MD5, чтобы избежать прямого раскрытия имени файла URL-адреса изображения.

  • После того, как кеш памяти и локальный кеш определены, нам нужно создать новый класс MemeryAndDiskCache, разумеется, имя произвольное, этот класс содержит упомянутые ранее LruCache и DiskLruCache. В классе MemoryAndDiskCache мы определяем два метода, один — getBitmap, а другой — putBitmap, что соответствует получению и кэшированию изображений, и внутренняя логика также очень проста. В getBitmap битмап извлекается в соответствии с приоритетом памяти и локального, в putBitmap память сначала кэшируется, а затем кэшируется локально.

  • После определения класса стратегии кэширования мы создаем класс ImageLoader, который должен содержать два метода: один для отображения изображения displayImage(url, imageView), а другой — для получения изображения из сети downloadImage(url, imageView) . В методе отображаемого изображения URL-адрес и imageView должны быть связаны через ImageView.setTag(url), чтобы избежать ошибки смещения изображения, вызванной повторным использованием ImageView при загрузке сетевых изображений в список. После этого из MemoryAndDiskCache будет получен кеш, если он есть, то будет загружен напрямую, если нет, то будет вызван метод получения картинок из сети. Есть много способов получить картинки из сети, здесь я обычно использую OkHttp+Retrofit. После получения изображения из сети сначала оцените, соответствует ли imageView.getTag() URL-адресу изображения. Если оно соответствует, загрузите изображение. Если оно несовместимо, не загружайте изображение. Таким образом, исключается неправильное размещение асинхронно загружаемой картинки в списке. При этом, после получения образа, образ будет закеширован через MemoryAndDiskCache.

Механизм отправки событий в Android

Когда наш палец касается экрана, событие фактически достигает представления, которое, наконец, отвечает на наше событие касания через процесс Activity -> ViewGroup -> View.

Когда дело доходит до распределения событий, необходимы следующие методы: dispatchTouchEvent(), onInterceptTouchEvent(), onTouchEvent. Далее мы кратко расскажем о механизме распределения событий по процессу Activity -> ViewGroup -> View.

Когда наш палец коснется экрана, будет запущено событие типа Action_Down, и первым отреагирует действие текущей страницы, то есть оно перейдет к методу dispatchTouchEvent() действия. Внутри этого метода просто следующая логика:

  • Вызовите getWindow.superDispatchTouchEvent().

  • Если предыдущий шаг возвращает true, верните true напрямую; в противном случае верните собственный onTouchEvent(). Эта логика хорошо понятна: если getWindow().superDispatchTouchEvent() возвращает true, значит, текущее событие обработано и нет необходимости вызывать собственное onTouchEvent, иначе — событие не обработано и должен быть обработан самой Activity, то есть вызовом собственного onTouchEvent.

Метод getWindow() возвращает объект типа Window.Как мы все знаем, в Android PhoneWindow является единственным классом реализации Window. Таким образом, это предложение по сути вызывает superDispatchTouchEvent() в PhoneWindow.

В этом методе PhoneWindow фактически вызывается mDecor.superDispatchTouchEvent(event). Этот mDecor — это DecorView, который является подклассом FrameLayout, а super.dispatchTouchEvent() вызывается в superDispatchTouchEvent() в DecorView. Здесь очевидно, что DecorView является подклассом FrameLayout, а FrameLayout — подклассом ViewGroup, который, по сути, вызывает dispatchTouchEvent() ViewGroup.

На данный момент наши события были переданы из Activity в ViewGroup.Далее давайте проанализируем эти методы обработки событий в ViewGroup.

Логика в dispatchTouchEvent() в ViewGroup примерно следующая:

  • Судите, перехватывает ли текущая ViewGroup события через onInterceptTouchEvent(), ViewGroup по умолчанию не перехватывает;

  • В случае перехвата верните собственный onTouchEvent();

  • Если он не перехвачен, он будет оцениваться в соответствии с возвращаемым значением child.dispatchTouchEvent(). Если он возвращает true, он возвращает true; в противном случае он возвращает свой собственный метод onTouchEvent(), реализующий восходящую доставку необработанных событий.

При нормальных обстоятельствах onInterceptTouchEvent() ViewGroup возвращает false, то есть не перехватывает. Здесь следует обратить внимание на последовательность событий, таких как событие «Вниз», событие «Перемещение»… Событие «Вверх», «От Вниз к Вверх» — это полная последовательность событий, соответствующая серии событий от нажатия до поднятия рукоятки. finger, если ViewGroup перехватывает событие Down, то последующие события будут переданы onTouchEvent этой ViewGroup. Если ViewGroup не перехватывает событие Down, она отправит событие Action_Cancel в представление, которое ранее обрабатывало событие Down, чтобы уведомить дочернее представление о том, что последующая последовательность событий была принята ViewGroup, и дочернее представление может восстановить предыдущую. состояние.

Вот общий пример: В часах Recyclerview много кнопок. Мы сначала нажимаем кнопку, затем сдвигаем ее на определенное расстояние и отпускаем. В это время Recyclerview будет скользить, и событие нажатия этой кнопки будет не срабатывать. В этом примере, когда мы нажимаем кнопку, кнопка получает событие Action_Down, и последующая последовательность событий должна обрабатываться этой кнопкой при нормальных обстоятельствах. Но мы пролистнули на расстояние, затем Recyclerview понял, что это операция скольжения, перехватил эту последовательность событий и перешел к своему методу onTouchEvent(), который отражал скольжение списка на экране. В это время кнопка все еще находится в нажатом состоянии, поэтому при перехвате нужно отправить Action_Cancel, чтобы уведомить кнопку о восстановлении предыдущего состояния.

Отправка событий в конечном итоге перейдет к диспетчеру ViewTouchEvent(). В диспетчере ViewTouchEvent() нет onInterceptTouchEvent(), что тоже легко понять.View не является ViewGroup и не будет содержать других под-View, поэтому перехвата или перехвата нет. Игнорируя некоторые детали, диспетчер ViewTouchEvent() напрямую возвращает свой собственный onTouchEvent(). Если onTouchEvent() возвращает true, это означает, что событие обработано, в противном случае необработанное событие будет передано до тех пор, пока View не обработает событие или оно не будет обработано, и, наконец, не достигнет завершения onTouchEvent() Activity.

Здесь часто спрашивают, в чем разница между onTouch и onTouchEvent. Прежде всего, эти два метода находятся в dispatchTouchEvent() представления, что представляет собой такую ​​логику:

  • Если touchListener не равен нулю, а представление включено, а onTouch возвращает значение true, оно вернет значение true непосредственно при выполнении этих трех условий, и метод onTouchEvent() не будет использоваться.

  • Пока одно из вышеперечисленных условий не выполняется, оно переходит к методу onTouchEvent(). Таким образом, порядок onTouch предшествует onTouchEvent.

Посмотреть процесс рисования

Отправной точкой для рисования является метод PerformTraversals() класса ViewRootImpl.В этом методе mView.measure(), mView.layout() и mView.draw() фактически вызываются последовательно.

Процесс рисования View разделен на три этапа: измерение, компоновка и рисование, которые соответствуют трем методам измерения, компоновки и рисования соответственно.

этап измерения. Метод измерения будет вызываться родительским представлением.После выполнения некоторой оптимизации и подготовки в методе измерения будет вызван метод onMeasure для выполнения фактического самоизмерения. Метод onMeasure делает разные вещи в View и ViewGroup:

  • Вид. Просмотр метода расчета onMeasure будет их размер и сохранение setMeasureDimension.

  • Группа просмотра. Метод onMeasure в ViewGroup вызывает все подпредставления методов Measure для самостоятельного измерения и сохранения. Затем ваш собственный размер рассчитывается по размеру и положению подвида и сохраняется.

стадия макета. Метод макета будет вызываться родительским представлением, метод макета сохранит размер и положение, переданные родительским представлением, и вызовет onLayout для фактического внутреннего макета. То, что onLayout делает в View и ViewGroup, также отличается:

  • Вид. Поскольку View не имеет дочерних View, в onLayout View ничего не делается.

  • Группа просмотра. Метод onLayout в ViewGroup вызовет метод макета всех дочерних представлений, передаст им размер и положение и позволит им завершить свой собственный внутренний макет.

этап рисования. Метод draw выполнит некоторую работу по планированию, а затем вызовет метод onDraw для рисования самого представления. Процесс планирования метода рисования выглядит примерно следующим образом:

  • Нарисуйте фон. Соответствует методу drawBackground(Canvas).

  • Нарисуйте предмет. Соответствует методу onDraw(Canvas).

  • Нарисуйте дочерний вид. Соответствует методу dispatchDraw(Canvas).

  • Постройте скользящую корреляцию и передний план. Соответствует onDrawForeground(Canvas).

Общие шаблоны проектирования в исходном коде Android и шаблоны проектирования, обычно используемые при разработке

Как взаимодействуют Android и js

В Android взаимодействие между Android и js разделено на два аспекта: Android вызывает методы в js, а js вызывает методы в Android.

Android вызывает js. Есть два способа для Android настроить js:

  • WebView.loadUrl("имя метода в javascript:js"). Преимущество этого метода в том, что он очень прост, но недостаток в том, что нет возвращаемого значения.Если вам нужно получить возвращаемое значение метода js, вам нужно js для вызова метода в Android, чтобы получить возвращаемое значение .

  • WebView.evaluateJavaScript("javascript: имя метода в js", ValueCallback). Этот метод лучше loadUrl тем, что вы можете получить возвращаемое значение метода js через обратный вызов ValueCallback. Недостатком является то, что этот метод доступен только в Android 4.4, а совместимость плохая. Однако в 2018 году для большинства приложений на рынке требуется минимальная версия 4.4, поэтому я не думаю, что эта проблема совместимости является большой проблемой.

js для настройки Android. Есть три способа настроить js на Android:

  • WebView.addJavascriptInterface(). Это официальное решение для js для вызова методов Android.Следует отметить, что аннотация @JavascriptInterface должна быть добавлена ​​к методам Android, вызываемым js, чтобы избежать дыр в безопасности. Недостаток этой схемы в том, что до Android 4.2 будут дыры в безопасности, но после 4.2 это исправили. Опять же, в 2018 году проблем с совместимостью меньше.

  • Перепишите метод shouldOverrideUrlLoading() WebViewClient так, чтобы он перехватывал URL-адрес и анализировал его после получения URL-адреса. Если он соответствует требованиям обеих сторон, можно вызвать метод Android. Преимущество в том, что он избегает лазеек в безопасности до Android 4.2, а недостаток тоже очевиден: невозможно напрямую получить возвращаемое значение вызова метода Android, возвращаемое значение можно получить только вызовом js-метода через Android.

  • Перепишите метод onJsPrompt() WebChromClient так же, как и предыдущий метод. После получения URL-адреса сначала проанализируйте его. Если он соответствует требованиям обеих сторон, вы можете вызвать метод Android. Наконец, если требуется возвращаемое значение, возвращаемое значение Android можно вернуть в js через result.confirm("Возвращаемое значение метода Android"). Преимущество метода в том, что в нем нет лазеек и ограничений совместимости, а также можно легко получить возвращаемое значение метода Android. На самом деле здесь следует отметить, что помимо onJsPrompt в WebChromeClient есть методы onJsAlert и onJsConfirm. Так почему бы не выбрать два других метода? Причина в том, что onJsAlert не имеет возвращаемого значения, а onJsConfirm имеет только два возвращаемых значения, true и false, при этом во фронтенд-разработке метод prompt в основном не вызывается, поэтому используется onJsPrompt.

Принцип термического ремонта

Процесс запуска активности

Принцип разреженного массива

Вообще говоря, SparseArray — это структура данных, используемая для замены HashMap в Android. Чтобы быть точным, он используется для замены HashMap, ключ которого имеет тип Integer, а значение имеет тип Object. Следует отметить, что SparseArray реализует только интерфейс Cloneable, поэтому его нельзя объявить с помощью Map. С точки зрения внутренней структуры, SparseArray состоит из двух массивов, один — mKeys типа int[], который используется для хранения всех ключей, другой — mValues ​​типа Object[], который используется для хранения всех значений. Наиболее распространенным является сравнение SparseArray с HashMap.Поскольку SparseArray состоит из двух массивов, он занимает меньше памяти, чем HashMap. Все мы знаем, что такие операции, как добавление, удаление, изменение и поиск, сначала должны найти соответствующую пару ключ-значение, а SparseArray внутренне адресуется бинарным поиском, который явно менее эффективен, чем временная сложность постоянного уровня HashMap. Когда речь заходит о бинарном поиске, также необходимо упомянуть, что предпосылка бинарного поиска заключается в том, что массив уже отсортирован, да, SparseArray сортируется в порядке возрастания по ключу. Подводя итог, можно сказать, что пространство, занимаемое SparseArray, лучше, чем у HashMap, но его эффективность ниже, чем у HashMap, это типичный time-for-space и подходит для хранения меньшей емкости. С точки зрения источника, я думаю, что методы remove(), put() и gc() SparseArray требуют внимания.

  • Удалить(). Метод remove() для SparseArray не удаляет напрямую, а затем сжимает массив, а устанавливает удаляемое значение как статический атрибут SparseArray DELETE, который на самом деле является объектом Object, и устанавливает для атрибута mGarbage в SparseArray значение true. , для этого свойства удобно вызывать собственный метод gc() для сжатия массива, когда это необходимо, чтобы не тратить место впустую. Это может повысить эффективность.Если ключ, который будет добавлен в будущем, равен ключу, который будет удален, добавляемое значение будет перезаписано DELETE.

  • gc(). Метод gc() в SparseArray не имеет абсолютно никакого отношения к сборщику мусора JVM. Внутри метода gc() на самом деле находится цикл for. Пара ключ-значение, значение которой не равно DELETE, перемещается вперед, чтобы покрыть пару ключ-значение, значение которой равно DELETE, для достижения сжатия массива. В то же время mGarbage установите значение false, чтобы избежать потери памяти.

  • помещать(). Такая логика у метода put: если ключ найден в массиве mKeys через бинарный поиск, то значение может быть напрямую перезаписано. Если он не найден, будет получен индекс ключа, ближайший к ключу, который будет добавлен в массив.Если значение, соответствующее этому индексу, равно DELETE, новое значение может быть напрямую перезаписано DELETE.Здесь движение массива элементов можно избежать, тем самым повышая эффективность. Если значение не DELETE, будет оцениваться mGarbage, если истинно, то будет вызван метод gc() для сжатия массива, после чего будет найден соответствующий индекс, пара ключ-значение после индекса будет перемещена назад, и будет вставлена ​​новая пара ключ-значение.Этот процесс может инициировать расширение массива.

Как избежать OOM при загрузке изображения

Мы знаем, что формула для расчета размера растрового изображения в памяти выглядит следующим образом: Пиксели, занимаемые по длине * Пиксели, занимаемые по ширине * Память, занимаемая каждым пикселем. Есть два способа избежать OOM: пропорционально уменьшить длину и ширину и уменьшить занимаемую каждым пикселем память.

  • Пропорциональное уменьшение длины и ширины. Мы знаем, что Bitmap создается с помощью фабричных методов BitmapFactory, decodeFile(), decodeStream(), decodeByteArray(), decodeResource(). Каждый из этих методов имеет параметр типа Options, который является внутренним классом BitmapFactory и хранит некоторую информацию о BItmap. В опциях есть свойство: inSampleSize. Мы можем уменьшить длину и ширину изображения, изменив inSampleSize, уменьшив тем самым память, занимаемую BItmap. Следует отметить, что этот размер inSampleSize должен быть степенью 2. Если он меньше 1, код заставит inSampleSize быть равным 1.

  • Уменьшить объем памяти, занимаемый пикселями. В параметрах PreferredConfig есть свойство, значение по умолчанию — ARGB_8888, которое представляет размер каждого пикселя. Мы можем уменьшить память вдвое, изменив ее на RGB_565 или ARGB_4444.

загрузка больших изображений

При загрузке большого изображения высокой четкости, такого как Qingming Shanghe Picture, во-первых, невозможно отобразить экран, а учитывая ситуацию с памятью, невозможно загрузить все это в память за один раз. На данный момент требуется частичная загрузка, в Android есть класс, отвечающий за частичную загрузку: BitmapRegionDecoder. Метод использования очень прост, создайте объект через BitmapRegionDecoder.newInstance(), а затем вызовите decodeRegion(Rect rect, BitmapFactory.Options options). Первый параметр rect — это отображаемая область, а второй параметр — параметры внутреннего класса в BitmapFactory.

Анализ исходного кода трехсторонней библиотеки Android

Поскольку анализ исходного кода слишком велик, вот ссылка на мой анализ исходного кода (Nuggets).

OkHttp

Анализ исходного кода OkHttp

Retrofit

Анализ исходного кода модификации 1 Анализ исходного кода модификации 2 Анализ исходного кода модификации 3

RxJava

Анализ исходного кода RxJava

Glide

Анализ исходного кода Glide

EventBus

Анализ исходного кода EventBus

Примерно такой процесс: регистр:

  • Получить объект класса подписчика

  • Используйте отражение, чтобы найти набор методов обработки событий в подписчике.

  • Пройдите набор методов обработки событий, вызовите метод subscribe(subscriber, subscriberMethod) в методе подписки:

  • Если наследование события верно, выполните обход stickEvents этого типа карты и используйте метод isAssignableFrom, чтобы определить, является ли текущее событие родительским классом для события обхода, и если да, отправьте событие

  • Если наследование события ложно, получить событие через stickyEvents.get(eventType) и отправить его

  • Если коллекция типов событий пуста, создайте новую коллекцию, цель этого шага — отложить инициализацию коллекции.

  • После получения коллекции типов событий добавьте новый тип событий в коллекцию.

  • Если коллекция Subscription пуста, создайте новую коллекцию. Цель этого шага — отложить инициализацию коллекции.

  • После получения коллекции Subscription просмотрите коллекцию и добавьте новый объект Subscription в соответствующее расположение, сравнив приоритет обработки событий.

  • Получите обработанный тип события eventType через subscriberMethod

  • Свяжите подписчика-подписчика и метод subscriberMethod для формирования объекта Subscription.

  • Получите коллекцию Subscription с помощью subscribesByEventType.get(eventType)

  • Получите коллекцию типов событий через typesBySubscriber.get(subscriber)

  • Определить, является ли текущий тип события закрепленным

  • Если текущий тип события не является закрепленным (закрепляемое событие), подписка (подписчик, метод подписчика) заканчивается здесь.

  • Если он липкий, оцените свойство наследования событий в EventBus, значение по умолчанию — true.

сообщение:

  • postSticky

  • Добавьте события в коллекцию stickyEvents типа Map.

  • вызвать метод post

  • post

  • Наследование события истинно, найдите все родительские типы текущего события и вызовите метод postSingleEventForEventType для отправки события.

  • Наследование событий ложно, отправляются только события текущего типа события

  • Разделено на четыре ситуации в Posttosubscription

  • POSTING, вызовите invokeSubscriber(subscription, event) для обработки событий, что по сути является отражением method.invoke()

  • ГЛАВНЫЙ, если invokeSubscriber обрабатывается непосредственно в основном потоке, в противном случае обработчик переключается на основной поток, чтобы вызвать invokeSubscriber для обработки событий.

  • ПРЕДПОСЫЛКА, если вы не вызываете invokeSubscriber напрямую в основном потоке для обработки события; в противном случае откройте поток и вызовите invokeSubscriber в потоке для обработки события

  • ASYNC, откройте поток, вызовите invokeSubscriber в потоке для обработки события

  • В postSingleEventForEventType получите коллекцию типов подписки с помощью подпискиByEventType.get(eventClass)

  • Пройдите эту коллекцию и вызовите postToSubscription для отправки событий.

  • Добавить событие в очередь событий текущего потока

  • Постоянно удаляйте события из очереди событий через цикл while и вызывайте метод postSingleEvent для отправки события.

  • В postSingleEvent определите наследование события, по умолчанию true

отменить регистрацию:

  • Удалить все подписки, связанные с подписчиком в subscribesByEventType

  • Удалить все типы, связанные с подписчиками в typesBySubscriber

Структуры данных и алгоритмы

рукописная быстрая сортировка

рукописная сортировка слиянием

Рукописная куча и сортировка кучи

Поговорите о разнице между алгоритмами сортировки (временная сложность и пространственная сложность)

1. Процесс запуска Activity (не отвечайте на жизненный цикл)

blog.CSDN.net/Луо Шэнъянь…

1.2 Режим запуска Activity и сценарии использования

(1) настройки манифеста, (2) флаг startActivityblog.CSDN.net/code Emperor…Расширьте здесь: разница между стеком (First In Last Out) и очередью (First In First Out)

3. Два способа запуска Сервиса

(1) startService(), (2) bindService()woo woo Краткое описание.com/afraid/2 удобство 6 злобных репортов 14 раундов…

4. Способ регистрации трансляции и отличия

(1) Статическая регистрация (минифест), (2) Динамическая регистрацияКраткое описание.com/afraid/EA5 О, 233-е 9…Расширить здесь: Когда использовать динамическую регистрацию

5. Разница между HttpClient и HttpUrlConnection

blog.CSDN.net/Го Линь_блог…Расширьте здесь: какой метод запроса используется в Volley (HttpClient до 2.3, HttpUrlConnection после 2.3)

6. Разница между http и https

blog.CSDN.net/какой день/искусство…Продлить здесь: Принцип реализации https

7. Рукописный алгоритм (выбирать пузыри надо уметь)

woo woo Краткое описание.com/afraid/ah 97 из 3 мер...

8. Процесс поддержания жизни (процесс нежити)

woohoo.brief.com/afraid/63AA Fe3 от 1…Расширьте здесь: каков приоритет процесса (следующая статья, все сказано)сегмент fault.com/ah/119000000…

9. Способ межпроцессного взаимодействия

(1) AIDL, (2) вещание, (3) мессенджер АЙДЛ:woo woo Краткое описание.com/afraid/ah 8 oh 43 Ade 5's… woo woo Краткое описание.com/afraid/0 стереть 211 мест… Messenger : blog.CSDN.net/Старая курица 62356579…Расширьте здесь: Кратко опишите Binder,blog.CSDN.net/Луо Шэнъянь…

10. Загрузите большую картинку

PS: Есть небольшая компания (масштабы липовые, и ее обманули), покажите мне проект напрямую, и позвольте мне объяснить принцип реализации. . Одно из интервью, которое больше всего лишило меня дара речи, я почти надел нижнее белье только для одного вопроса, и я собирался помочь им написать код. .blog.CSDN.net/Старая курица 62356579…

11. Кэш уровня 3 (с ним могут быть связаны все основные кадры изображений)

(1) кеш памяти, (2) локальный кеш, (3) сетевой ОЗУ:blog.CSDN.net/Го Линь_блог…местный:blog.CSDN.net/Го Линь_блог…

12. Фреймворк MVP (необходимо спросить)

blog.CSDN.net/Старая курица 62356579…Расширьте здесь: Пример рукописного mvp, разница между mvc и mvp, преимущества mvp

13. Объясните контекст

blog.CSDN.net/Старая курица 62356579…

14.JNI

Woohoo.Краткое описание.com/Fear/Aba 734's 5 Nos…Расширьте здесь: где JNI используется в проекте, например: основная логика, ключи, логика шифрования.

15. Разница между виртуальной машиной java и виртуальной машиной Dalvik

ву ву

16. В чем разница между спящим потоком и ожиданием

blog.CSDN.net/Лю Чжэнвэнь/…

17. Просмотр, распределение событий ViewGroup

blog.CSDN.net/Го Линь_блог… blog.CSDN.net/Го Линь_блог…

18. Сохранить состояние активности

onSaveInstanceState() blog.CSDN.net/Учиха тоже/Ах…

19. WebView взаимодействует с js (какие API называются)

blog.CSDN.net/cappuccino…

20. Обнаружение утечек памяти, оптимизация производительности памяти

blog.CSDN.net/Го Линь_блог…

21. Оптимизация макета

blog.CSDN.net/Го Линь_блог…

22. Пользовательские виды и анимация

Следующие два объяснения очень подробные.Большинство интервьюеров в этой части не будут задавать очень глубокие вопросы, или они произведут на вас эффект и позволят объяснить принцип. (1)Woohoo.ME loop.com/custom view/…(2)blog.CSDN.net/Янбо Бир/AR…

Суммировать

Какие сложные задачи вы решали на работе, а какие проекты выполняли с чувством выполненного долга (этот вопрос обязательно будет задан, так что обязательно подготовьтесь)

На самом деле, эти проблемы все еще основаны на обычном накоплении.Для меня самое приятное - это развитие проекта KCommon, который значительно повышает эффективность моей разработки.

Ссылаться нау-у-у. Краткое описание.com/afraid/564no 39206…

Подробнее

Помимо программистов, помимо написания хорошего кода, вы должны научиться и этим!

Резюме технической сути, рассказ о том, чем я занимался в первой половине года

Трюк, который научит вас создавать визуальные эффекты со скользящим верхом

Фактический бой проекта NDK - мониторинг удаления помощника по мобильному телефону с высокой имитацией 360

Если вы считаете, что это хорошо, переадресация - это большая поддержка для меня!

Здесь вы найдете не только технологии!

image