Overview of Android Infrastructure

Preview:

Citation preview

Overview of Android Infrastructure

Alexey Buzdin

@AlexeyBuzdin

alex.buzdin@gmail.com

LArchaon

Everyone needs an app....

We need an app too!

simple CRUD app

Requirement:

Support as many android devices as you can

(including tablets)

4.04.1–4.2

3.0

2.3

2.22.1

Google data, March 2013

Ain't that good for a developer

Is there is someone who could help us?

nope

Ease our pain

maybe

Layers

Presentation

Application logic

Domain

Presentation

Application logic

Domain

Presentation

Application logic

Domain

Presentation

Application logic

Domain

But Whers the problem?

Missing in Android 2.2

● ActionBar● Support for tablets● Decent ListView support for server communication

Solution?

Compatability libraries

Android Support library

ActionBarSherlock

etc

Support Libraryhttp://developer.android.com/tools/extras/support-library.html

Fragments

Async Stuff

Experimental stuff

Fragments

Example

ActionBar

ActionBarSherlock

http://actionbarsherlock.com/

Refreshing list

Pull to Refresh

https://github.com/chrisbanes/Android-PullToRefresh

Presentation

Application logic

Domain

Presentation

Application logic

Domain

Android Runtime

On Android you develop in Java

... but Android does not run Java Bytecode !

DalvikVM

Dalvik Virtual Machine– Custom VM optimized for mobile devices– Register-based JVM– More efficient and compact– Use memory efficiently – Dalvik Executable Code (.dex)

● 30% fewer instructions● 35% fewer code units● 35% more bytes

– Trace JIT compiler (since 2.2)

36

Android Runtime

Android Java = Java language + Dalvik + Apache Harmony

Android Java API = Java SE – AWT/Swing + Android API

Sun-Java = Java language + JVM + JDK

App lifecycle

Activity

public class MyActivity extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); }

}

Activity

public class MyActivity extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); }

}

Resources

Activity

public class MyActivity extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); }

}

Context● Context of current state of the

application/object

Context● Context of current state of the

application/object● Context is a handle to the system it

provides services like – resolving resources – obtaining access to databases and

preferences

Important

any resource taken from context will leave as long as Context does

Context problempublic class MyActivity extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); String appName = getString(R.string.appName); }

}

Passing Contextpublic class MyStringProvider {

Context context;

public MyStringProvider(Context context) { this.context = context; }

public String getString(){ return context.getString(R.string.app_name); }

}

Passing Context

Context problempublic class MyActivity extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); String appName = getString(R.string.appName); }

}Presentation

Application logic

Domain

Presentation

Application logic

Domain

Injection libraries

Dependency Injection● RoboGuice● Dagger

RoboGuice

● Based on Google Guice● Lightweight● Multifunctional (has resource injection)

RoboGuicepublic class MyActivity extends RoboActivity {

@Inject MyStringProvider stringProvider;

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);

((TextView) findViewById(R.id.textView)) .setText(stringProvider.getString()); }

}

RoboGuicepublic class MyStringProvider {

Context context;

@Inject public MyStringProvider(Context context) { this.context = context; }

public String getString(){ return context.getString(R.string.app_name); }}

Notable fact

Optimezed context injectionpublic class MyActivity extends RoboActivity {

int i;

@Inject MyStringProvider stringProvider;

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);

i = 1; ((TextView) findViewById(R.id.textView)) .setText(stringProvider.getString()); }

}

RoboGuicepublic class MyStringProvider {

Context context;

@Inject public MyStringProvider(Context context) { this.context = context; }

public String getString(){ return context.getString(R.string.app_name) + ((MyActivity)context).i; }}

Daggerpublic class DaggerActivity extends DaggerBaseActivity {

@Inject DaggerStringProvider stringProvider;

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);

((TextView) findViewById(R.id.textView)).setText(stringProvider.getString());

}

}

Daggerpublic class DaggerBaseActivity extends Activity {

@Inject Bus bus;

@Override protected void onCreate(Bundle state) { super.onCreate(state);

DaggerApplication.inject(this); }

@Override protected void onResume() { super.onResume(); bus.register(this); }

@Override protected void onPause() { super.onPause(); bus.unregister(this); }}

Daggerpublic class DaggerApplication extends Application {

private static ObjectGraph objectGraph;

@Override public void onCreate() { super.onCreate();

objectGraph = ObjectGraph.create(new DaggerModule(this)); }

public static <T> void inject(T instance) { if(objectGraph != null) objectGraph.inject(instance); }}

@Module( entryPoints = { DaggerApplication.class, DaggerActivity.class})public class DaggerModule { private final Context appContext;

public DaggerModule(Context appContext) { this.appContext = appContext.getApplicationContext(); }

@Provides @Singleton Bus provideBus() { return new Bus(); }

@Provides Context provideContext() { return appContext; }

}

SummaryDagger

● More customizable● Easy communicates with other libraries● Requires more code

RoboGuice● Simpler● Out of Box functionality

Problem with views

Other “Injection” libraries

