*#*#4636#*#* - Просмотр технической информации о телефоне, батарее, wifi, а также статистике использования.
*#*#225#*#* - Информация из календаря.
*#06# - IMEI телефона.
Всем известно что Android - это платформа с открытым кодом. Как скачать исходники из репозитория я уже писал в отдельной статье. Сегодня я хочу описать простой способ с помощью которого можно просматривать исходный код платформы в Eclipse IDE.
Зачем это нужно ? Во-превых, для того чтобы не открывать онлайн документацию в поисках описания классов и интрефейсов из SDK. Во-вторых, это позволит выполнять отладку по коду SDK и видеть что происходит внутри вызываемых методов.
Итак, если исходники ещё не подключены к среде разработки, то при попытке открыть описание любого класса из Android SDK (Open Declaration - F3)
Самый простой способ подключить исходники - это установить плагин "Android Source" для Eclipse. Актуальную ссылку на апдейт сайт можно найти на Google Code страничке проекта. Обратите внимание, что вам нужен именно Android Sources плагин, поскольку там их несколько.
Теперь берём эту ссылку (сейчас она вот такая http://adt-addons.googlecode.com/svn/trunk/source/com.android.ide.eclipse.source.update/) и добавляем плагин. В Eclipse Indigo это можно сделать через Help > Install New Software.
После установки плагина мы с радостью видим исходный код Android SDK. На текущий момент поддерживаются все версии с 1.5 по 4.0.3. Пользуйтесь - это весьма удобно !
adb shell chmod 777 /data/local/tmp adb install BurritoRoot3.apk adb shell /data/local/tmp/BurritoRoot3.bin --root
HttpClient client = new DefaultHttpClient(); HttpGet request = new HttpGet(url.toString()); HttpResponse response = client.execute(request); // Pull content stream from response HttpEntity entity = response.getEntity(); InputStream inputStream = entity.getContent(); ByteArrayOutputStream content = new ByteArrayOutputStream(); // Read response into a buffered stream int readBytes = 0; byte[] buffer = new byte[BUFFER_SIZE]; while ((readBytes = inputStream.read(buffer)) != -1) { content.write(buffer, 0, readBytes); } String plain = new String(content.toByteArray()); JSONArray data = new JSONArray(plain);Отточеная до автоматизма тривиальность не предвещала никаких сложностей. Но увы, как это часто бывает всё не так просто как кажется на первый взгляд.
for (int i = 0; i < response.length(); i++) { JSONObject item = response.getJSONObject(i).getJSONObject("item"); Point point = Point.fromJson(item); ContentValues values = new ContentValues(); values.put(KEY_POINT_ID, object.getId()); values.put(KEY_POINT_NAME, object.getName()); values.put(KEY_POINT_LAT, object.getLat()); values.put(KEY_POINT_LON, object.getLon()); values.put(KEY_POINT_TYPE, object.getType()); values.put(KEY_POINT_UPDATE_TIME, object.getUpdateTime()); db.insert(TABLE_POINT, null, values); }Запускаем - замечательно, всё работает как в сказке с первой попытки. Только вот как-то долго... Прошло минуты две, а обработаны меньше четверти данных... Вряд-ли у пользователей хватит терпения ждать 10 минут, пока загрузится приложение. Да и как-то не гуманно это что-ли, так что начинаем анализировать на что тратится так много времени. Для этого под Android есть специальная тулза traceview и Debug.startMethodTracing / Debug.stopMethodTracing в помощь. Важный момент - размер .trace - файла по умолчанию 8 Мб, что исчерпывается быстро. Поэтому я установил его побольше и ограничить импорт выборкой одной страницы в 582 записи. Оборачиваем интересующий нас код вот так:
Debug.startMethodTracing("myapp", 20 * 1024 * 1024); // ... сдесь идёт код импорта данных ... Debug.stopMethodTracing();Снова запускаем и по завершению выполнения находим myapp.trace в корне /sdcard на телефоне. Теперь для анализа "сырых" данных из этого файла запускаем:
traceview <путь-к-папке>/myappИз таблички со статистикой видно что 77,5% времени "сжирает" операция вставки данных в таблицу. Затем 17,3% тратится на выборку данных с сервера из которых 14,4% от общего времени - это разборка JSON строк в объекты. Я решил начать оптимизировать с малого, а именно с разборки JSON. Первая мысль, которая мне пришла в голову - это использовать JSON Streaming. JSON Streaming - это аналог XML Pull парсера для JSON. Идея этого подхода заключается в том, что структура документа не хранится целиком в памяти, а вместо этого парсер сообщает о синтаксическом разборе каждого элемента в отдельности. Поскольку этот подход не поддерживается стандартной библиотекой org.json, я воспользовался альтернативой - Jackson JSON. Вот приблизительно как выглядел код после того как я его переписал:
do { HttpClient client = new DefaultHttpClient(); HttpGet request = new HttpGet(url.toString()); HttpResponse response = client.execute(request); // Pull content stream from response HttpEntity entity = response.getEntity(); InputStream inputStream = entity.getContent(); JsonFactory f = new JsonFactory(); JsonParser jp = f.createJsonParser(inputStream); if (jp.nextToken() == JsonToken.VALUE_NULL) { break; } while (jp.nextToken() != JsonToken.END_ARRAY) { Point point = null; while (jp.nextToken() != JsonToken.END_OBJECT) { String fieldname = jp.getCurrentName(); jp.nextToken(); // move to value, or // START_OBJECT/START_ARRAY if ("item".equals(fieldname)) { // contains an object point = pointFromJson(jp); } else { throw new IllegalStateException("Unrecognized field '" + fieldname + "'!"); } } if (point != null) { db.getPointDao().create(point); } } jp.close();А, вот как изменилась статистика: После трёх замеров, получилось что разборка JSON ускорилась всего на 10-15%. Немного, но кроме этого данные не копируются в памяти несколько раз, поскольку пыборка происходит непосредственно из потока с http-ответом. Оптимизируем дальше. Почему JSON ? На самом деле в этой задаче нет особого смысла использовать JSON, поскольку данные "плоские" и не имеют никакой вложенности. Я решил ещё раз переписать вытягивание данных и на этот раз использовать CSV, поскольку он самый экономичный по размеру. А значит и по скорости приложение должно ощутимо выиграть, поскольку меньше данных будет скачиваться по сети. Итак, вместо 4,3Мб в CSV формате получилось 2,7 Мб что меншье на 37%. Код приводить не буду, он достаточно тривиальный. Скажу только что для работы с csv лучше всего использовать готовую библиотеку opencsv. Что же получилось теперь ? Прирост получился невелик - всего 5%-10%, но это потому что у меня слишком быстрый интернет. В реальных условиях работы через 3G импорт данных будет работать значительно быстрее после таких изменений. Ведь всё-таки почти на 40% меньше данных скачивается. Теперь, когда передача данных рабоатает оптимально, можно заняться оптимизацией вставки объектов в базу данных. Когда выполняется много однотипных операций вставки, то имеет смысл обернуть их в одну транзакцию. Таким образом будет во множество раз меньше обращений к файловой системе для физического сохранения данных, что сэкономит огромное количество времени. Это раз. И вторая, менее значительная оптимизация - использование откомпилированых запросов на вставку. Это два. Улучшенный код вставки данных:
// Prepare insert statement. SQLiteStatement insert = db.compileStatement(INSERT_STATEMENT); db.beginTransaction(); try { while (scanner.hasNextLine()) { String line = scanner.nextLine(); String[] columns = line.split(";"); try { if (columns.length == 7) { insert.clearBindings(); // Important ! Order of the fields in the statement // should be the same like in CSV input. for (int i = 0; i < 7; i++) { insert.bindString(i + 1, columns[i]); } insert.execute(); result++; } } catch (Exception e) { Log.e("AppTrack", Log.getStackTraceString(e)); } } db.setTransactionSuccessful(); } finally { db.endTransaction(); }
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/"> <soapenv:Header/> <soapenv:Body> <tem:ApproveSession> <tem:token>?</tem:token> </tem:ApproveSession> </soapenv:Body> </soapenv:Envelope>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/"> <soapenv:Header/> <soapenv:Body> <tem:ApproveSession> <tem:token>token${=context.getProperty("ThreadIndex")+1}</tem:token> </tem:ApproveSession> </soapenv:Body> </soapenv:Envelope>