Android trends





Material Design



- MaterialDesign Style
- RecyclerView
- Cardview
- Design Library
- Shared Elements
- Vector Drawable
- Coordinator Layout
View Binding - Olvidando el findViewById()



@Bind(R.id.first_name)
EditText firstName;
@OnClick(R.id.button_continue)
public void continue(){}View Binding - Olvidando el findViewById()



@Bind(R.id.first_name)
EditText firstName;
@OnClick(R.id.button_continue)
public void continue(){}EventBus and Otto


Publisher
Event
post()
EventBus
Event
Event
Subscriber
Subscriber
onEvent(Event e)
onEvent(Event e)
EventBus and Otto


//Creación del modelo de datos
public class MessageEvent { /*Definición del modelo*/ }
//Registrar el componente que recibirá el mensaje
eventBus.register(this);
public void onEvent(MessageEvent event) {
/* Do something */
};
//Enviar el evento
eventBus.post(event);//Creación del modelo de datos
public class MessageEvent { /*Definición del modelo*/ }
//Registrar el componente que recibirá el mensaje
bus.register(this);
@Subscribe
public void answerAvailable(MessageEvent event) {
/* Do something */
};
//Enviar el evento
eventBus.post(event);EventBus and Otto




Volley & Retrofit
Comunicación con el servidor





Volley

¿Cómo funciona Volley?
The Big Picture

Añadir Request al RequestQueue

Ejecutar el Request de manera asíncrona

Devolver la respuesta al hilo principal


Volley

Ciclo de vida de una petición



Volley - Request Example
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// Manejo de la respuesta
parseArtistsResponse(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// Manejo de errores
error.printStackTrace();
}
});
// Añadir la petición a la cola de peticiones
Volley.newRequestQueue(this).add(stringRequest);

Retrofit
The android rest client



Estructura para tu modelo de Retrofit

Los archivos más importantes
ApiClient.Java
ApiService.Java
models


Retrofit - Api Service

Esta será la interfaz en la que declararemos los metodos de la api
public interface GitHubService {
@GET("/users/{user}/repos")
void listRepos(@Path("user") String user, Callback<List<Repo>>);
}

Retrofit - Api Service

Las cabeceras
@GET("/users/{user}/repos")
@GET("/users/list?sort=desc")Las anotaciones de los métodos, de primera instancia, nos permiten manipular el tipo de método y la URL a la cuál haremos la petición.



Argumentos del método
@POST("/users/new")
void createUser(@Body User user, Callback<User> cb);Los argumentos que contenga el método de la petición definen por medio de anotaciones qué papel jugará ese argumento.
@GET("/group/{id}/users")
List<User> groupList(@Path("id") int groupId, @Query("sort") String sort);Retrofit - Api Service



En esta clase definiremos el comportamiento del objeto encargado de realizar las peticiones.
Para un mejor manejo de este objeto utilizamos el patrón Singleton.
APP
Client
Server
Retrofit - Api Client



public class ApiClient {
public static GithubApiService API_SERVICE;
public static GithubApiService getInstance() {
if(API_SERVICE == null){
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.setConverter(new GsonConverter(gson))
.build();
API_SERVICE = restAdapter.create(GithubApiService.class)
}
return API_SERVICE
}
}

Retrofit - Api Client

ApiClient.getInstance(this)
.listRepos("silmood", new Callback<List<Repo>>{
@Override
public void success(List<Repo> repos, Response response) {
adapter.addAll(repos);
}
@Override
public void failure(RetrofitError error) {
error.printStackTrace();
}
})

Retrofit - Sending Request


Image downloading - Picasso, Glide & Fresco

Picasso.with(context)
.load(url)
.resize(50, 50)
.centerCrop()
.into(imageView)

Image downloading - Picasso, Glide & Fresco

Glide.with(myFragment)
.load(url)
.centerCrop()
.placeholder(R.drawable.loading_spinner)
.crossFade()
.into(myImageView);

Image downloading - Picasso, Glide & Fresco

