Pages

.

Showing posts with label Android. Show all posts
Showing posts with label Android. Show all posts

TypeConverters with Custom Types, Static or Instance Methods? | Android Room Persistence Library



There's all the useless copy and paste tutorials out there that only use the Date object. Congratulations authors, on adding nothing to the world. Then there is the inconsistency on whether the @TypeConverter methods should be static or instance. I'll sort this out in this tutorial.


1. Static, or Instance, @TypeConverter Methods?

Short Answer: It doesn't matter, choose which ones you want to use. The generated code will account for this.

I looked at the generated code, as I was using static methods for the Date Converters and instance methods for my custom types Collection Converters.

1.1. Static Methods

The generated code, used static calls and it didn't make an attempt to make an instance out of the Date Converters class.

Static Declatation
public class DateConverters {

  @TypeConverter
  public static Long dateToTimestamp(Date date) { ... }
  ...
}



Generated Code from Static Declaration
public BikeRaceDao_Impl(RoomDatabase __db) {
  ...
  final Long _tmp =
    DateConverters.dateToTimestamp(
        value.getStartDate());
  ...
}

1.2. Instance Methods

The generated code, made an instance of the CollectionConverters class and used the methods with the instance.

Instance Declaration

public class CollectionConverters {

  @TypeConverter
  public String stringSetToString(Set<String> stringSet);
  ...
}

Generated Code from Instance Declaration
public class ProfileFilterDao_Impl
           implements ProfileFilterDao {
  ...
  private final CollectionConverters __collectionConverters =
      new CollectionConverters();
  ...
  public ProfileFilterDao_Impl(RoomDatabase __db) {
    ...
    _tmp_1 =
        __collectionConverters.stringSetToString(
            value.getCategories());
    ...
  }
}

1.3. Conclusion

I would suggest that we use Static methods, with a private constructor to prevent instances of these classes being misused, as per Effective Java 3rd Edition Item Four: Enforce noninstantiability with a private constructor.


2. @TypeConverter methods with Custom Types

As I've said in the Conclusion above, I've gone for static methods with a private constructor. I am using GSON to convert my POJOs to JSON for storage in the database.


/**
 * Converters for the RoomDatabase.
 * This class keeps the Converters for Collections together.
 */
public class CollectionConverters {

  @TypeConverter
  public static String stringSetToString(
           Set<String> stringSet) {

    return (stringSet == null || stringSet.isEmpty()) ?
        null : new Gson().toJson(stringSet);
  }

  @TypeConverter
  public static Set<String> stringToStringSet(
           String string) {

    if (string == null || string.isEmpty())
        return null;

    Type setType =
        new TypeToken<HashSet<String>>() {}.getType();

    return new Gson().fromJson(string, setType);
  }

  @TypeConverter
  public static String raceTypeSetToString(
           Set<RaceType> raceTypeSet) {

    return (raceTypeSet == null || raceTypeSet.isEmpty()) ?
        null : new Gson().toJson(raceTypeSet);
  }

  @TypeConverter
  public static Set<RaceType> stringToRaceTypeSet(
           String string) {

    if (string == null || string.isEmpty()) return null;

    Type setType =
        new TypeToken<HashSet<RaceType>>() {}.getType();

    return new Gson().fromJson(string, setType);
  }

  /**
   * Preventing Instancing with a Private Constructor.
   */
  private CollectionConverters() {
    throw new AssertionError(
      "Class should not be instantiated");
  }
}

Thank you for reading and I hope this helped.
reade more... Résuméabuiyad

Android Room Database Tutorial with Fragments, RecyclerView, LiveData, ViewModel and Data Binding


Intro

This tutorial will implement an Android App with Room Database, Fragments, RecyclerView, LiveData, ViewModel and Data Binding. Lets stop messing around, we'll get to the code.


Step 0 - The App's Build.Gradle file

We need to add the following to the app/build.gradle file for the app to work. We're using Java 1.8 for the Lambda in the Fragment and Data Binding.

android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
dataBinding {
enabled = true
}
}

dependencies {
...
implementation "android.arch.lifecycle:extensions:1.0.0"
implementation "android.arch.persistence.room:runtime:1.0.0"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
testImplementation "android.arch.persistence.room:testing:1.0.0"
}


Step 1 - Database Entity, the Model

Start with the Data Driven Design, we'll first implement the model.

@Entity
public class BlogPost {

@PrimaryKey(autoGenerate = true)
private int id;

@ColumnInfo(name = "title")
private String title;

/**
* Default Constructor
*
* Room Database will use this no-arg constructor by default.
* The others are annotated with @Ignore,
* so Room will not give a warning about "Multiple Good Constructors".
*/
public BlogPost() {
}

@Ignore
public BlogPost(String title) {
this.title = title;
}

// Setters and Getters...
}


Step 2 - DAO - Data Access Object

We'll need to outline an interface class that will allow us to access the Database's content via queries.

@Dao
public interface BlogPostDao {

@Query("SELECT * FROM blogpost")
LiveData<List<BlogPost>> getAllBlogPosts();

@Query("SELECT * FROM blogpost WHERE id = :id LIMIT 1")
LiveData<List<BlogPost>> findBlogPostById(long id);

@Query("SELECT * FROM blogpost WHERE title LIKE :title LIMIT 1")
LiveData<List<BlogPost>> findBlodPostByTitle(String title);

@Query("SELECT COUNT(*) FROM blogpost")
int rowCount();

@Insert
void insertBlogPosts(BlogPost... blogPosts);

@Update
void updateBlogPosts(BlogPost... blogPosts);

@Delete
void deleteBlogPosts(BlogPost... blogPosts);
}


Step 3 - Room Database Implementation

To access the generated DAO class, the RoomDatabase needs to be implemented.

@Database(entities = {BlogPost.class}, version = 1, exportSchema = false)
public abstract class BlogPostDatabase extends RoomDatabase {

private static BlogPostDatabase INSTANCE;

public static BlogPostDatabase getInstance(Context context) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(
context.getApplicationContext(),
BlogPostDatabase.class,
"BlogPostsDatabase")
.build();
}

return INSTANCE;
}

public static void destroyInstance() {
INSTANCE = null;
}

public abstract BlogPostDao blogPostDao();
}


Step 4 - View Model Implementation

The AndroidViewModel show the LiveData List that the MainActivity and MainFragment will observe.

public class BlogPostsViewModel extends AndroidViewModel {

private final LiveData<List<BlogPost>> blogPosts;

public BlogPostsViewModel(@NonNull Application application) {
super(application);

blogPosts = BlogPostDatabase
.getInstance(getApplication())
.blogPostDao(
.getAllBlogPosts();
}

public LiveData<List<BlogPost>> getBlogPosts() {
return blogPosts;
}
}


Step 5 - RecyclerView Adapter

To populate the RecyclerView, we'll need the Adapter.

public class MainActivityFragmentRecyclerViewAdapter extends
RecyclerView.Adapter
<MainActivityFragmentRecyclerViewAdapter
.MainActivityFragmentRecyclerViewHolder> {

private List<BlogPost> blogPosts;

public MainActivityFragmentRecyclerViewAdapter(List<BlogPost> blogPosts) {
this.blogPosts = blogPosts;
}

@Override
public MainActivityFragmentRecyclerViewHolder onCreateViewHolder(
ViewGroup parent, int viewType) {
RecyclerItemBinding itemBinding = RecyclerItemBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);

return new MainActivityFragmentRecyclerViewHolder(itemBinding);
}

@Override
public void onBindViewHolder(
MainActivityFragmentRecyclerViewHolder holder, int position) {
String blogPostTitle = blogPosts.get(position).getTitle();
holder.bind(blogPostTitle);
}

@Override
public int getItemCount() {
return blogPosts.size();
}

public void setBlogPosts(List<BlogPost> blogPosts) {
this.blogPosts = blogPosts;
notifyDataSetChanged();
}

static class MainActivityFragmentRecyclerViewHolder
extends RecyclerView.ViewHolder {

RecyclerItemBinding binding;

MainActivityFragmentRecyclerViewHolder(RecyclerItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}

void bind(String blogPostTitle) {
binding.blogPostTextView.setText(blogPostTitle);
binding.executePendingBindings();
}
}
}


Step 6 - Observing the ViewModel in the Fragment

We observe the ViewModel and use it to update the RecyclerView's content.

public class MainActivityFragment extends Fragment {

public MainActivityFragment() {
}

@Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {

FragmentMainBinding binding =
DataBindingUtil.inflate(
inflater, R.layout.fragment_main, container, false);

MainActivityFragmentRecyclerViewAdapter recyclerViewAdapter =
new MainActivityFragmentRecyclerViewAdapter(new ArrayList<>());

binding.recyclerView.setLayoutManager(
new LinearLayoutManager(getActivity()));

binding.recyclerView.setAdapter(recyclerViewAdapter);

BlogPostsViewModel viewModel =
ViewModelProviders.of(this).get(BlogPostsViewModel.class);

viewModel.getBlogPosts().observe(
MainActivityFragment.this, recyclerViewAdapter::setBlogPosts);

return binding.getRoot();
}
}


Step 7 - The Other Fragments and Activities

There's not too much craziness in the other Fragments and Activities. We're using DataBinding and there's an Activity-Fragment pair for adding a new Database Entry.

The Activity Package on GitHub: AndroidRoomDatabaseTutorialBasic / activity.


Step 8 - The Layout Files

There's no magic in the layout files. They only have the layout tag for the Data Binding to work. So I'll leave the link to the directory from the Git Hub.

The Layout Directory on GitHub: AndroidRoomDatabaseTutorialBasic / main / res / layout.


Step 9 - Optionally add a Database Intialiser

We can use the following class to populate the database with hard coded data.

public class DatabaseInitializer {

public static void populateAsync(final BlogPostDatabase database) {
new PopulateDbAsync(database).execute();
}

private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {

private final BlogPostDatabase database;

PopulateDbAsync(BlogPostDatabase database) {
this.database = database;
}

@Override
protected Void doInBackground(final Void... params) {
// If the Database is empty, add the initial data.
if (database.blogPostDao().rowCount() == 0) {
List<BlogPost> blogPosts = new ArrayList<>();
blogPosts.add(new BlogPost("Blog Post #1"));
blogPosts.add(new BlogPost("Blog Post #2"));
blogPosts.add(new BlogPost("Blog Post #3"));

database.blogPostDao()
.insertBlogPosts(
blogPosts.toArray(new BlogPost[blogPosts.size()]));
}

return null;
}
}
}


Examining The Database

Using DB Browser for SQL Lite, we can attach it to the database file to view the database. The database file can be got in Android Studio's Device File Explorer view.

The database file is in the directory: data/data/com.package/database/



reade more... Résuméabuiyad

Android Database Tutorial - Basic SQLite DB



UPDATE: This is superseded by the Room Database Tutorial.

Here is the new tutorial: Room Database, Fragments, RecyclerView, LiveData, ViewModel and Data Binding

Is this what you're looking for?

The areas of Android Development that are touched on in this tutorial are:
  • SQLiteOpenHelper - For Database purposes,
  • AsyncTask - To load the ListView without using the Main UI Thread,
  • Thread - To execute queries without using the Main UI Thread,
  • ViewHolder Pattern - To display the BlogPost titles in the ListView,
  • Dynamic Creation of Database Row IDs - a lot of the database logic deals with the BlogPost titles, so I needed a way to handle insertion.

Introduction

I'm making this tutorial as a future reference for myself, and publishing it so it may be a basic step by step guide for others to implement Databases in their Android Application. I have Googled for "Android Database Tutorial". The Google Developer results are confusing, Android Hive is old/erroneous and Vogella's one is really detailed/long.

The App

This will be a tutorial on the basics of creating a database in Android. The end result will be an app that can create records and display them as a ListView. The reason the app is so simple is to remove the clutter, to allow the database to be the main focus of the tutorial.

Also there isn't a whole lot of data integrity checks, logging, user feedback, exception handling here, so keep your panties unbunched.

I can evolve this app in the following ways, and I will with future posts:
  • Advanced Data Types in the Database,
  • Content Providers,
  • RecyclerView for a nice layout to the ListView,
  • ActionBar commands to create and delete BlogPosts,
  • Pulling from a RESTful Service, etc...

Android Database Technology

Android uses an SQLite Database when storing data on the device. You can use a remote database too, but it's best to use a RESTful Web Service to accomplish tasks in that way. Storing content locally is good if the user doesn't have a network connection and wants to view current data, or create data for synching to the cloud later.

Database Implementation Flow

I'll provide a synopsis of the steps below. As Android Database interaction is Model Driven, we'll implement our Model Class first. "Model" is just a fancy word for "POJO", which itself is a fancy word for "Plain Old Java Object". Secondly we'll implement the outline of our Database Class, to provide the foundation to build interactions on. The Database Class will be a Singleton, so only one instance will exist in our app, it's best to get into good practices early. There's two schools of thought on the next step; one, implement the queries as needed, as not to bloat functionality, two, write the CRUD logic up front to reduce development time later on and we're in the database zone at this time. I'd recommend the former for normal development, but for the purposes of keeping this tutorial streamlined, I'll take the latter approach. Finally we'll add the UI Activities to make use of the database functionality.

I have the source code posted to GitHub: Android Database Tutorial-Basic repository. Each of the steps below will be a commit.



Step 0: New App with Blank Activity

This step is optional, you may already have the app that you wish to use the database in. Create the new app, give it a name and select the "Blank Activity" to begin with. I just accepted all the defaults for the naming. As none of that matters much in the context of this tutorial. Close MainActivity.java and activity_main.xml when they open. We'll deal with them later. At this point I added my project to Source Control with git.

Commit Diff: 394ff7f



Step 1: Implement the Model Class

We'll add a new package under our app, for good practice. We'll call it "model". Next add a Java Class, which will be our model. We'll call it "BlogPost". Our app will display words that the user creates in a ListView on screen. With that in mind, here is our simple Model Class, it has the ID and the Title fields.


The ID field always needs to be present in a Model Class that we want to be in the database. The ID is the Primary Key for the database. The Database will start from 1, not from 0 as you'd expect. Hopefully, simple stuff here.

Commit Diff: 391add4



Step 2: Implement the Database Class

In this step, we will create the outline of the Database Class. Add another package, call it "database". Add a Java Class, call it "DatabaseConnection" and the kind will be Singleton.

In our code editor we make it so DatabaseConnection.java extends SQLiteOpenHelper. SQLiteOpenHelper requires that we implement its two abstract methods, onCreate() and onUpgrade(). The lifecycle method onCreate() is called when the app is first launched. In the onCreate() method implementation, we issue a CREATE TABLE command to the database. The lifecycle method onUpgrade() is called when the app is updated to a later version, i.e when an update is installed from the Google Play Store. In the onUpgrade(), you can manage the addition and removal of columns from the database table. But in our implementation, we'll just drop the old database in favour of the new version, which will be created when we call the onCreate() method manually.


Commit Diff: c685b65



Step 3: Basic Queries for our Use Cases

The use cases of this app are;
  1. Create a new BlogPost.
  2. Delete an existing BlogPost,
  3. View all the BlogPosts in the database.
Still in our DatabaseConnection class, we add the following three methods after the onUpgrade() method.


Commit Diff: 16bf861



Step 3 Bonus: The other CRUD queries

The abbreviation CRUD stands for Create, Retrieve, Update and Delete. So far we have the Creation, Deletion and Retrieval of records implementation. Let's see how the Updating and other forms of Retrieval of records are handled with respect to our Model. Again, keeping things simple, we'll ignore the possibility of raising exceptions and handling return codes here.

The following methods are pretty self explanatory, they allow for the retrieval of one record, allow updating of one record, count the records, determine if a record exists, and find the ID of an existing record in the database.


Commit Diff: 2340692



Step 4: UI Activity to Insert, Delete and Display Records



As this tutorial is not about UI programming, I'll just leave code for calling the queries the commit ID, for the rest of the code, here for you to examine. The AsyncTask and ViewHolder Pattern is defined in that Commit ID. The UI is an EditText to input BlogPost titles, then two buttons to Add and Delete the BlogPosts, based on their titles, in the ListView based on their titles matching the text in the EditText.

Loading https://gist.github.com/8488564....

Commit Diff: 7b59a0e, this one is a bit of a mish-mash of updates.



Conclusion

I hope this tutorial was basic enough for novice programmers to understand and comprehensive for intermediate programmers to get ideas of this possibilities of storing data locally on the Android device.

All the code is in the GitHub Repository, AndroidDatabaseTutorial-Basic. If the app fails to build on your IDE, start by changing the "buildToolsVersion" in your "app/build.gradle" to match the version in your SDK Manager.

reade more... Résuméabuiyad

Apps updated to Material Design



My three existing Android apps;
  • Football Crest Quiz,
  • Tip Cal¢
  • and Charlie Foxtrot,
Finally got the Android 5.0 treatment. It was a little tricky to sort out the first update, but the other two were similar after that.

I'm most proud of the update to Charlie Foxtrot. I like the colouring of the app. I really hate the search icon.


The other two updates are just changing the theme for the res/values-v21/styles.xml to Material rather than Holo.

I'm guilty of dropping the ball on maintaining my apps, so I righted this wrong by updating them. I also uploaded the three of them to GitHub, as their major development is done. So others can follow up, if they'd like.

There was a problem with the signing key for Football Crest Quiz app, so I cannot publish the update to the Play Store.

Grab the updated apps from the Google Play store.

Android app on Google Play



The apps on GitHub: Charlie Foxtrot | Tip Calc | Football Crest Quiz
reade more... Résuméabuiyad