Post on 11-Jan-2017
transcript
#mobilization2016
THE 2016 ANDROIDDEVELOPER TOOLBOX
@Nilhcem
#mobilization2016
THE 2016 ANDROIDDEVELOPER TOOLBOX
"Use the right tools for the right job"
(with the Android Plugin for Gradle)
BUILD VARIANTS
DEBUG SCREENExample from Google iosched
INTERNAL SETTINGS APPSimilar to U+2020 Sample app
Display build / device information
Change endpoint (restart process with)
Show logs ( )
Allow easy bug report capturing( )
Enable/Disable Takt / Stetho / Scalpel /Madge...
JakeWharton/ProcessPhoenix
pedrovgs/Lynx
mattprecious/telescope
MOBILIZATION 2016 APPLICATIONhttps://github.com/Nilhcem/mobilization-2016
MEASURING TOOLS
ANDROID STUDIO(Android Monitor tab)
DETECT MEMORY LEAKS
FRAME RATE
build.gradle:
MyApplication.java:
https://github.com/wasabeef/Takt
compile 'jp.wasabeef:takt:1.0.3'
public class MyApplication extends Application {
@Override public void onCreate() {
super.onCreate();
Takt.stock(this).play();
}
}
HUGOhttps://github.com/JakeWharton/hugo
Prefix classes/methods with:
@DebugLog
Result:
MobilizationApp V ⇢ onCreate() V ⇢ initGraph() V ⇠ initGraph [13ms] V ⇢ initLogger() V ⇠ initLogger [1ms] V ⇠ onCreate [73ms]
PIDCAThttps://github.com/JakeWharton/pidcat
$ pidcat com.nilhcem.mobilization
ANDROIDDEVMETRICS
build.gradle:
MyApplication.java:
https://github.com/frogermcs/AndroidDevMetrics
apply plugin: 'com.frogermcs.androiddevmetrics'
public class MyApplication extends Application {
@Override public void onCreate() {
super.onCreate();
AndroidDevMetrics.initWith(this);
}
}
CODE STATIC ANALYSIS TOOLS
Lint
Error Prone (Google)
Infer (Facebook)
SONARQUBEDockerfile:
FROM java:8
MAINTAINER Nilhcem
RUN DEBIAN_FRONTEND=noninteractive apt update
RUN DEBIAN_FRONTEND=noninteractive apt install -y wget unzip
RUN wget -q https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-6.1.zip
RUN unzip -qq sonarqube-6.1.zip -d /opt/
RUN rm sonarqube-6.1.zip
EXPOSE 9000
CMD ["/opt/sonarqube-6.1/bin/linux-x86-64/sonar.sh", "console"]
Then launch:
$ docker build -t nilhcem/sonarqube . $ docker run -p 9000:9000 -d nilhcem/sonarqube
SONARQUBE GRADLE CONFIGURATIONbuild.gradle:
apply plugin: 'org.sonarqube'
sonarqube { properties { def appProject = project(':app') def appProjectBuildDir = appProject.buildDir
property 'sonar.projectKey', 'mobilization' property 'sonar.projectName', 'Mobilization 2016' property 'sonar.projectVersion', appProject.android.defaultConfig.versionName
property 'sonar.host.url', 'http://localhost:9000' property 'sonar.sources', 'src' property 'sonar.sourceEncoding', 'UTF-8' property 'sonar.java.binaries', 'build/intermediates/classes/production/debug' property 'sonar.java.test.binaries', 'build/intermediates/classes/test/production/debug'
property 'sonar.exclusions', 'src/test/java/**' property 'sonar.core.codeCoveragePlugin', 'jacoco' property 'sonar.jacoco.reportPath', "$appProjectBuildDir/jacoco/testProductionDebugUnitTest.exec" property 'sonar.junit.reportsPath', "$appProjectBuildDir/test-results/testProductionDebugUnitTest/productionDebug" } }
Then launch:
./gradlew :app:assembleProductionDebug :app:testProductionDebugUnitTest :app:sonarqube
TESTING TOOLS
MOCK SERVER
NODEJS + EXPRESS/* Setup */ var fs = require('fs'); var express = require('express'); var app = express(); app.set('port', process.env.PORT || 8080); var port = app.get('port');
/* Speakers list */ app.get('/speakers', function(req, res) { res.type('application/json; charset=utf8'); res.status(200).send(fs.readFileSync('data/speakers.json', 'utf8')); });
/* Other web services */ // ...
/* Starting the server */ app.listen(port, function () { console.log('Express server listening on port ' + port); });
NODEJS + EXPRESSSlow service
sleep(2000);
function sleep(durationMillis) { var now = new Date().getTime(); while (new Date().getTime() < now + durationMillis) { // do nothing } } // Please do not take pictures of this slide, // This is not something I am proud of
EXAMPLE
Then, go to:
https://github.com/Nilhcem/mobilization-2016 /tree/master/mockserver
$ npm install
$ npm start
http://localhost:8990/
HOSTS EDITORhttps://play.google.com/store/apps/details?id=com.nilhcem.hostseditor
HTTP DEBUGGING
HTTP DEBUGGING
mitmproxy
Fiddler
Charles proxy
CHARLES PROXYSimulate a slow connection
Repeat queries
Check the responses
Add some breakpoints to: Cancel an HTTP(s) call
Edit a request
Edit a response
ANDROID STATERESTORING
DON'T KEEP ACTIVITIES
FILL RAMhttps://play.google.com/store/apps/details?id=com.tspoon.androidtoolbelt
ANDROID DEVICE MONITOR - STOP PROCESS
ANALYZING TOOLS
DEVELOPER OPTIONS
UIAUTOMATORVIEWER
ANIMATIONSDeveloper options -> Animation scale
ANIMATIONS
Make a screencast (API 19+):
VLC :
$ adb shell screenrecord /sdcard/demo.mp4
$ adb pull /sdcard/demo.mp4
Press the keyboard 'E' key to see
frames one by one
APKTOOL + DEX2JAR + JD-GUI
http://ibotpeaches.github.io/Apktoolhttps://github.com/pxb1988/dex2jar
http://jd.benow.ca
ANDROID STUDIO(Build > Analyze APK)
STETHO
STETHO - UI
STETHO - NETWORK
STETHO DUMPAPP
STETHO - DUMPAPP
SOME MORE DUMPAPP EXAMPLES$ dumpapp accessToken invalidate $ dumpapp accessToken show fce1235425dcdeadbeef8cafebabe42
$ dumpapp clipboard get Hello
$ dumpapp clipboard set "Text to copy" $ dumpapp onTrimMemory $ dumpapp openIntent scheme://open/speaker/3
$ dumpapp gcmToken uHyMKnEQ:APA91bEHZ6afFLQQMzKgSDjp5y_0397usitPqj_Bp02
$ dumpapp geolocData Location[fused 22.5430883,114.1043205 acc=21 et=+21m5s492ms]
$ dumpapp runningServices com.example.LocationService
STETHO - CUSTOM PLUGINhttp://code.tutsplus.com/tutorials/debugging-android-apps-with-facebooks-stetho--cms-24205
class AppDumperPlugin implements DumperPlugin { @Override public String getName() { return "my_plugin_name"; }
@Override public void dump(DumperContext dumpContext) throws DumpException { PrintStream writer = dumperContext.getStdout(); String commandName = (args.isEmpty()) ? "" : args.remove(0);
if (commandName.equals("test")) { out.println("Hello, World!"); } } }
STETHO - CUSTOM PLUGIN
STETHO - DUMPAPP (EXAMPLE)
(ActivityProvider in a dependency graph in debug)
@Singleton public class ActivityProvider implements Application.ActivityLifecycleCallbacks {
private Activity currentActivity;
@Inject public ActivityProvider(Application app) { app.registerActivityLifecycleCallbacks(this); }
public Activity getCurrentActivity() { return currentActivity; }
@Override public void onActivityResumed(Activity activity) { currentActivity = activity; }
@Override public void onActivityPaused(Activity activity) { currentActivity = null; } }
STETHO - DUMPAPP (EXAMPLE)
AppDumperPlugin.java
private void displayCurrentSessionData(PrintStream writer) {
Activity activity = activityProvider.getCurrentActivity();
if (activity instanceof SessionDetailsActivity) {
try {
// Use reflection to access private "session" field
Field field = SessionDetailsActivity.class.getDeclaredField("session");
field.setAccessible(true);
Session session = (Session) field.get(activity);
writer.println(new GsonBuilder().setPrettyPrinting().create().toJson(session));
} catch (Exception e) {
writer.println(e.getMessage());
}
}
}
$#*!, I FORGOT THESE TOOLS!
ADB + SHELL
# Open a deep linking intent adb shell am start -a android.intent.action.VIEW -d "scheme://app/deep/linking"
# List running services adb shell dumpsys activity services
# Get the path of an install application adb shell pm path app.package.name
# Clear app data adb shell pm clean app.package.name
# Take a screenshot adb shell screencap -p | perl -pe '\''s/\x0D\x0A/\x0A/g'\'' > screen.png
# Paste text from your computer clipboard to your android device pbpaste | sed "s/%/%%/g" | sed "s/ /\%\s/g" | xargs adb shell input text
LOCKITOhttps://play.google.com/store/apps/details?id=fr.dvilleneuve.lockito
ACCESSIBILITY SCANNERhttps://play.google.com/store/apps/details?id=com.google.android.apps.accessibility.auditor
AS A CONCLUSION...
CHOOSE ACCORDINGTO YOUR NEEDS AND
TASTES
THE 2016 ANDROIDDEVELOPER TOOLBOX
(EXAMPLE OF AN) ANDROID DEVELOPER
TOOLBOX
https://twitter.com/Nilhcem
https://github.com/Nilhcem/mobilization-2016
http://www.slideshare.net/Nilhcem/the-2016-android-developer-toolbox-mobilization
MASTER YOUR TOOLS TO BUILD BETTER APPS