3. Existing Solutions
Cursor query(boolean distinct, String table, String[]
columns, String selection, String[]
selectionArgs, String groupBy, String having,
String orderBy, String limit);
static final String TAG = InEvery.class.getSimpleName();
Log.wtf(TAG, "Y TAG?!");
Picasso.with(context).load(url).placeholder(R.drawable.ph)
.error(R.drawable.err).into(imageView);
Repetetive code:
Not using a builder when should:
Using a builder when shouldn't:
(Fluent kills inheritance)
4. Existing Solutions
Gson gson = new GsonBuilder().excludeFieldsWithModifiers(
Modifier.STATIC).create();
// By default, if you mark a field as transient, it will be
excluded.
The @DatabaseField annotation can have the following fields:
(27 ot them)
Entity user = schema.addEntity("User");
user.addIdProperty();
user.addStringProperty("name");
user.implementsSerializable();
// Then generate .java files & paste to your project.
A mess to maintain:
Abusing built-in language features:
Many features remain unused on mobile:
5. Existing Solutions
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/pic" />
NetworkImageView pic = (NetworkImageView)view
.findViewById(R.id.pic);
pic.setImageUrl("http://example.com/pic.png",
mImageLoader); // OMG
AsyncHttpClient client = new AsyncHttpClient();
client.get("http://example.com/timeline.json", new
JsonHttpResponseHandler() {
@Override
public void onSuccess(JSONArray response) {
System.out.println(
"Keep calm & process JSON on the UI thread.");
}
});
Ignoring separation of concerns #2:
Ignoring separation of concerns:
7. ●
Help handle most common tasks?
●
Uniform API?
●
Simple to make easy things easy?
●
Flexible to make hard things possible?
●
Won't reinvent Java, OO friendly?
●
Like Django, but for Android?
What If?
12. DependencyProvider
private final DBOpenHelper dbOpenHelper;
private PrefsManager prefsManager;
public DependencyProvider(Context ctx) {
super(ctx);
dbOpenHelper = new DBOpenHelper(ctx);
}
@Override
public AbstractDBOpenHelper getDBOpenHelper() {
return dbOpenHelper;
}
public PrefsManager getPrefsManager(Context ctx) {
if (prefsManager == null) {
prefsManager = new PrefsManager(ctx);
}
return prefsManager;
}
public DialogFactory getDialogFactory(Context ctx) {
return new DialogFactory(ctx);
}
13. Injection Annotations
@InjectDependency - from DependencyProvider
@InjectParentActivity - in a Fragment
@InjectView(int id, boolean click) - from layout
@InjectFragment(int id) - in an Activity
@InjectResource(int value) - strings, images...
@InjectSystemService - TelephonyManager, ...
@InjectBundleExtra(String key, boolean optional)
- from Intent in an Activity
or from args in a Fragment
14. Injection in an Activity
class MyActivity extends Activity implements OnClickListener {
@InjectSystemService
private LayoutInflater layoutInflater;
@InjectDependency
private DialogFactory dialogFactory;
@InjectView(id = R.id.button_add, click = true)
private Button addButton;
@Override
public void onPreInject() {
setContentView(R.layout.activity_my);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // <-- Injection
}
@Override
public void onClick(View v) {
if (v == addButton) { // TODO }
}
}
16. Base Activities & Fragments
DroidParts:
●
3.0+ with ActionBar features
●
Pre-3.0, no ActionBar
(.legacy.*)
DroidParts Support:
●
Pre-3.0 with Android Support Library
(.support.v4.*)
●
Pre-3.0 with ActionBarSherlock
(.sherlock.*)
●
Pre-3.0 with Android Support Library ActionBar
(.support.v7.*)
17. Base Activities & Fragments
No “DroidParts” or “DP” prefix.
Nice features:
→
SingleFragmentActivity
TabbedFragmentActivity
21. EventBus
interface EventReceiver<T> {
void onEvent(String name, T data);
}
EventBus.postEvent(String name)
// or
EventBus.postEventSticky(String name, Object data)
Send:
Receive:
EventBus.registerReceiver(EventReceiver<T> receiver,
String... eventNames)
// TODO Remember to unregister.
22. EventBus
@ReceiveEvents(name = { "MESSAGE_SENT", "MESSAGE_RECEIVED" })
private void onMessageEvent(String eventName) {
// TODO process 2 types of events without data
}
@ReceiveEvents
private void onAnyEvent(String eventName, Object eventData) {
// TODO process any event with optional data
}
EventBus.postEvent(String name)
// or
EventBus.postEventSticky(String name, Object data)
Send:
Receive in an injected class:
24. JSON, ORM
// JSON
class JSONSerializer<ModelType extends Model> {}
// ORM
class EntityManager<EntityType extends Entity> {}
// JSON
class Model implements Serializable {}
// ORM
class Entity extends Model {
@Column(name = "_id")
public long id;
}
Base Model Classes:
Managers:
25. JSON
@Key(Sting name, boolean optional)
Using org.json.* under the hood.
class JSONSerializer<ModelType extends Model> {
JSONObject serialize(ModelType item)
JSONArray serialize(Collection<ModelType> items)
ModelType deserialize(JSONObject obj)
ArrayList<ModelType> deserialize(JSONArray arr)
}
Annotation:
Manager:
26. JSON
{
"author": "Alex"
"address": "http://droidparts.org",
"posts": [{
"published": 1398970584375,
"title": "Title",
}],
}
class Blog extends Model {
@Key
public String author;
@Key(name="address")
public Uri uri;
@Key
public Post[] posts;
}
class Post extends Model {
@Key
public Date published;
@Key(name="title", optional=false)
public String title;
@Key(optional = true)
public String text = "";
}
27. JSON
{
"sub_obj": {
"str": "val"
}
}
@Key(name = "sub_obj" + Key.SUB + "str")
String str;
@Override
public Blog deserialize(JSONObject obj) throws JSONException {
Blog blog = super.deserialize(obj);
for (Post post : blog.posts) {
post.blog = blog;
}
return blog;
}
Override serialize()/deserialize() for tweaking:
Accessing nested object's property:
28. ORM
@Table(name = "posts")
public class Post extends Entity {
@Column
public Date published;
@Column(unique = true)
public String title;
@Column(nullable = true)
public String text = "";
@Column(eager = true)
public Blog blog;
}
@Table(String name)
@Column(String name, boolean nullable, boolean unique,
boolean eager)
Annotations:
Class example:
29. ORM
class Blog extends Entity {
List<Post> getPosts() {
EntityManager<Post> em = new EntityManager<Post>(
Post.class,
Injector.getApplicationContext());
Select<Post> select = em.select().where("blog_id",
Is.EQUAL, this.id);
return em.readAll(select);
}
}
Reading one-to-many:
30. EntityManager: C_UD
class Post extends Entity {}
EntityManager<Post> em =
new EntityManager<Post>(Post.class, ctx);
// Usually subclass & add helper methods.
Post post = new Post();
em.create(post);
assert(post.id != 0);
em.update(post);
em.createOrUpdate(post);
int postsDeleted = em.delete()
.where("year", Is.LESS, 2013)
.execute();
32. DB Contract
interface DB {
interface Table {
}
interface Column {
String ID = BaseColumns._ID;
}
}
@Table(name = DB.Table.BLOGS)
class Blog extends Entity {
@Column(name = DB.Column.NAME, unique = true)
String name;
}
import static org.exapmpe.app.DB.Column.*;
em.select().columns(ID, NAME).where(BLOG_ID, Is.EQUAL, 10);
33. DBOpenHelper
package org.droidparts.persist.sql;
class AbstractDBOpenHelper extends SQLiteOpenHelper { }
class DBOpenHelper extends AbstractDBOpenHelper {
@Override
protected void onCreateTables(SQLiteDatabase db) {
createTables(db, Blog.class, Post.class);
createIndex(db, "blogs", false, "title");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion) {
addMissingColumns(db, Blog.class, Post.class)
}
}
34. EntityCursorAdapter
class PostListAdapter extends EntityCursorAdapter<Post> {
PostListAdapter(Context ctx, Select<Post> select) {
super(ctx, Post.class, select);
}
@Override
void bindView(Context context, View view, Post item) {
//TODO
}
}
Select object:
●
Has type information.
●
Can read an instance.
●
Wraps a Cursor.
●
Perfect data source for an Adapter.
35. ViewHolder
class ViewHolder {
public ViewHolder(View view) {
Injector.inject(view, this);
}
}
View view = layoutInflater.inflate(android.R.layout.
simple_list_item_1);
Text1Holder holder = new Text1Holder(view);
view.setTag(holder);
holder.text1.setText("Text 1");
class Text1Holder extends ViewHolder {
@InjectView(id = android.R.id.text1)
public TextView text1;
public Text1Holder(View view) {
super(view);
}
}
37. JSON, ORM
public class MyClassConverter extends Converter<MyClass> {
@Override
public boolean canHandle(Class<?> cls) { // TODO }
@Override
public String getDBColumnType() { // TODO }
@Override
public <V> MyClass readFromJSON(Class<MyClass> valType,
Class<V> componentType, JSONObject obj,
String key) throws JSONException { // TODO }
@Override
public <V> void putToContentValues(Class<MyClass> valueType,
Class<V> componentType, ContentValues cv,
String key, MyClass val) { // TODO }
// ...
}
ConverterRegistry.registerConverter(new MyClassConverter());
Adding new type support:
38. Background Work
AsyncTask:
●
Ad-hoc tasks, based on user input.
●
Important if succeeded.
●
Independent, non-cancellable.
●
Submitting a post.
IntentService:
●
Regular tasks, scheduled.
●
Not important if succeeded.
●
Sequential, cancellable.
●
Querying for new comments.