Share
Embed
Introduction to Android Development
The basics, best practices and the future of Android development
Sérgio Santos
April 06, 2016
Transcript
62 slides
-
Introduction to Android Development Basics, best practices and the future
Sérgio Santos from Bloco -
Overview • Basics • Architecture • Level 1 - Databases,
APIs, Compatibility • Level 2 - Testing, Dependency Injection • Future -
Activities (code) public class HomeActivity extends Activity { @Override protected
void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); } } -
Activities (intents) Intent intent = new Intent(this, HomeActivity.class); startActivity(intent); Uri
browerUri = Uri.parse("http://www.google.com"); Intent browserIntent = new Intent(Intent.ACTION_VIEW, browerUri); startActivity(browserIntent); -
Application Manifest <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.bloco.intro"> <uses-permission android:name="android.permission.INTERNET"/>
<!-- Rest of permissions --> <application android:icon="@mipmap/ic_launcher" android:label="@string/app_name"> <activity android:name=".HomeActivity" android:label="@string/home"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".AnotherActivity" android:label="@string/another"> <!-- Rest of activities --> </manifest> -
Application Build apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion
"23.2.1" defaultConfig { minSdkVersion 17 targetSdkVersion 23 versionCode 1 versionName "1.0" } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.2.1' // Rest of dependencies } -
More application features • Resources • File storage • Preferences
(key-value store) • Database (sqlite) -
res/layout/activity_home.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/message" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:hint="@string/message" /> <Button android:id="@+id/send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send" /> </LinearLayout> -
Using resources public class HomeActivity extends Activity { @Override protected
void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); Button sendButton = (Button) findViewById(R.id.send); EditText messageEditText = (EditText) findViewById(R.id.message); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String message = messageEditText.getText(); Toast.makeText(this, message, Toast.LENGTH_LONG).show(); } }); } } -
Custom Views <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <io.bloco.intro.AwesomeEditText android:id="@+id/message" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:hint="@string/message" /> <Button android:id="@+id/send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send" /> </LinearLayout> -
Custom Views class AwesomeEditText extends EditText { public AwesomeEditText(Context context,
AttributeSet attrs) { super(context, attrs); } @Override public Editable getText() { return super.getText().append(" (awesome!)"); } } -
Data binding <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="io.bloco.intro.User"/> </data> <LinearLayout
android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.lastName}"/> </LinearLayout> </layout> -
Data binding @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); HomeActivityBinding
binding = DataBindingUtil.setContentView(this,R.layout.activity_home); User user = new User("Sérgio", "Santos"); binding.setUser(user); } -
Content Providers // Run query Cursor cur = null; ContentResolver
cr = getContentResolver(); Uri uri = Calendars.CONTENT_URI; String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND (" + Calendars.ACCOUNT_TYPE + " = ?) AND (" + Calendars.OWNER_ACCOUNT + " = ?))"; String[] selectionArgs = new String[] {"[email protected]", "com.google", "[email protected]"}; // Submit the query and get a Cursor object back. cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null); -
Realm Realm realm = Realm.getInstance(this); // All writes are wrapped
in a transaction to facilitate safe multi threading realm.beginTransaction(); // Add a person Person person = realm.createObject(Person.class); person.setName("Young Person"); person.setAge(14); realm.commitTransaction(); RealmResults<User> result = realm.where(User.class) .greaterThan("age", 10) // implicit AND .beginGroup() .equalTo("name", "Peter") .or() .contains("name", "Jo") .endGroup() .findAll(); -
Retrofit public interface TwitterService { @GET("timeline/{userId}") Call<List<Tweet>> listTweets(@Path("userId") String userId);
} Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.twitter.com/") .build(); TwitterService twitterService = retrofit.create(TwitterService.class); -
Retrofit twitterService .listTweets("sdsantos" ) .enqueue(new Callback<List<Tweet>> { @Override public void
onSuccess(Call<List<Tweet>> call, Response<List<Tweet>> response) { List<Tweet> tweets = request.body(); // Do something with tweets } @Override public void onError(ApiError apiError) { // Show error } }); -
Compatibility • V4 Support Library • V7 Support Library •
Design Support Library -
Instrumentation Testing with Espresso @RunWith(AndroidJUnit4.class) public class CalculatorActivityTest { @Rule
public ActivityTestRule<CalculatorActivity> activityTestRule = new ActivityTestRule<>(CalculatorActivity.class); @Test public void testSum() throws Exception { onView(withId(R.id.calculator_input)) .perform(typeText("2 + 2")) onView(withText("=")) .perform(click()); onView(withId(R.id.calculator_result)) .check(matches(withText("4"))); } } -
Unit Testing with Mockito class Controller { private final Database
database; private final Screen screen; public Controller(Database database, Screen screen) { this.database = database; this.screen = screen; } public void check() { List<String> fruits = database.getFruits(); if (fruits.isEmpty()) { screen.showEmptyMessage(); } else { screen.showFruits(fruits); } } } -
Unit Testing with Mockito public class ControllerTest extends AndroidTestCase {
private Controller controller; private Database database; private Screen screen; @Override protected void setUp() throws Exception { super.setUp(); database = mock(Database.class); screen = mock(Screen.class); controller = new Controller(database, screen); } // Tests... } // Tests public void testCheckWithFruit() { List<String> fruits = new ArrayList<>(); fruits.add("banana"); fruits.add("apple"); when(database.getFruits()).thenReturn(fruits); controller.check(); verify(screen).showFruits(eq(fruits)); } public void testCheckEmpty() { List<String> emptyFruits = new ArrayList<>(); when(database.getFruits()).thenReturn(emptyFruits); controller.check(); verify(screen).showEmptyMessage(); } -
Dependencies // Avoid class Foo { public Foo() { Bar
bar = new Bar(); // or Bar.getInstance() bar.something(); } } new Foo(); // Better class Foo { public Foo(Bar bar) { bar.something(); } } new Foo(new Bar()); -
Dependencies // Perfect interface Bar { void something() } class
MyBar implements Bar { @Override public void something() { // ... } } class Foo { public Foo(Bar bar) { bar.something(); } } new Foo(new MyBar()); -
Dependency Injection // Goal class Foo { @Inject public Foo(Bar
bar) { bar.something(); } } Dagger v1: http://square.github.io/dagger/ Dagger v2: http://google.github.io/dagger/ -
Resources • Google Codelabs: https://codelabs.developers.google.com • Android development like a
pro: https://speakerdeck.com/rallat/android-development-like-a-pro • Fernando Cejas: http://fernandocejas.com • Kotlin 101: https://speakerdeck.com/ivanbruel/kotlin-101 • Bloco’s blog: https://bloco.io/blog