Upload
boris-farber
View
1.721
Download
0
Embed Size (px)
Citation preview
10 ways to improve your app performanceBoris Farber Developer Advocate
+borisfarber @borisfarber www.api-solutions.com
IF YOU HAVE A SMALL APP FORGET THESE SLIDES
Symptoms
● Long start time● Slow app● Janky scrolling● Irresponsive app
Activity Leaks
Why memory leaks are dangerous
● Holding references to unused Activity○ handlers + static variables
● Activity holds its view
● Activities/fragments etc - they have a life cycle● Static references
○ become dangling "pointers"○ staticActivity = staticFragment.getActivity()○ hold chain of references that lead to Activity <==
MEMORY LEAK
Outer class (Activity)
Inner class (Handler)
This is leakpublic class LeakActivity extends Activity {// ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); NastyManager.getInstance().addListener(this);// ...
This is leak + fix@Overridepublic void onDestroy() { super.onDestroy();
NastyManager.getInstance().removeListener(this);}
remove listener
This is leakpublic class MainActivity extends Activity {
// ...
Handler handler; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
} };
// ...
What happens if ...
handler.postDelayed(...)
This is leak + fix
private static class MyHandler extends Handler {
private final WeakReference<MainActivity> mActivity;
// ...
public MyHandler(MainActivity activity) {
mActivity = new WeakReference<MainActivity>(activity);
// ...
}
@Override
public void handleMessage(Message msg) {
}
// ...
}
Outer class (Activity)
Inner class (Handler)
Prefer static to non static inner classes
● Non static Handler --> Activity leak● Both classes have a different lifetime
3 ways to handle
● Use event bus● Unregister listeners● Prefer static inner classes to non static
What you can do● Do code reviews● Understand your app structure● Use tools (MAT ...)● Print logs on callbacks
Activity LeaksQuestions ?
Scrolling
Use UI Thread only for UI
● JSON parsing● Memory (images)● Networking● Database access
What you can do to speed up scrolling
● Memory access - use library (caching, loading)● Networking access -
○ use library○ revisit your concurrency model
● Database access - use loaders
● JSON - use library DO NOT DO ANYTHING ON UI THREAD
JSON
● Small JSONs - GSON is the best● Large JSONs - Jackson, ig-json-parser
Large JSON
● Parsing has a performance effect● When converted to class with getters and
setters
UI thread
Looper.myLooper() == Looper.getMainLooper()
UI thread
● Never block it● Many java.net APIs are blocking
○ Streams○ Equals of URL class result in DNS call○ HttpURLConnection
Don't over sync
● When user doesn't need it● Use push notifications
Scrolling Questions ?
Concurrency APIs 1
Service
● Service methods run on UI thread ! ● Consider
○ IntentService○ AsyncTask○ Executors (separate bullet)○ HandlerThreads, Handlers and Loopers (separate
bullet)● Libraries do it for you
IntentService
● Single threaded● Simple/One job in a time● No job system (keep track for jobs)● No way to stop to manage it
AsyncTask
● Consider Loaders (part of support library)● Don't care about result outside of UI● Activity lifecycles - can cause a memory leak
(rotation)● Changes rapidly (grab latest and add to your
project)
Concurrency APIs 1 Questions ?
Deprecation
Deprecation
● API will be removed● Your app will not work● No way to update APIs and tools
Deprecation
● There is a compelling reason to move○ Security○ Correctness○ Performance
What you can do around deprecation
● Know and use APIs● Refactor your dependencies
Newer is better
● Prefer Toolbar to ActionBar● Prefer RecyclerView (especially for
animations)
Don't use Apache Http Connection
● Removed at M (still available as dependency)
● Use HttpURLConnection○ Simple APIa○ Small size○ Transparent compression○ Response caching
DeprecationQuestions ?
JNI
Best
● Use JNI as part of 3rd party lib
Media
● Explore advanced CPU features● Memory ● Audio● Video
Security
● Own wrapped library (SSL/crypto etc')● Rooted devices● Your own sensitive data/protocols
What you can do
● Calls are expensive● Keep the boundaries simple● Keep native code to minimum● Keep native code isolated
○ All native methods in same class or package○ Porting layer
● Test, test, test ...
JNIquestions?
Architecture
Understand app components life cycle
● Activities● Fragments● Tasks● Flags
● Add logs on callbacks
Work with framework not against ...
● Framework components have a purpose○ Specific semantics○ Used when those semantics are desired
● Don't over engineer● Keep simple
Your architecture
● Consistent● Get people on board quickly● Have someone experienced
Design your app for
● Sleeping most of the time● Responsive when "awaken"
Architecturequestions?
Concurrency APIs 2
ExecutorService
Executor Framework
● Thread pool● Callbacks● Futures
Dispatch downloadsExecutorService executor = Executors.newFixedThreadPool(1);// ...public void performAsyncCall(Handler handler) throws Exception{ // Fire a request. Future<Response> response = executor.submit(new Request (new URL("https://www.google.co.uk/")));
// Do your tasks here // ...
Dispatch downloads// ...
// block current thread
InputStream body = response.get().getBody();
// System.out.print(getStringFromInputStream(body));
// post result to handler
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putString("text", "New data");
message.setData(bundle);
handler.sendMessage(message);
}
// ...
executor.shutdown();
Parse resultspublic static class Request implements Callable<Response> {
private URL url;
public Request(URL url) {
this.url = url;
System.out.println("On async http dispatcher thread " + Thread.currentThread().getId());
}
@Override
public Response call() throws Exception {
System.out.println("On worker thread from pool " + Thread.currentThread().getId());
return new Response(url.openStream());
}
}
Parse resultspublic static class Response {
private InputStream body;
public Response(InputStream body) {
this.body = body;
}
public InputStream getBody() {
System.out.println("On async http dispatcher thread " + Thread.currentThread().getId());
return body;
}
}
Executor Framework
● Excellent for mapreduce jobs
Handlers and Loopers
HandlerThread
● A thread with a message box● Saves a lot of boilerplate code● Uses Looper
Loopers and Handlers
● Looper○ Synchronized message queue to process messages
from handlers ○ Takes the next task, executes it, then takes the next
one and so on● Handler
○ Set of methods to post messages
Parse results// Has Handler in own thread, will batch update the main Activity
private class MyLooper extends Thread {
@Override
public void run() {
// Prepare the current thread to loop for events
Looper.prepare();
dispatcherThreadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
...
}
};
Looper.loop();
}
Push handler to any other thread
Inside Looper (Android src)private Looper(boolean quitAllowed) {
// ...
mThread = Thread.currentThread();
}
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
// ...
for (; ; ) { // ...
}
Concurrency APIs 2Questions ?
Miscellany
Pick 3rd party lib checklist
● Solves your problem● Plays nicely with your current dependencies● Dex method count● Dependencies● Maintenance● Runtime permissions
System Abuse
● Don't call private APIs by reflection● Don't call private native methods (NDK/C
level)● Don't use Runtime.exec● "adb shell am" to communicate with other
process is not something we want to support
Avoid complex views
● Visual clutter● Harder to maintain and draw● Use HierarchyViewer
Use TimingLogging for measuring
Best of luck in your journey wherever Android takes you
Thank you !Boris FarberDeveloper Advocate