приветствую уважаемые форумчане! с недавнего времени в стате лайвинтернет идут переходы с android.app при переходе на который выходит «Не удается получить доступ к сайту» переходов как ни странно много,не пойму что это за сайт-поисковик или.
- Я.Метрика — где посмотреть страницы-источники переходов?
- Резкий всплеск прямых заходов. Как бороться?
- Странное продвижение сайта конкурентов
На сайте с 24.03.2010
27 апреля 2017, 08:44
На сайте с 06.07.2014
27 апреля 2017, 08:54
ага вот как, спс за подсказку..
На сайте с 20.08.2010
27 апреля 2017, 10:15
На сайте с 02.05.2005
27 апреля 2017, 18:53
Это не рефспам, а переходы из Android-приложений, в подавляющем большинстве — поискового приложения Google (https://play.google.com/store/apps/details?id=com.google.android.googlequicksearchbox)
Это приложение при переходе из результатов поиска ставит ссылающейся страницей адрес
В статистике LiveInternet для внутренней технической совместимости этот адрес немного меняется на фиктивную ссылку
Такие переходы сами по себе были и раньше, но заносились в строку «Не веб-страница» и не считались поисковыми переходами Google, поэтому одновременно с появлением этого непонятного адреса в источниках немного увеличилась доля поиска Google, в среднем на пару процентных пунктов.
Источник: searchengines.guru
Встраиваем In-App Updates в приложение
Обновление приложений и поддержка актуальной версии у пользователей это очень важный момент в жизненном цикле каждого приложения. От части это зависит от того, что Android постоянно изменяется: какие-то методы и классы добавляются, какие-то становятся устаревшими, и примерно раз в год с выходом новой версии следует обновлять targetSdkVersion и переписывать код, если это требуется. Кроме того, постоянно обновляются сторонние библиотеки, которые нужно также обновить у себя в приложении, добавляются новые функции и особенности в само приложение. И про багфиксы не стоит забывать, нужно постоянно отслеживать баги в приложениях и оперативно исправлять, потому что недовольный пользователь скорее всего испортит статистику приложению негативным отзывом.
Всё это вынуждает постоянно поддерживать приложение. Отсюда вытекает следующий момент: как установить актуальную версию приложения максимальному числу пользователей.
Этот вопрос от части решает сам Google Play. Он присылает уведомления пользователю о том, что доступна новая версия приложения, предлагает включить автоматическое обновление приложений, что обеспечивает самую свежую версию приложения на устройстве. Однако не все пользователи использую эту возможность, многие обновляют от случая к случаю, игнорируют уведомления.
От APK до AAB: КАК РАБОТАЕТ ANDROID | РАЗБОР
В этом случае мы можем взять дело в свои руки и встроить механизм обновления в само приложения.
В прошлом году Google выпустили механизм In-App Updates с целью ещё больше ускорить обновление приложений у пользователей. In-App Updates позволяет показывать пользователю при входе в приложение диалог, где ему будет предложено обновить приложение до последней версии. Этот функционал работает, начиная с версии Android 5.0 (API 21). Кроме того, приложение должно быть опубликовано в Google Play.
In-App Updates поддерживает два способа обновления приложения:
- Flexible. Если пользователь согласится на обновление приложения, загрузка начнётся в фоновом режиме, при этом пользователь сможет продолжить пользоваться приложением. Как только загрузка завершится, в приложение (если оно активно) придёт результат загрузки, после чего мы можем либо предложить перезапустить приложение, либо дождаться, когда пользователь из него выйдет. Если же на момент окончания загрузки приложение находилось в фоновом режиме, то установка новой версии начнётся сразу же. Такой подход удобен в большинстве случаев, поскольку позволяет обновить приложение, не мешая пользователю.
- Immediate. Пользователь не сможет работать в приложении до тех пор, пока не обновится до новой версии. Когда он согласится на обновление, на этом же экране начнётся процесс загрузки и установки, после чего приложение перезапустится. Такой подход нужен в случае, если вышло критическое обновление приложения, либо если старая версия приложения более не может работать корректно. В остальных случаях лучше всего использовать первый вариант.
Рассмотрим первый вариант обновления приложения на примере одного из наших приложений «Страны мира«.
Перед началом работы нужно добавить библиотеку Play Core в приложение. Для этого в файле build.gradle модуля приложения добавим следующую зависимость:
implementation ‘com.google.android.play:core:1.7.2’
Теперь создадим класс UpdateManager, в котором будем проводить работу, связанную с обновлением.
public final class UpdateManager < private static UpdateManager instance; private UpdateManager() < >public static UpdateManager getInstance() < if (instance == null) < instance = new UpdateManager(); >return instance; > >
Перед тем, как показывать диалог, следует проверить наличие новой версии. Для этого воспользуемся классом AppUpdateManager для запроса к Google Play.
private static UpdateManager instance; private AppUpdateManager appUpdateManager; . public void checkForUpdate(Activity activity) < appUpdateManager = AppUpdateManagerFactory.create(activity); TaskappUpdateInfoTask = appUpdateManager.getAppUpdateInfo(); appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> < if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) < >>); >
К экземпляру класса AppUpdateManager привязываем слушатель OnSuccessListener, который будет получать результат запроса. Если новая версия приложения доступна, отправляем интент, отображающий диалог для скачивания новой версии.
public final static int UPDATE_REQUEST_CODE = 8; . public void checkForUpdate(Activity activity) < appUpdateManager = AppUpdateManagerFactory.create(activity); TaskappUpdateInfoTask = appUpdateManager.getAppUpdateInfo(); appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> < if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) < if (!activity.isFinishing()) try startUpdate(appUpdateInfo, activity); > catch (IntentSender.SendIntentException e) if (BuildConfig.DEBUG) Log.e(ERROR_TAG, «SendError: » + e); > > > > >); > private void startUpdate(AppUpdateInfo appUpdateInfo, Activity activity) throws IntentSender.SendIntentException appUpdateManager.startUpdateFlowForResult( appUpdateInfo, AppUpdateType.FLEXIBLE, activity, UPDATE_REQUEST_CODE); >
Если пользователь согласится на обновление, начнётся процесс загрузки в фоновом режиме. Обратите внимание, что с помощью метода startUpdateFlowForResult() мы можем узнать, какое действие выбрал пользователь. Для этого мы передаём в него константу UPDATE_REQUEST_CODE, результат с этим кодом придёт в метод onActivityResult() активности.
Здесь мы можем получить один из следующих результатов:
- RESULT_OK — пользователь согласился на обновление. Если вместо Flexible используется Immediate, то результат может не прийти, поскольку в этот момент Google Play возьмёт работу на себя.
- RESULT_CANCELED — пользователь отклонил или отменил обновление.
- ActivityResult.RESULT_IN_APP_UPDATE_FAILED — произошла какая-то другая ошибка, из-за которой пользователь не смог дать разрешение или отклонить обновление.
Поскольку мы используем Flexible метод обновления, нам нужно узнать, когда завершится загрузка новой версии. Для этого нам нужно добавить слушатель InstallStateUpdatedListener и привязать его к экземпляру AppUpdateManager после того, как начнётся загрузка. О том, что пользователь начал загрузку, мы узнаём, получим результат в методе onActivityResult() активности.
public void registerListener() < if (appUpdateManager == null) return; appUpdateManager.registerListener(listener); >private final InstallStateUpdatedListener listener = state -> < if (state.installStatus() == InstallStatus.DOWNLOADED) < >>;
С помощью этого слушателя мы можем узнать, загружается обновление или уже загружено, и если загружается — получить прогресс выполнения. Таким образом, при желании можно показывать прогресс загрузки прямо в приложении, но в данном случае мы этого делать не будем.
Когда загрузка завершена, отвязываем слушатель и проверяем, активно приложение или нет. Если приложение активно, то показываем сообщение о том, что обновление готово для установки, в противном случае запускаем установку обновления. Делается это с помощью метода completeUpdate() экземпляра AppUpdateManager. Также для этих целей мы добавим слушатель UpdateListener к нашему классу UpdateManager. Если этот слушатель привязан к активности — значит приложение активно, если отвязан — значит находится в фоне.
public final class UpdateManager < public interface UpdateListener < void onShowSnackbar(); >private UpdateListener updateListener; . private final InstallStateUpdatedListener listener = state -> < if (state.installStatus() == InstallStatus.DOWNLOADED) < onUpdateDownloaded(); > >; private void onUpdateDownloaded() < if (appUpdateManager == null) return; appUpdateManager.unregisterListener(listener); if (updateListener != null) < updateListener.onShowSnackbar(); >else < appUpdateManager.completeUpdate(); >> >
Когда обновление будет загружено, пользователь увидит внизу экрана Snackbar с сообщением об этом и кнопкой для перезагрузки приложения. При желании можно Snackbar заменить на что-нибудь ещё.
Сам же Snackbar выглядит очень просто, при нажатии на Restart мы вызываем completeUpdate(), как делали бы это, будучи в фоновом режиме.
Snackbar snackbar = Snackbar.make(mainView.getRootView(), «An update has just been downloaded.», 10000); snackbar.setAction(«RESTART», view -> UpdateManager.getInstance().completeUpdate()); snackbar.setActionTextColor( ContextCompat.getColor(mainView.onGetContext(), R.color.colorAccent)); snackbar.show();
Если пользователь проигнорирует это сообщение, обновление останется неустановленным и будет храниться в памяти. Поэтому при каждом запуске приложения мы будем проверять, имеются ли загруженные обновления, и если да — выводить сообщение. В противном случае, как и раньше, проверяем наличие обновлений в Google Play. Для этого вернёмся к слушателю OnSuccessListener из начала и добавим в него следующее условие.
public void checkForUpdate(Activity activity) < appUpdateManager = AppUpdateManagerFactory.create(activity); TaskappUpdateInfoTask = appUpdateManager.getAppUpdateInfo(); appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> < if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) if (updateListener != null) updateListener.onShowSnackbar(); > > else if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) < if (!activity.isFinishing()) < try < startUpdate(appUpdateInfo, activity); >catch (IntentSender.SendIntentException e) < if (BuildConfig.DEBUG) < Log.e(ERROR_TAG, «SendError: » + e); >> > > >); >
Таким образом сообщение будет выводиться до тех пор, пока пользователь не обновит приложение до последней версии.
Итоговый листинг класса UpdateManager выглядит следующий образом:
public final class UpdateManager < public interface UpdateListener < void onShowSnackbar(); >private UpdateListener updateListener; private static UpdateManager instance; private AppUpdateManager appUpdateManager; public final static int UPDATE_REQUEST_CODE = 8; private final String ERROR_TAG = «UPDATE_ERROR»; private UpdateManager() < >public static UpdateManager getInstance() < if (instance == null) < instance = new UpdateManager(); >return instance; > public void attachUpdateListener(UpdateListener listener) < updateListener = listener; >public void detachUpdateListener() < updateListener = null; >public void checkForUpdate(Activity activity) < appUpdateManager = AppUpdateManagerFactory.create(activity); TaskappUpdateInfoTask = appUpdateManager.getAppUpdateInfo(); appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> < if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) < if (updateListener != null) < updateListener.onShowSnackbar(); >> else if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) < if (!activity.isFinishing()) < try < startUpdate(appUpdateInfo, activity); >catch (IntentSender.SendIntentException e) < if (BuildConfig.DEBUG) < Log.e(ERROR_TAG, «SendError: » + e); >> > > >); > private void startUpdate(AppUpdateInfo appUpdateInfo, Activity activity) throws IntentSender.SendIntentException < if (appUpdateManager == null) return; appUpdateManager.startUpdateFlowForResult( appUpdateInfo, AppUpdateType.FLEXIBLE, activity, UPDATE_REQUEST_CODE); >public void registerListener() < if (appUpdateManager == null) return; appUpdateManager.registerListener(listener); >public void completeUpdate() < if (appUpdateManager == null) return; appUpdateManager.completeUpdate(); >private final InstallStateUpdatedListener listener = state -> < if (state.installStatus() == InstallStatus.DOWNLOADED) < onUpdateDownloaded(); >>; private void onUpdateDownloaded() < if (appUpdateManager == null) return; appUpdateManager.unregisterListener(listener); if (updateListener != null) < updateListener.onShowSnackbar(); >else < appUpdateManager.completeUpdate(); >> >
Итак, мы встроили In-App Updates в приложение, осталось только проверить его работу.
Для этого отлично подойдёт такая особенность Google Play, как внутренний совместный доступ (Internal app-sharing). Он позволяет загружать APK или Android App Bundle на специальную страницу загрузки, к которым будут иметь доступ только тестировщики. Это позволяет быстро проверить приложение, при этом его не обязательно подписывать релизным ключом, можно делиться также и debug-версиями. Тестировщикам отправляется специальная ссылка на скачивание, которая перенаправляет их в Google Play, где им будет предложено установить данную версию приложения.
Для того, чтобы настроить внутренний совместный доступ, нам нужно задать список тестировщиков для этого приложения. Зайдём в консоль разработчика Google Play и откроем нужное нам приложение. В меню «Инструменты разработки» выберем «Внутренний доступ к приложению«, после чего откроется страница для настройки. Здесь нас интересует только «Управление пользователями с правами загрузки«.
Тут мы можем формировать различные списки тестировщиков, включать их и отключать. При нажатии на «Создать список» появится окно, где будет предложено ввести электронную почту каждого из участников тестирования, а также задать имя новому списку. Создадим список и добавим в него себя как тестировщика.
Теперь мы можем перейти на страницу загрузки для внутреннего совместного доступа. Сюда мы может публиковать различные APK для тестирования. Соберём два APK нашего приложения с разными кодами версий (например, 23 и 24) и загрузим их на эту страницу. При загрузке мы также можем задать название данной версии, чтобы было проще их различать.
После того, как APK будут загружены, они отобразятся в списке.
С этого момента мы готовы тестировать эти версии. Зайдём на устройство, с которого будем запускать приложение, и на нём перейдём по ссылке от версии 1.23. Откроется специальная страница, которая предложит перейти в Google Play для установки.
После перехода в Google Play нам будет предложено установить эту версию приложения.
Примечание! Если вместо страницы приложения вы видите сообщение «Настройки для разработчиков отключены», то нужно будет выполнить следующее:
- Зайдите на устройстве в Google Play.
- Откройте Настройки и найдите внизу «Версия Play Маркета».
- Кликайте по этой надписи несколько раз, до тех пор пока не появится уведомление «Вы стали разработчиком«. После этого у вас появятся дополнительные настройки в Google Play.
- Включите появившуюся опцию сверху «Внутренний доступ к приложениям«.
- Теперь при переходе по ссылке у вас будет загружаться страница приложения.
Как только установка завершится, открываем приложение. В нашем приложении под списком меню указывается текущий код и версия приложения, поэтому будем ориентироваться на неё, что обновление прошло успешно. Как видно, сейчас текущая версия 1.23.
Теперь закрываем приложение, снова открываем браузер и переходим уже по ссылке от версии 1.24. Нам аналогично будет предложено перейти в Google Play, только на этот раз мы можем не установить, а обновить или удалить приложение. Важно: не нужно обновлять приложение с этой страницы! Смотрим только, что она есть, и закрываем.
Снова заходим в наше приложение и, если всё сделано верно, отобразится диалог с предложением обновить версию.
Нажимаем на «Обновить«, после чего начинается скачивание обновления. За его прогрессом можно следить в шторке уведомлений.
Как только загрузка завершится, в наш слушатель придёт состояние DOWNLOADED, после чего внизу экрана отобразится сообщение с предложением перезагрузить приложение.
Нажимаем Restart, и здесь в работу включаются сервисы Google Play, которые обновят приложение до новой версии.
После того, как установка завершится, приложение запустится автоматически. Скроллим список вниз и видим, что версия приложения теперь стала 1.24, значит обновление прошло успешно.
Таким образом, благодаря In-App Updates, пользователи смогут более быстро получать новые версии, чем при обновлении непосредственно через Google Play.
Раздел: Кодинг Метки: Android, app, application, coding, google play, in-app update, store
Встраиваем In-App Updates в приложение : 3 комментария
- Алексей 21.10.2020 А как сослаться на UpdateManager например в MainActivity?
Источник: android-tools.ru
Русские Блоги
Полное имя ANR:Application Not Responding, То есть приложение не отвечает.
1.2 Причина
В системе AndroidActivityManagerService (именуемый AMS)сWindowManagerService (сокращенно WMS)Будет определено время отклика приложения. Если приложение не может реагировать на прикосновение к экрану или ввод с клавиатуры в определенное время, или конкретное событие не было обработано, появится сообщение ANR.
Следующие четыре условия могут вызвать возникновение ANR:
- InputDispatching Timeout: Не может реагировать на события касания экрана или ввода с клавиатуры в течение 5 секунд
- BroadcastQueue Timeout : В реализации трансляции переднего плана (BroadcastReceiver) onReceive() Функция не обрабатывается за 10 секунд, а фон 60 секунд.
- Service Timeout : Служба переднего плана находится в пределах 20 секунд, а фоновая служба не выполняется в течение 200 секунд.
- ContentProvider Timeout : Публикация ContentProvider не завершена в течение 10 секунд.
1.3 Избегайте
Старайтесь избегать трудоемких операций в основном потоке (потоке пользовательского интерфейса).
Затем в дочерние потоки помещаются трудоемкие операции.
О многопоточности можно обратиться к: Многопоточность Android: понимание и простое использование, краткое изложение
2. Метод анализа ANR
2.1 Воспроизведение ANR
Вот тест, сделанный Google Pixel xl (система Android 8.0), известный как сын Google, для создания кнопки для перехода к ANRTestActivity , В последнем onCreate() Основной поток спит 20 секунд:
Вход ANRTestActivity После того, как экран на некоторое время стал черным, примерно на семь или восемь секунд, наконец, обнаружилась аномалия ANR.
Google Pixel xl ANR.png
2.2 Метод анализа ANR первый: Журнал
Только что сгенерировав ANR, посмотрите журнал:
Вы можете видеть, что logcat четко записывает время, когда произошла ошибка ANR, а также tid потока и предложение, чтобы резюмировать причину: WaitingInMainSignalCatcherLoop , Возможно означает, что основной поток ожидает исключения.
Последнее предложение The application may be doing too much work on its main thread. Сообщили, что в основном потоке было выполнено слишком много работы.
2.3 Второй метод анализа ANR: traces.txt
В журнале только что есть второе предложение Wrote stack traces to ‘/data/anr/traces.txt’ , Указывая, что исключение ANR было выведено в traces.txt File, используйте команду adb для экспорта этого файла с телефона:
-
CD в adb.exe Каталог, в которомAndroid SDKиз platform-tools Справочник, например:
cd D:AndroidAndroidSdkplatform-tools
В добавление кWindowsВ дополнение к cmd вы также можете использоватьAndroidStudioизTerminalВвести команду adb.
- После перехода в указанный каталог выполните следующую команду adb для экспорта traces.txt файл:
adb pull /data/anr/traces.txt
—— pid 23346 at 2017-11-07 11:33:57 —— —-> Идентификатор процесса и время генерации ANR Строка Cmd: com.sky.myjavatest Build fingerprint: ‘google/marlin/marlin:8.0.0/OPR3.170623.007/4286350:user/release-keys’ ABI: ‘arm64’ Build type: optimized Zygote loaded classes=4681 post zygote classes=106 Intern table: 42675 strong; 137 weak JNI: CheckJNI is on; globals=526 (plus 22 weak) Libraries: /system/lib64/libandroid.so /system/lib64/libcompiler_rt.so /system/lib64/libjavacrypto.so /system/lib64/libjnigraphics.so /system/lib64/libmedia_jni.so /system/lib64/libsoundpool.so /system/lib64/libwebviewchromium_loader.so libjavacore.so libopenjdk.so (9) Куча: 22% свободно, 1478 КБ / 1896 КБ; 21881 объект —-> использование памяти . «main» prio = 5 tid = 1 Sleeping —-> Причина в том, что Sleeping | group=»main» sCount=1 dsCount=0 flags=1 obj=0x733d0670 self=0x74a4abea00 | sysTid=23346 nice=-10 cgrp=default sched=0/0 handle=0x74a91ab9b0 | state=S schedstat=( 391462128 82838177 354 ) utm=33 stm=4 core=3 HZ=100 | stack=0x7fe6fac000-0x7fe6fae000 stackSize=8MB | held mutexes= at java.lang.Thread.sleep(Native method) — sleeping on (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:373) — locked (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:314) at android.os.SystemClock.sleep(SystemClock.java:122) at com.sky.myjavatest.ANRTestActivity.onCreate (ANRTestActivity.java:20) —-> Имя пакета и конкретное количество строк, генерирующих ANR at android.app.Activity.performCreate(Activity.java:6975) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) at android.app.ActivityThread.-wrap11(ActivityThread.java:-1) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6541) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Используйте ctrl + F, чтобы найти имя пакета в файле, чтобы быстро найти соответствующий код.
Связанные проблемы можно увидеть в приведенном выше журнале:
- Идентификатор процесса и имя пакета: pid 23346 com.sky.myjavatest
- Причины ANR: Sleeping
- Конкретное количество строк, вызвавших ANR: ANRTestActivity.java:20 Строка 20 класса
Специальное примечание: при создании нового ANR исходный файл traces.txt будет перезаписан.
2.4 Третий метод анализа ANR: анализ вызовов потоков Java
Команды, предоставляемые JDK, могут помочь анализировать и отлаживать приложения Java. Команды:
jstack
Pid можно получить с помощью команды jps, которая перечислит все процессы виртуальной машины Java, запущенные в текущей системе, например
7266 Test 7267 Jps
Ссылка на конкретный анализ: анализ ANR приложения Android, раздел IV.1
2.5. Метод анализа ANR 4. DDMS анализирует проблемы ANR
- Используйте инструмент DDMS-Update Threads
- Прочтите вывод обновления потоков
Ссылка на конкретный анализ: анализ ANR приложения Android, раздел 4.2
3. Причины и решения для ANR
Приведенный выше пример — это просто ошибка ANR, вызванная простой длительной операцией основного потока. Причин ANR множество:
- Блокировка основного потока или чтение данных основного потока
Решение: избегайте взаимоблокировок и используйте подпотоки для обработки длительных операций или блокирующих задач. Старайтесь избегать поставщика запросов в основном потоке и не злоупотреблять SharePreferenceS
- ЦП полностью загружен, ввод / вывод заблокирован
Решение: операции чтения и записи файлов или операции с базой данных выполняются в дочернем потоке асинхронно.
- Недостаточно места
Решение: AndroidManifest.xml Можно установить в файле android_largeHeap=»true» , Чтобы увеличить объем памяти, используемый приложением. ноЭтот метод не рекомендуется, Принципиально предотвратить утечки памяти, оптимизировать использование памяти — правильный путь.
- Основные компоненты ANR
Также следует избегать трудоемких операций в жизненном цикле основных компонентов. Обратите внимание на onRecieve () BroadcastReciever, фоновую службу и ContentProvider, чтобы не выполнять слишком длинные задачи.
Четыре, анализ исходного кода ANR
Специальное заявление:В статье рассматривается принцип триггера Android ANR и записываетсяService、BroadcastReceiverсContentProviderВызвано ANR. Код этой статьи цитируется ниже и резюмируется исходя из моего простого понимания.
4.1 ServiceВызванныйService Timeout
Service TimeoutРасположен»ActivityManager»AMS.MainHandler в потоке получает SERVICE_TIMEOUT_MSG Вызвано сообщением.
4.1.1 Отправка отложенных сообщений
ServiceВызывается в процессе подключения к процессу system_server realStartServiceLocked , С последующим mAm.mHandler.sendMessageAtTime() Для отправки отложенного сообщения часто определяется задержка, например, передний план.Service20 секунд.ActivityManagerВ потокеAMS.MainHandlerзаметано SERVICE_TIMEOUT_MSG Он будет активирован при отправке сообщения.
AS.realStartServiceLocked
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException < . // Отправка сообщения о задержке (SERVICE_TIMEOUT_MSG) bumpServiceExecutingLocked(r, execInFg, «create»); try < . // Наконец, выполняем метод onCreate () службы app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState); >catch (DeadObjectException e) < mAm.appDiedLocked(app); throw e; >finally < . >>
AS.bumpServiceExecutingLocked
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) < . scheduleServiceTimeoutLocked(r.app); >void scheduleServiceTimeoutLocked(ProcessRecord proc) < if (proc.executingServices.size() == 0 || proc.thread == null) < return; >long now = SystemClock.uptimeMillis(); Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_TIMEOUT_MSG); msg.obj = proc; // Если сообщение SERVICE_TIMEOUT_MSG не удалено по истечении тайм-аута, выполняем процесс тайм-аута службы mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT)); >
4.1.2 Войдите в основной поток целевого процесса для создания службы
Основной поток целевого процесса вызывается через такие слои, как BinderhandleCreateService(CreateServiceData data)。
private void handleCreateService(CreateServiceData data) < . java.lang.ClassLoader cl = packageInfo.getClassLoader(); Service service = (Service) cl.loadClass(data.info.name).newInstance(); . try < // Создаем объект ContextImpl ContextImpl context = ContextImpl.createAppContext(this, packageInfo); context.setOuterContext(service); // Создаем объект приложения Application app = packageInfo.makeApplication(false, mInstrumentation); service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault()); // Вызов сервисного метода onCreate () service.onCreate(); // Отменить отложенное сообщение AMS.MainHandler ActivityManagerNative.getDefault().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); >catch (Exception e) < . >>
Этот метод создаст целевой объект службы, а также часто используемые обратные вызовы.Serviceиз onCreate() Метод, за которым следует serviceDoneExecuting() Вернитесь на system_server, чтобы выполнить сообщение задержки отмены AMS.MainHandler.
4.1.3 Вернитесь на system_server для выполнения сообщения задержки отмены AMS.MainHandler
AS.serviceDoneExecutingLocked
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) < . if (r.executeNesting . >
В этом методе обработка логики службы завершается, и ранее задержанное сообщение удаляется. SERVICE_TIMEOUT_MSG . Если вы не вызовете этот метод до завершения выполнения, он будет выпущен по истечении тайм-аута. SERVICE_TIMEOUT_MSG Чтобы сообщить, что произошло ANR.
4.2 BroadcastReceiverBroadcastQueue Timeout, вызванный
BroadcastReceiver TimeoutРасположен»ActivityManager»BroadcastQueue.BroadcastHandler в полученном потоке BROADCAST_TIMEOUT_MSG Вызвано сообщением.
4.2.1 Функция обработки широковещательной рассылки processNextBroadcast () broadcastTimeoutLocked (false) отправляет задержанные сообщения
Последовательность обработки широковещательной передачи состоит в том, чтобы сначала обработать параллельную широковещательную передачу, а затем обработать текущую упорядоченную широковещательную передачу.
final void processNextBroadcast(boolean fromMsg) < synchronized(mService) < . // Обрабатываем текущую заказанную трансляцию do < r = mOrderedBroadcasts.get(0); // Получаем все получатели трансляции int numReceivers = (r.receivers != null) ? r.receivers.size() : 0; if (mService.mProcessesReady r.dispatchTime >0) < long now = SystemClock.uptimeMillis(); if ((numReceivers >0) (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) < // шаг 1. Отправка сообщения с задержкой, эта функция обрабатывает множество вещей, например тайм-аут обработки широковещательной передачи и завершение широковещательной передачи. broadcastTimeoutLocked(false); . >> if (r.receivers == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) < if (r.resultTo != null) < // 2. Обработка широковещательных сообщений performReceiveLocked(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, false, false, r.userId); r.resultTo = null; >// 3. Отменить сообщение ANR о тайм-ауте широковещательной передачи cancelBroadcastTimeoutLocked(); > > while (r == null); . // Получаем следующую заказанную трансляцию r.receiverTime = SystemClock.uptimeMillis(); if (!mPendingBroadcastTimeoutMessage) < long timeoutTime = r.receiverTime + mTimeoutPeriod; // Установить тайм-аут трансляции setBroadcastTimeoutLocked(timeoutTime); >. > >
Вышеупомянутый шаг 1. Функция broadcastTimeoutLocked (false): запись информации о времени и вызов функции для установки сообщения о задержке отправки.
final void broadcastTimeoutLocked(boolean fromMsg) < . long now = SystemClock.uptimeMillis(); if (fromMsg) < if (mService.mDidDexOpt) < // Delay timeouts until dexopt finishes. mService.mDidDexOpt = false; long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod; setBroadcastTimeoutLocked(timeoutTime); return; >if (!mService.mProcessesReady) < return; >long timeoutTime = r.receiverTime + mTimeoutPeriod; if (timeoutTime > now) < // step 2 setBroadcastTimeoutLocked(timeoutTime); return; >>
Вышеупомянутый шаг 2. Функция setBroadcastTimeoutLocked: установите конкретную операцию тайм-аута широковещательной передачи, то же самое — отправить задержанное сообщение
final void setBroadcastTimeoutLocked(long timeoutTime) < if (! mPendingBroadcastTimeoutMessage) < Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this); mHandler.sendMessageAtTime(msg, timeoutTime); mPendingBroadcastTimeoutMessage = true; >>
4.2.2 Параметр timeoutTime функции setBroadcastTimeoutLocked (long timeoutTime) представляет собой текущее время плюс установленное время ожидания.
long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
mTimeoutPeriod — это 10 секунд очереди переднего плана и 60 секунд фоновой очереди.
public ActivityManagerService(Context systemContext)
4.2.3 В процессе processNextBroadcast () вызовите cancelBroadcastTimeoutLocked после выполнения performReceiveLocked.
cancelBroadcastTimeoutLocked: в функции processNextBroadcast () обработки широковещательных сообщений performReceiveLocked () вызывает cancelBroadcastTimeoutLocked (), когда широковещательное сообщение обрабатывается, чтобы отменить сообщение о тайм-ауте.
final void cancelBroadcastTimeoutLocked() < if (mPendingBroadcastTimeoutMessage) < mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this); mPendingBroadcastTimeoutMessage = false; >>
4.3 Тайм-аут ContentProvider для ContentProvider
ContentProvider Timeout запускается, когда AMS.MainHandler, расположенный в потоке «ActivityManager», получает сообщение CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG.
Обратитесь к четвертому разделу, чтобы понять принцип триггера Android ANR.
Пять, сбор информации Android ANR
Независимо от того, являются ли это четырьмя основными компонентами или процессами, пока происходит ANR, в конечном итоге будет вызван метод AMS.appNotResponding ().
Ссылка: понимание процесса сбора информации Android ANR
Понять принцип триггера Android ANR (https://link.jianshu.com/?t=http://gityuan.com/2016/07/02/android-anr/)
Изучите процесс сбора информации Android ANR (https://link.jianshu.com/?t=http://gityuan.com/2016/12/02/app-not-response/)
Подробное объяснение ANR для оптимизации приложений Android (https://www.jianshu.com/p/6d855e984b99)
Анализ ANR исходного кода Android (https://link.jianshu.com/?t=http://blog.csdn.net/jasonwang18/article/details/60326807)
Если вам это нравится, следите за ним и добро пожаловать!
Источник: russianblogs.com