<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/my_image_view"
android:layout_width="20dp"
android:layout_height="20dp"
fresco:fadeDuration="300"
fresco:actualImageScaleType="focusCrop"
fresco:placeholderImage="@color/wait_color"
fresco:failureImage="@drawable/error"
fresco:retryImage="@drawable/retrying"
fresco:retryImageScaleType="centerCrop"
fresco:backgroundImage="@color/blue"
fresco:overlayImage="@drawable/watermark"
fresco:pressedStateOverlayImage="@color/red"
fresco:roundAsCircle="false" />mSimpleDraweeView.setImageURI(uri);

ORMs DB - GreenDAO, DBFlow & Realm

//Creación de la base de datos
new DaoMaster.DevOpenHelper(this, "notes-db", null)
//Preparar los DAOs
daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
noteDao = daoSession.getNoteDao();
//Insertando un elemento a la tabla
Note note = new Note(null, noteText, comment, new Date());
noteDao.insert(note);
//Creación del modelo
Schema schema = new Schema(1, "de.greenrobot.daoexample");
Entity note= schema.addEntity("Note");
note.addIdProperty();
note.addStringProperty("text").notNull();
note.addStringProperty("comment");
note.addDateProperty("date");
new DaoGenerator().generateAll(schema, "../DaoExample/src-gen");


ORMs DB - GreenDAO, DBFlow & Realm

//Creación de la base de datos
@Database(name = ColonyDatabase.NAME, version = ColonyDatabase.VERSION)
public class ColonyDatabase {
public static final String NAME = "Colonies";
public static final int VERSION = 1;
}
//Creación de una tabla
@Table(databaseName = ColonyDatabase.NAME)
public class Queen extends BaseModel {
@Column
@PrimaryKey(autoincrement = true)
long id;
@Column
String name;
}
//Métodos de escritura sobre la base de datos
TransactionManager.getInstance()
.addTransaction(new SaveModelTransaction<>(processModelInfo));
TransactionManager.getInstance()
.addTransaction(new UpdateModelListTransaction(processModelInfo));
TransactionManager.getInstance()
.addTransaction(new DeleteModelListTransaction(processModelInfo));
//Métodos de lectura
TransactionManager.getInstance().addTransaction(new SelectListTransaction<>(new TransactionListenerAdapter<TestModel.class>() {
@Override
public void onResultReceived(List<TestModel> testModels) {
// on the UI thread, do something here
}
}, TestModel.class, condition1, condition2,..);


ORMs DB - GreenDAO, DBFlow & Realm
//Clase modelo
public class User extends RealmObject {
@PrimaryKey
private String ame;
private int age;
@Ignore
private int sessionId;
// Standard getters & setters
}
//Métodos de escritura sobre la base de datos
Realm realm = Realm.getInstance(this);
realm.beginTransaction();
User user = realm.createObject(User.class); // Create a new object
user.setName("John");
user.setEmail("john@corporation.com");
realm.commitTransaction();
//Métodos de lectura
RealmQuery<User> query = realm.where(User.class);
query.equalTo("name", "John");
query.or().equalTo("name", "Peter");
RealmResults<User> result1 = query.findAll();
Realm


RxAndroid + Retrolambda
Observable<OnTextChangeEvent> userNameText =
WidgetObservable.text((EditText) findViewById(R.id.edtUserName));
userNameText.subscribe( e -> Log.d("[Rx]", e.text().toString()));
Bonus - RxJava + Retrofit
@GET("/v1/search?type=artist")
Observable<ArtistSearchResponse> searchArtist(@Query("q") String query);public void performSearch(String query) {
apiService.searchArtist(query)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<ArtistSearchResponse>() {
@Override
public void call(ArtistSearchResponse artistSearchResponse) {
artistSearchResponse.getArtists();
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
});
}

RxJava + Retrofit + Retrolambda
@GET("/v1/search?type=artist")
Observable<ArtistSearchResponse> searchArtist(@Query("q") String query);public void performSearch(String query) {
apiService.searchArtist(query)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ArtistSearchResponse::getArtists,
Throwable::printStackTrace);
}

apiService.searchArtist(query)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(artistSearchResponse -> Observable.from(artistSearchResponse.getArtists()))
.first()
.subscribe(Artist::getName);apiService.searchArtist(query)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(artistSearchResponse -> Observable.from(artistSearchResponse.getArtists()))
.last()
.subscribe(Artist::getName);

RxJava + Retrofit + Retrolambda
@GET("/v1/artist")
Observable<Bio> getBio(@Query("name") String artistName);apiService.searchArtist(query)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(artistSearchResponse -> Observable.from(artistSearchResponse.getArtists()))
.flatMap(artist -> apiService.getArtistBio(artist.getName()))
.subscribe(bio -> bio.getAutor());

RxJava + Retrofit + Retrolambda
MVP

Una arquitectura más limpia




MVP - Model View Presenter
Interactor
Presenter
ViewModel

Petición de datos
Notificar
Mostrar información
ViewModel
Comunmente implementado por una actividad o fragmento
@Override
public void setupList() {
mArtistResultsList.setLayoutManager(new LinearLayoutManager(CONTEXT));
mArtistResultsList.setAdapter(mResultsAdapter);
}
@Override
public void setupAdapter() {
mResultsAdapter.setOnItemClickListener(mSearchPresenter);
}
@Override
public void setupSearchInput() {
mArtistSearchInput.setQueryListener(mSearchPresenter);
}
@Override
public void onQueyChange(String query){
mPresenter.searchArtists(query);
}
@Override
public void displayFoundArtists(ArrayList<Artist> artists) {
mResultsAdapter.replace(artists);
}
@Override
public void displayFailedSearch() {
Toast.makeText(CONTEXT, R.string.failed_search, Toast.LENGTH_SHORT).show();
}
@Override
public void displayNetworkError() {
Toast.makeText(CONTEXT, R.string.network_error, Toast.LENGTH_SHORT).show();
}
@Override
public void displayServerError() {
Toast.makeText(CONTEXT, R.string.server_error, Toast.LENGTH_SHORT).show();
}Recibe las interacciones del usuario con la vista
Presenter
@Override
public void searchArtists(String query) {
searchInteractor.performSearch(query, this);
}
@Override
public void onArtistsFound(ArrayList<Artist> artists) {
searchView.displayFoundArtists(artists);
}
@Override
public void onFailedSearch() {
searchView.displayFailedSearch();
}
@Override
public void onNetworkError(RetrofitError error) {
error.printStackTrace();
searchView.displayNetworkError();
}
@Override
public void onServerError(RetrofitError error) {
error.printStackTrace();
searchView.displayServerError();
}Interactor


@Override
public void performSearch(String query, final ArtistSearchServerCallback callback){
apiService.searchArtist(query, new Callback<ArtistSearchResponse>() {
@Override
public void success(ArtistSearchResponse artistSearchResponse, Response response) {
if(artistSearchResponse.getArtists().isEmpty())
callback.onFailedSearch();
else
callback.onArtistsFound(artistSearchResponse.getArtists());
}
@Override
public void failure(RetrofitError error) {
if (error.getKind() == RetrofitError.Kind.NETWORK)
callback.onNetworkError(error);
else
callback.onServerError(error);
}
});
}Dependency Injection


Interactor
Presenter
ViewModel
Dependency
Dependency
Dagger 2


public ArtistSearchPresenter(ArtistSearchView view, ArtistSearchInteractor interactor) {
searchView = view;
searchInteractor = interactor;
}Dependencia
Dependencia
Necesitamos de otros objetos para construir un objeto
Dagger 2


Clases que crean instancias de los objetos para la inyección de dependencias
Constituyen una API interna a través de la cual obtenemos los objetos que necesitamos para resolver las dependencias
@module@component@inject
Inyecta una dependencia. Comunmente se usa como anotación para un atributo de una clase


Gradle Flavors & Build Variants
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField 'String', 'HOST', '"http://192.168.1.34:8000"'
}
debug {
minifyEnabled false
buildConfigField 'String', 'HOST', '"http://192.168.1.34:3000"'
}
}
}

Gradle Flavors & Build Variants
android {
productFlavors {
free {
packageName 'com.silmood.myapp'
resValue('color', 'primary' , '"#000"')
}
paid {
packageName 'com.silmood.myapp.paid'
resValue('color', 'primary' , '"#fff"')
}
}
}

Go, Dart & Kotlin




That's it!
silmood
@silmood



Android Trends
By Petter Hdz
Android Trends
- 1,330