Resource and View “Injection”● RoboGuice● ButterKnife● AndroidAnnotations

RoboGuice Views Injectionpublic class RoboGuiceActivity extends RoboActivity {

@Inject RoboGuiceStringProvider stringProvider; @InjectView(R.id.textView) TextView textView;

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);

textView.setText(stringProvider.getString()); }

}

Butterknifepublic class DaggerActivity extends DaggerBaseActivity {

@Inject DaggerStringProvider stringProvider; @InjectView(R.id.textView) TextView textView;

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Views.inject(this);

textView.setText(stringProvider.getString()); }

}

AndroidAnnotations@EActivity(R.layout.translate) // Sets content view to R.layout.translatepublic class TranslateActivity extends Activity { @ViewById // Injects R.id.textInput EditText textInput; @ViewById(R.id.myTextView) // Injects R.id.myTextView TextView result; @AnimationRes // Injects android.R.anim.fade_in Animation fadeIn; @Click // When R.id.doTranslate button is clicked void doTranslate() { translateInBackground(textInput.getText().toString()); } @Background // Executed in a background thread void translateInBackground(String textToTranslate) { String translatedText = callGoogleTranslate(textToTranslate); showResult(translatedText); } @UiThread // Executed in the ui thread void showResult(String translatedText) { result.setText(translatedText); result.startAnimation(fadeIn); } }

Presentation

Application logic

Domain

Presentation

Application logic

Domain

Data Source

Android has built in SQLite

Data Source

... but lacks ORM

Alternatives:– GreenDAO– ORMLite

ORMLite@DatabaseTable(tableName = "accounts")

public class Account {

@DatabaseField(id = true) private String name;

@DatabaseField(canBeNull = false) private String password;

Account() {

// all persisted classes must define a no-arg constructor with at least package visibility

}

}

// you get the SQLiteOpenHelper from your Android Activity

ConnectionSource connectionSource = new AndroidConnectionSource(sqliteOpenHelper);

// instantiate the DAO to handle Account with String id

Dao<Account,String> accountDao = BaseDaoImpl.createDao(connectionSource, Account.class);

TableUtils.createTable(connectionSource, Account.class);

String name = "Jim Smith";

Account account = new Account(name, "_secret");

accountDao.create(account)

Account account2 = accountDao.queryForId(name);

connectionSource.close();

ORMLite

Testing

Testing

● DVM or JVM● Automation or Unit Tests

DVM

● Requires a separate Test Project● android.test or Robotium

Android.testpublic class SimpleActivityTestStandard extends ActivityUnitTestCase<SimpleActivity> { public SimpleActivityTestStandard() { super(SimpleActivity.class); }

public void setUp() throws Exception { startActivity(new Intent(getInstrumentation().getTargetContext(), SimpleActivity.class), null, null); }

public void testLayout() { SimpleActivity activity = getActivity(); assertNotNull(activity.findViewById(R.id.button1)); Button view = (Button) activity .findViewById(com.example.test.target.R.id.button1); assertEquals("My Button 1", view.getText()); }}

Robotium    public void testPreferenceIsSaved() throws Exception {  Solo solo = new Solo(getInstrumentation(), getActivity());       solo.sendKey(Solo.MENU);      solo.clickOnText("More");       solo.clickOnText("Preferences");       solo.clickOnText("Edit File Extensions");       assertTrue(solo.searchText("rtf"));                       solo.clickOnText("txt");       solo.clearEditText(2);       solo.enterText(2, "robotium");       solo.clickOnButton("Save");       solo.goBack();       solo.clickOnText("Edit File Extensions");

       assertTrue(solo.searchText("application/robotium")solo.finishOpenedActivities();

);               

JVMpublic class RoboGuiceActivityTest {

@Test public void shouldHaveISet() throws Exception { RoboGuiceActivity activity = new RoboGuiceActivity(); assertThat(activity.getI(), equalTo(1)); }}

JVMpublic class RoboGuiceActivityTest {

@Test public void shouldHaveISet() throws Exception { RoboGuiceActivity activity = new RoboGuiceActivity(); assertThat(activity.getI(), equalTo(1)); }}

We have Mockito! lets mock something

in our Activity ...

Activity source code

Robolectric to the rescue

Allows you to run

you android code

on JVM

Robolectric

@RunWith(RobolectricRoboTestRunner.class)public class RoboGuiceActivityTest {

@Test public void shouldHaveISet() throws Exception { RoboGuiceActivity activity = new RoboGuiceActivity(); activity.onCreate(null); assertThat(activity.getI(), equalTo(1)); }}

Robolectric + Mockito

Testing● DVM or JVM● Automation or Unit Tests● Robotium or Robolectric● Or both

Conclusion

More libraries?

More libraries

Solution?

Android Bootstrap● Fragments,Fragment Pager● android-maven-plugin, Dagger● ActionBarSherlock ● ViewPagerIndicator● http-request, GSON ● Robotium

http://www.androidbootstrap.com/

or customs scripts

Build Systems● Gradle● Maven

https://github.com/LArchaon/android_mvn_template/

Questions?