Once again about the architecture of the Android application or a gentleman's set of libraries

    So I decided to write a review of libraries with the help of which it is easy and convenient to write applications for Android.
    The list looms like this:

    If interested, I ask for cat.

    We need to start with the fact that languages ​​are not my thing , I apologize in advance why there are so many libraries, what is due to it and why.
    Naturally, everything is dictated by the architecture of the application. Based on my experience, almost always, you need to have the following application structure:

    As you can see, the application is divided into 3 layers - the “face”, data storage and service for asynchronous commands, where the logic is almost always hidden.

    I don’t want to breed a holivar and argue about architecture, I just describe how I do it and how my colleagues do it.

    A few words why so


    Content provider is a very powerful thing with data change notifications and it's all out of the box. We use mainly as a wrapper over the base. I didn’t find a way to properly load data from an Internet there.

    Service of asynchronous commands - everything is clear from the name, performs actions asynchronously (in another thread). Almost all actions should be asynchronous - writing to the database, going to the Internet, and counting.
    Why “asynchronous commands”? Here, too, everything is simple - each action is a complete team. Which knows with what parameters to start, what to do with them and notification of its completion.
    Here there evilduck is all described in detail.

    "Muzzle"- A set of activity / fragments for displaying data. I want to note that the entire loading of data from the repository should be asynchronous (nor any trips to the database from the UI even for a single number). And here the loader manager comes to our aid - also a feature out of the box. Worth looking towards CursorLoader

    Bike


    Probably every programmer made his own bike. There is also benefit from them - you begin to understand all the pitfalls of what you need and why. But writing a bike every time is not good. And you cannot roll it from another project. The code is sold and is the property of the customer.
    Of course, your code can be issued in the lib, publish it, in the contract with the customer write that we use the lib. But writing a lib for all occasions is not so simple, all the more so why make another bike if you already have it, and bugs have already been found and fixed. So in the end it happened to me.

    I'm not that the enemy is reflexive, but I prefer the ones that generate the code. It is convenient to debut and you can always see what is happening there.
    On top of everything, I like annotations. My entire selection of libraries is practically consistent with this principle. Let's start ...

    Groundy


    This library implements command service.
    • Teams can be kenselit
    • AsyncTask lovers won't notice the transition
    • There is support for callbacks and they are very easy to use.

    I prefer to write a static method to run the command and typify the callback.
    Although a callback can be any object, I prefer to type it. This will help the compiler to help us.
    And it will protect you from the temptation to hang Kalbeks on public methods of activation, thereby violating one of the pillars of OOP - encapsulation :)
    For these reasons, you need a static launch method.

    public class LoginCommand extends GroundyTask{
    	private static final String ARG_PASSWORD = "arg_password";
    	private static final String ARG_USER = "arg_username";
    	@Override
    	protected TaskResult doInBackground() {
    		String userName = getStringArg(ARG_USER);
    		String password = getStringArg(ARG_PASSWORD);
    		//do something 
    		return succeeded();
    	}
    	public static void start(Context context, BaseLoginCommandCallback callback, String login, String password) {
    		Groundy.create(LoginCommand.class)
    				.arg(ARG_USER, login)
    				.arg(ARG_PASSWORD, password)
    				.callback(callback)
    				.queueUsing(context);
    	}
    	public static abstract class BaseLoginCommandCallback{
    		@OnSuccess(LoginCommand.class)
    		public void handleSuccess(){
    			onLoginSuccess();
    		}
    		@OnFailure(LoginCommand.class)
    		public void handleFailure(){
    			onLoginError();
    		}
    		protected abstract void onLoginSuccess();
    		protected abstract void onLoginError();
    	}
    }
    


    Retrofit


    A very simple tool for calling REST services, both well-written and not very well-written. The code really does not generate, but it is very simple.
    I looked in the direction of Spring Android, but it is somehow heavy.

    public interface ServicesFootballua {
    	String API_URL = "http://services.football.ua/api";
    	@GET("/News/GetArchive")
    	NewsArchive getNewsArchive(@Query("pageId") long pageId,
    							 @Query("count") long count,
    							 @Query("datePublish") String date);
    } 
    

    Well, that's how we use it

    	private static ServicesFootballua API = new RestAdapter.Builder()
    										.setServer(ServicesFootballua.API_URL)
    										.build()
    										.create(ServicesFootballua.class);
    ...................................
    	archive = API.getNewsArchive(PAGE_ID, COUNT, dateFormat.format(getTodayTime()));
    


    you can substitute your converter, http client and a bunch of other things.

    AnnotatedSQL


    Generates a database and content provider for annotations.
    My craft, I am completely satisfied. A couple of articles are on a habr - here and here .
    Recently, with a kick, evilduck was published in maven central.

    I looked at ORMLite, but it seems to me that it is not suitable for android. Usually we don’t need to pull everything and everything straight. Normal sql and vishki solve almost everything.

    Android Annotations


    I looked at this lib for a very long time and recently decided to zayuzat it in production - I liked it, despite the fact that it was necessary to use the heated classes.
    The most powerful tool, the main thing is not to use Background well or turn on the head.
    A good plus - you can cut it almost painlessly.

    I looked at AQuery and Dagger, but IMHO Android Annotations - already has it all.
    The only minus is sometimes it’s hard to look for the error “why is it not compiling?”.

    You can read about all the features on the off site. The only thing I want to add is that I always write a static method to start activating, creating a fragment. This isolates all the code in one place and no one in the code knows whether we are a built-in class or an original one.

    For example, a fragmentAlertDialogFragment will have a method
    	public static void show(FragmentActivity activity, 
    						DialogType type, int titleId, String msg, int positiveTitleId,
    						OnDialogClickListener positiveListener) {
    		DialogUtil.show(activity, DIALOG_NAME,
    			AlertDialogFragment_.builder()
    			.titleId(titleId)
    			.errorMsg(msg)
    			.positiveButtonTitleId(positiveTitleId)
    			.dialogType(type).build()
    		).setOnClickListener(positiveListener);
    	}
    

    and everywhere in the code the call will be like
    AlertDialogFragment.show(BaseActivity.this,
                        DialogType.CONFIRM,
                        R.string.some_title,
                        getString(R.string.some_message),
                        R.string.btn_edit,
                        new OnDialogClickListener() {...............}
    


    and no one knows about the existence of AlertDialogFragment_

    Android db-commons


    I recently found this wonderful lib. Because we use loaders everywhere and everywhere, usually the result is Cursor. You can always tighten the CursorAdapter and display what you need.

    But this one offers us to use the List, but above the cursor, and with the list, everyone’s favorite ArrayAdapter.
    Here is such a symbiosis - you kind of see List and use the ArrayAdapter, but in fact it is a cursor and an adapter cursor. Real “street magic” :) The
    guys were not too lazy and wrote such a LazyList with a small cache inside, everything is as it should be - inside LruCache.

    In order to get a List instead of Cursor, you need to write a function ( transform ) to convert the cursor string to an object and you will get a loader that returns not Cursor, but List
    return CursorLoaderBuilder.forUri(URI_ITEMS)
                    .projection(ItemConverter.PROJECTION)
                    .where(ItemTable.ACTIVE_STATUS + " = ?", 1)
                    .where(ItemTable.DESCRIPTION + " like ?", "%" + searchText + "%")
                    .transform(new ItemConverter()).build(getActivity());
    


    But as we know, sometimes you need to read the cursor into some object, for example, you need to count something, for this the guys have a wrap method
    public Loader onCreateLoader(int i, Bundle bundle) {
    	return CursorLoaderBuilder
    			.forUri(ITEMS_URI)
    			.projection("count(" + ItemTable.GUID + ")")
    			.where(ItemTable.ACTIVE_STATUS + " = ?", 1)
    			.where(ItemTable.STOCK_TRACKING + " = ?", 1)
    			.where(ItemTable.TMP_AVAILABLE_QTY + " <= " + ItemTable.RECOMMENDED_QTY)
    			.wrap(new Function() {
    				@Override
    				public Integer apply(Cursor c) {
    					if (c.moveToFirst()) {
    						return c.getInt(0);
    					}
    					return 0;
    				}
    			}).build(DashboardActivity.this);
    }
    

    here is such a simple way.
    This is a very trivial example. The possibilities are much cooler.
    At the same time, the wrap method is still executed in another thread. so you can still go to the database for more data.
    As I said, this is sometimes necessary.

    Ending


    It’s clear that for the UI itself a bunch of different libs are used (mostly components), but this already depends on the designer’s imagination :)

    Well, here’s such a piece of build script for gradle that apt would wound up ( yes, there is already a special plugin, but have not tried it yet ).
    Oh yes - download android-db-commons-0.1.6.jar to the libs folder

    ext.androidAnnotationsVersion = '2.7.1';
    configurations {
        apt
    }
    dependencies {
        compile fileTree(dir: 'libs', include: '*.jar')
        compile 'com.google.guava:guava:13.0.1'
        compile 'com.telly:groundy:1.3'
        apt 'com.telly:groundy-compiler:1.3'
        apt "com.googlecode.androidannotations:androidannotations:${androidAnnotationsVersion}"
        compile "com.googlecode.androidannotations:androidannotations-api:${androidAnnotationsVersion}"
        compile 'com.github.hamsterksu:android-annotatedsql-api:1.7.8'
        apt 'com.github.hamsterksu:android-annotatedsql-processor:1.7.8'
    }
    android.applicationVariants.all { variant ->
    	aptOutput = file("${project.buildDir}/source/apt_generated/${variant.dirName}")
    	variant.javaCompile.doFirst {
    		aptOutput.mkdirs()
    		variant.javaCompile.options.compilerArgs += [
    			'-processorpath', configurations.apt.getAsPath(),
    			'-processor', 'com.annotatedsql.processor.provider.ProviderProcessor,com.annotatedsql.processor.sql.SQLProcessor,com.googlecode.androidannotations.AndroidAnnotationProcessor,com.telly.groundy.GroundyCodeGen',
    			'-AandroidManifestFile=' + variant.processResources.manifestFile,
    			'-s', aptOutput
    		]
    	}
    }
    


    Thank you all for your attention.

    Also popular now: