Rosie is an Android framework to create applications following the principles of Clean Architecture.

Overview

Karumi logoRosie Maven Central

The only way to make the deadline—the only way to go fast—is to keep the code as clean as possible at all times.

— Robert C. Martin in Clean Code: A Handbook of Agile Software Craftsmanship

Introduction

Rosie is an Android framework to create applications following the principles of Clean Architecture.

Rosie divides your application in three layers, view, domain and repository. For each layer, Rosie provides plenty of classes that will make defining and separating these concerns much easier.

  • View: It contains all your presentation logic, implemented through the Model-View-Presenter pattern. Rosie provides classes to represent the main components of this layer like RosieActivity, RosieFragment or RosiePresenter.
  • Domain: Holding all your business logic, its main component is RosieUseCase that gives you an easy way to define your application use cases and execute them in a background thread using the command pattern.
  • Repository: This layer gives you an abstraction of how to retrieve and store data in your application following the Repository pattern. RosieRepository and the multiple DataSource classes gives you the base to start building your own repositories.

Finally, Rosie comes with Dagger to solve Dependency inversion through Dependency Injection.

Screenshots

Screencast

Application UI/UX designs by Luis Herrero.

Data provided by Marvel. © 2016 MARVEL

Usage

First thing you will need to do is to make your own Application instance extend RosieApplication in order to provide your global dependencies module to Dagger:

public class SampleApplication extends RosieApplication {
	@Override protected List<Object> getApplicationModules() {
		return Arrays.asList((Object) new SampleGlobalModule());
	}
}

Extending from RosieApplication is needed to be able to easily use the configuration Rosie provides you related to Dependency Injection. If you do not want to use Dependency Injection in your project, you do not need to extend from RosieApplication.

Rosie provides several base classes to start implementing your architecture separated in three layers, view, domain and repository. Let's explore them in detail.

View

The view package contains all the classes needed to implement your presentation logic following the MVP pattern. To use the view package, make your Activity extend from RosieActivity or your Fragment from RosieFragment and specify the layout that Rosie will automatically inflate for you:

public class SampleActivity extends RosieActivity {
	@Override protected int getLayoutId() {return R.layout.sample_activity;}
	/*...*/
}

Extending from RosieActivity or RosieFragment is not mandatory. If your project is already extending from any other base Activity please review the class PresenterLifeCycleLinker and use it inside your base Activity or your Activities as follow:

public abstract class MyBaseActivity extends FragmentActivity
    implements RosiePresenter.View {

  private PresenterLifeCycleLinker presenterLifeCycleLinker = new PresenterLifeCycleLinker();

  @Override protected void onCreate(Bundle savedInstanceState) {
    ...
    presenterLifeCycleLinker.initializeLifeCycle(this, this);
    ...
  }
  @Override protected void onResume() {
    ...
    presenterLifeCycleLinker.updatePresenters(this);
    ...
  }

  @Override protected void onPause() {
    ...
    presenterLifeCycleLinker.pausePresenters();
    ...
  }

  @Override protected void onDestroy() {
    ...
    super.onDestroy();
    ...
  }

}

Rosie provides you some base classes to be extended and give you a quick access to the Dependency Injection and Model View Presenter features, but the usage of inheritance to use these features is not mandatory.

Dagger

Besides, you can define the Dagger dagger module that will contain the dependencies for your activity by overriding the getActivityScopeModules method:

public class SampleActivity extends RosieActivity {
	@Override protected List<Object> getActivityScopeModules() {
		return Arrays.asList((Object) new SampleModule());
	}
	/*...*/
}

There is also two useful annotations to provide Context dependencies into your classes, one for your Application context and one for your current Activity:

public class InjectedClass {
	@Inject public InjectedClass(@ForApplication Context applicationContext, @ForActivity Context activityContext) {
		/*...*/
	}
}

Presenter

To follow the MVP pattern, Rosie provides a RosiePresenter class that will be responsible for all your presentation logic. Rosie will take care of linking your view (a RosieActivity or RosieFragment implementation) with your presenter and subscribing it to its lifecycle. In order to do that, create a RosiePresenter and inject it into your activity/fragment with the @Presenter annotation:

public class SamplePresenter extends RosiePresenter<SamplePresenter.View> {
	public interface View extends RosiePresenter.View {
		/*...*/
	}
}
public class SampleActivity extends RosieActivity implements SamplePresenter.View {
	@Inject @Presenter SamplePresenter presenter;
	@Override protected void onPreparePresenter() {/*...*/}
}

Once both, view and presenter, are linked you can react to your view lifecycle directly from the presenter. The onPreparePresenter method gives you the opportunity to configure your presenter before any of these methods are called (e.g. with your intent parameters). You will be also able to call your view easily from the presenter:

public class SamplePresenter extends RosiePresenter<SamplePresenter.View> {
	protected void initialize() {/*...*/}
	protected void update() {/*...*/}
	protected void pause() {/*...*/}
	protected void destroy() {/*...*/}
	
	private void sampleMethod() {
		View view = getView(); // Get the view linked to the presenter
		view.foo();
	}
	
	public interface View extends RosiePresenter.View {
		void foo();
	}
}

To understand when the lifecycle methods are called take a look at the following table:

RosiePresenter Activity Fragment
initialize onCreate onViewCreated
update onResume onResume
pause onPause onPause
destroy onDestroy onDestroy

Domain

The domain package is meant to contain all your business logic that will change from app to app. For that reason, Rosie only provides a single RosieUseCase class that will help you execute your use cases in background following the command pattern.

To start using the Rosie domain package, create your use cases extending RosieUseCase and define the method containing your use case logic. Remember to call notifySuccess or notifyError to get results back to your presenter:

public class DoSomething extends RosieUseCase {
	@UseCase public void doSomething(Object arg) {
		Object response = getData();
		if (response.isOk()) {
			notifySuccess(response.getContent());
		} else {
			notifyError(response.getError());
		}
	}
}

To call your use case, create a UseCaseCall, configure it and execute it. Rosie gives you a fluent API to easily do this from your presenters:

public class SamplePresenter extends RosiePresenter<SamplePresenter.View> {
	public void callUseCase() {
		createUseCaseCall(doSomething)
			.args(arg /*, arg2, arg3, ...*/)
			.onSuccess(new OnSuccessCallback() {
				/*...*/
			})
			.onError(new OnErrorCallback() {
				/*...*/
			})
			.execute();
	}
	/*...*/
}

All the configuration calls are optional and can be omitted when not needed but keep in mind that provided arguments must match the ones declared in your UseCase method or an error will be raised. It is important to keep in mind that, by default, your callback methods will be executed in the main thread so you can easily update your UI from them.

Named use cases

Sometimes you need to specify use cases that are very similar to each other. To avoid creating multiple classes representing every use case configuration, you can create a single RosieUseCase class with multiple methods. Rosie will be able to identify the use case being called by matching its input parameters. If you create two methods with the @UseCase annotation that have the same input parameters, you can provide a name to each of them in order to identify them:

public class DoSomething extends RosieUseCase {
	public static final String USE_CASE_NAME = "UseCaseName";
	public static final String OTHER_USE_CASE_NAME = "OtherUseCaseName";
	@UseCase(name = USE_CASE_NAME) public void doSomething(Object arg) {/*...*/}
	@UseCase(name = OTHER_USE_CASE_NAME) public void doSomethingElse(Object arg) {/*...*/}
}

Even though using names is not mandatory when the method input parameters are different, it's highly recommended to use them just to make its usage more readable.

To call a named use case just configure its name:

public class SamplePresenter extends RosiePresenter<SamplePresenter.View> {
	public void callUseCase() {
		createUseCaseCall(doSomething)
			.args(arg)
			.useCaseName(DoSomething.USE_CASE_NAME)
			.execute();
	}
	/*...*/
}

Error handling

Errors can be either manually reported or implicitly notified when an exception is thrown from your use case context. To handle errors, there is a capturing event system that iterates over all your registered OnErrorCallback implementations and notifies them of the issue. Every callback needs to return a boolean value to inform whether the error has been handled and needs no further management. Callbacks are always called in this specific order:

  1. Use case specific callback; the ones you register while calling a use case.
  2. Global callbacks; the ones you can register from your presenter constructor.

With this approach you can create an error callback in your presenter that shows a generic error message to your user and create multiple other listeners for specific use cases and/or errors.

To register global callbacks from your presenter, call registerOnErrorCallback in the constructor:

public class SamplePresenter extends RosiePresenter<SamplePresenter.View> implements OnErrorCallback {
	public SamplePresenter(UseCaseHandler useCaseHandler) {
		super(useCaseHandler);
		registerOnErrorCallback(this);
	}

	@Override public boolean onError(Error error) {
		getView().showGenericError();
		return true;
	}
}

As explained in the UseCase section, you can also register error callbacks for a use case call. Rosie will assign those a higher priority:

public class SamplePresenter extends RosiePresenter<SamplePresenter.View> {
	public void callUseCase() {
		createUseCaseCall(doSomething)
			.args(arg)
			.onError(new OnErrorCallback() {
				getView().showSpecificError();
				// The error has been handled
				// Avoid calling any other error callback by returning true
				return true;
			})
			.execute();
	}
	/*...*/
}

You can create your own ErrorFactory implementation to map Exceptions to Errors. In your implementation you can unify your error handling.

public class CustomErrorFactory extends ErrorFactory {

	@Inject public CustomErrorFactory() {
	}

	@Override public Error create(Exception exception) {
		if (targetException instanceof MyConnectionException) {
			return new ConnectionError();
		}
		return new UnknownError();
  }
}

Remember to provide your ErrorFactory implementation inside a Dagger module.

	@Provides public ErrorHandler providesErrorHandler(CustomErrorFactory errorFactory) {
		return new ErrorHandler(errorFactory);
	}

Repository

The third layer is meant to encapsulate your data sources. To start using it just extend RosieRepository and configure its data sources in its constructor:

public class SampleRepository extends RosieRepository<Key, Value> {
	@Inject public SampleRepository(SampleApiDataSource sampleApiDataSource,
			SampleCacheDataSource sampleCacheDataSource) {
		addReadableDataSources(sampleApiDataSource);
		addCacheDataSources(sampleCacheDataSource);
	}
}

There are three different types of data sources and each one has its own interface that you can implement to use it in your repository:

  • ReadableDataSource<K, V>: Defines data sources where you can read values by a given key or retrieving them all.
  • WriteableDataSource<K, V>: Defines data sources where you can persist data with operations to add, update or delete values.
  • CacheDataSource<K, V>: Defines a mix of readable and writeable data sources to speed up access to your values.

There are empty implementations of each data source to simplify your own subclasses by overriding only the methods that make sense in your context. Besides, there is a generic InMemoryCacheDataSource to store recent values up to a configurable time.

public class SampleRepository extends RosieRepository<Key, Value> {
	@Inject public SampleRepository() {
	PaginatedCacheDataSource<Key, Value> inMemoryCacheDataSource =
		new InMemoryCacheDataSource<>(new TimeProvider(), MINUTES.toMillis(5));
	addCacheDataSources(inMemoryCacheDataSource);
  }
}

Users of the repositories can retrieve or modify data by using one of the multiple methods available for that matter. There are multiple ways to retrieve and store data using repositories, the following snippet shows some of the most useful:

// Initialization
Key key = /*...*/; Value value; Collection<Value> values;
RosieRepository<Key, Value> repository = /*...*/;

// Get a value by its key
value = repository.getByKey(key);
// Get a value by its key using only the defined cache
value = repository.getByKey(key, ReadPolicy.CACHE_ONLY);
// Get all the available values
values = repository.getAll();
// Get all the available values using only readable sources
values = repository.getAll(ReadPolicy.READABLE_ONLY);
// Add a new value
repository.addOrUpdate(value);
// Add a new value only to the first writeable data source that works
repository.addOrUpdate(value, WritePolicy.WRITE_ONCE);
// Delete a value by its key
repository.deleteByKey(key);
// Delete all values stored in the repository
repository.deleteAll();

Paginated repositories

Finally, Rosie gives you support for pagination in repositories. If your data is paginated, just extend PaginatedRosieRepository instead of RosieRepository. You will also need to implement your paginated data sources, PaginatedReadableDataSource<V> and PaginatedCacheDataSource<K, V>. Once your paginated repository is completely defined and configured you will be able to use it just as a regular repository with additional pagination-related methods:

// Initialization
Key key = /*...*/; PaginatedCollection<Value> page;
PaginatedRosieRepository<Key, Value> repository = /*...*/;

// Get a value by its key just as with a regular repository
Value value = repository.getByKey(key);
// Get a page
page = repository.getPage(Page.withOffsetAndLimit(offset, limit));
// Get a page using only the cache data source
page = repository.getPage(Page.withOffsetAndLimit(offset, limit), ReadPolicy.CACHE_ONLY);

To run the application using real data obtained from the Marvel API create a marvel.properties file inside the sample directory and add your public and private key there as follows:

MARVEL_PUBLIC_KEY="YOUR_MARVEL_PUBLIC_KEY"
MARVEL_PRIVATE_KEY="YOUR_MARVEL_PRIVATE_KEY"

Add it to your project

Include the library in your build.gradle

dependencies{
    compile 'com.karumi.rosie:rosie:3.0.2'
}

or to your pom.xml if you are using Maven

<dependency>
    <groupId>com.karumi.rosie</groupId>
    <artifactId>rosie</artifactId>
    <version>3.0.2</version>
    <type>aar</type>
</dependency>

More information?

We are writing some blog posts to explain the main motivations behind Rosie and some desing considerations:

Related projects

Do you want to contribute?

Feel free to report us or add any useful feature to the library, we will be glad to improve it with your help.

Keep in mind that your PRs must be validated by Travis-CI. Please, run a local build with ./gradlew checkstyle build connectedCheck before submitting your code.

Libraries used in this project

License

Copyright 2015 Karumi

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • Error when running a signed APK

    Error when running a signed APK

    Hi, I do not understand what is happening.

    • When I install the app from Android Studio with the debugkeystore by default, there is no problem.
    • When I sign the app with the Android Studio tool and my keystore, then I pass it to the phone, install the app, execute and ERROR: This object does not contain any use case to execute. Did you forget to add the @UseCase annotation?

    Why these two different behaviors with the same code?

    Has something similar happened to you?

    Thanks in advance

    opened by pinicius 17
  • Moving Butter Knife and Renderers libraries from Rosie to the sample project, updated sample butterknife version 7.0.1 -> 8.0.1

    Moving Butter Knife and Renderers libraries from Rosie to the sample project, updated sample butterknife version 7.0.1 -> 8.0.1

    There was a warning for using a @Nullable over a @OnClick method in MarvelActivity.java, I have updated Butterknife, replace that @Nullable for @Optional and updated all the @Bind annotation for @BindView. Fix #70

    opened by luis-ibanez 11
  • Add a new interface extending from RosiePresenter.View

    Add a new interface extending from RosiePresenter.View

    Some of the views we have to implement have a loading state. We are going to create a RosiePresenter.ViewWithLoading interface with two methods: showLoading and hideLoading to avoid code duplicity in all the library client code related to this classes.

    enhancement 
    opened by Serchinastico 10
  • New repositories approach

    New repositories approach

    This is my repositories approach, it doesn't bring anything new to the table, it's just a clean up and a better segregation of interfaces, to wrap up:

    Repository

    As in the old implementation, if you want to create your repositories just extend from this class. To configure your data sources, call addReadables, addWriteables, addCaches in your constructor:

      public SampleRepository(SampleReadable sampleReadable, SampleWriteable sampleWriteable,
          SampleInMemoryCache sampleInMemoryCache) {
        addReadables(sampleReadable);
        addWriteables(sampleWriteable);
        addCaches(sampleInMemoryCache);
      }
    

    To do special stuff, like reading only from cache, reading only fresh data, etc, there is a method with a ReadPolicy that let's you define those special cases.

    Readable, Writeable, Cache

    This is the separation of the previous DataSource interface. Readable has get and getAll methods, Writeable has addOrUpdate, addOrUpdateAll, delete and deleteAll methods. Finally, Cache just merges these two interfaces. There is still the problem of having to implement methods you might not need (I might only need to implement get and don't need to implement getAll). You can use one of the EmptyReadable, EmptyWriteable and EmptyCache classes which provides an empty implementation for every method.

    I did an MVP with an implementation based on annotations where data sources only need to annotate it's get method with a @Get annotation. If the annotation is not present then the repository goes to the following data source. I've done some profiling and the annotation-based implementation is 20 times slower, we are talking about repository calls of 7ms which I think it's not acceptable. We might use apt but it will take us longer.

    Pagination is solved in the same fashion as in the previous implementation.

    Let me know the parts you like, the parts you don't, and let's close the repositories.

    opened by Serchinastico 8
  • Feedback from DroidsOnRoids

    Feedback from DroidsOnRoids

    @flipper83 @Serchinastico we have some feedback about Rosie in this blog post: http://www.thedroidsonroids.com/blog/android/rosie-lets-dive-into-clean-architecture/ There are some things we could improve like: change some methods visibility in the RosiePresenter to improve the class testability from the unit test point of view. What do you think?

    question 
    opened by pedrovgs 6
  • Update Travis-CI configuration to use the new image they recommended us.

    Update Travis-CI configuration to use the new image they recommended us.

    As a bit more information, this new Android image comes with:

    • Android SDK 25.2.3
    • build-tools-25.0.2
    • The new sdkmanager tool - a command line tool that allows you to view, install, update, and uninstall packages for the Android SDK. Replaces the previous android tool, see https://developer.android.com/studio/tools/help/android.html
    • Also, the new Android image should be retro-compatible. See the full list of Android SDK components that can be specified in the .travis.yml file, including build-tools-26.0.0-preview.
    opened by pedrovgs 5
  • I have another question, How can I include third library in my arr like your rosie?

    I have another question, How can I include third library in my arr like your rosie?

    I have many third library in my android library like Rosie with dagger and Butter Knife.But my arr package not include this, so It can't reference this library.

    How can i build it let my arr include this third library?Thx

    question 
    opened by h3clikejava 5
  • Communication between presenters

    Communication between presenters

    First of all, thanks for share with community this great framework.

    I've a doubt about how can I communicate two presenters without using Event Bus solution. I've got an Editor activity with a presenter and multiple fragments, each one of them with a presenter, that be added to this activity in different steps of the process. How can the activity's presenter know when the user has clicked on fragment list item, if user's click event is handled by fragment's presenter?

    Thanks

    question 
    opened by pinicius 5
  • Marvel example

    Marvel example

    This branch includes some minor changes anticipating what we talked with Luis for the new designs

    • Add a toolbar in detail activities
    • Replace comic by comic series and add a grid of comics inside the comic series details window
    • Use native tabview
    opened by Serchinastico 5
  • Rosie incompatible with android.arch.lifecycle ¿?

    Rosie incompatible with android.arch.lifecycle ¿?

    Hi I'm trying to implement a Lifecycle observer using android arch components and it crashes, seems the cause is RosieApplication, android.arch is unable to get ApplicationContext, so crash because is null.

     java.lang.RuntimeException: Unable to get provider android.arch.lifecycle.ProcessLifecycleOwnerInitializer: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.Application.registerActivityLifecycleCallbacks(android.app.Application$ActivityLifecycleCallbacks)' on a null object reference
                                                              at android.app.ActivityThread.installProvider(ActivityThread.java:5156)
                                                              at android.app.ActivityThread.installContentProviders(ActivityThread.java:4748)
                                                              at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4688)
                                                              at android.app.ActivityThread.-wrap1(ActivityThread.java)
                                                              at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1405)
                                                              at android.os.Handler.dispatchMessage(Handler.java:102)
                                                              at android.os.Looper.loop(Looper.java:148)
                                                              at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                              at java.lang.reflect.Method.invoke(Native Method)
                                                              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
                                                           Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.Application.registerActivityLifecycleCallbacks(android.app.Application$ActivityLifecycleCallbacks)' on a null object reference
                                                              at android.arch.lifecycle.LifecycleDispatcher.init(LifecycleDispatcher.java:59)
                                                              at android.arch.lifecycle.ProcessLifecycleOwnerInitializer.onCreate(ProcessLifecycleOwnerInitializer.java:35)
                                                              at android.content.ContentProvider.attachInfo(ContentProvider.java:1748)
                                                              at android.content.ContentProvider.attachInfo(ContentProvider.java:1723)
                                                              at android.app.ActivityThread.installProvider(ActivityThread.java:5153)
                                                              at android.app.ActivityThread.installContentProviders(ActivityThread.java:4748) 
                                                              at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4688) 
                                                              at android.app.ActivityThread.-wrap1(ActivityThread.java) 
                                                              at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1405) 
                                                              at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                              at android.os.Looper.loop(Looper.java:148) 
                                                              at android.app.ActivityThread.main(ActivityThread.java:5417) 
                                                              at java.lang.reflect.Method.invoke(Native Method) 
                                                              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
                                                              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
    

    Do you know how can I integrate both parts?

    bug 
    opened by rubdottocom 4
  • How to connect a Activity to its Presenter without Injection?

    How to connect a Activity to its Presenter without Injection?

    I am not able to link my Activity witch extends from RosieAppCompatActivity, to my Presenter.

    When i try

    @Inject @Presenter
    WelcomePresenter presenter;
    

    I always get the error "The presenter instance to be registered can't be null".

    How do i link my activity to its presenter without the use of Injection?

    question 
    opened by matbrandao 4
  • Update gradle

    Update gradle

    :tophat: What is the goal?

    Update Gradle (tools, wrapper..) to improve the compilation time and take advantage of the last improvements in Android Studio 3.0. Also, update dependencies to follow new implementation/api instead of compile standard, that helps to improve compilation times

    How is it being implemented?

    • Follow this tutorial https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration?utm_source=android-studio#new_configurations
    • Add google() repository
    • Set compile version and target version to 27
    • Create variables in gradle to update the versions of the library and the samples at the same time

    GIF

    happy

    opened by Aracem 4
  • Allowing to implement RosiePresenter.View interfaces in base Fragments or Activity classes

    Allowing to implement RosiePresenter.View interfaces in base Fragments or Activity classes

    Hello guys! First of all thank you very much for Rosie. It has been very helpful in the project We are working on.

    I tried to implement my View interface in a Base Fragment class and I realized that was not possible because in RosiePresenter.class getViewInterfaceClass method was not looking for interfaces in any available super classes. I added a method that is called inside getViewInterfaceClass for seeking any RosiePresenter.View interfaces in any super class available if there is no RosiePresenter.View interfaces in the current class.

    PS: I ran the tests but I got this error while testing the app sample: android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: (with id: com.karumi.rosie.sample:id/snackbar_text and with text...

    Thank you!

    opened by felHR85 2
  • Stabilize Travis-CI builds

    Stabilize Travis-CI builds

    As we've seen for the last months Travis-CI is failing constantly due to problems initializing the Android emulator, even when our tests are not flacky at all. Travis-CI is one of the core components in this project, so we should try to stabilize our build in Travis-CI environments using this tool

    enhancement 
    opened by pedrovgs 2
  • OnErrorCallback null pointer exception with Marshmallow devices

    OnErrorCallback null pointer exception with Marshmallow devices

    Every time I call the method notifyError(new Error("Error")) in a UseCase class I'm getting a null pointer exception. It seems that when I create the " .onError(new OnErrorCallback() {...}" in the presenter something goes wrong."I have tried with two different devices using Marshmallow version and in both cases the result is:

    java.lang.NullPointerException: Attempt to invoke interface method 'boolean com.karumi.rosie.domain.usecase.error.OnErrorCallback.onError(java.lang.Error)' on a null object reference at com.karumi.rosie.domain.usecase.RosieUseCase.notifyError(RosieUseCase.java:77)

    Versions under Marshmallow works perfectly. Any tip about what is causing this error?

    Thanks in advance

    bug 
    opened by bcarrera 1
  • Extract caching logic from RosieRepository.java

    Extract caching logic from RosieRepository.java

    I think It's not cool to put the caching logic directly into the repository (RosieRepository.java) because it violates the Single Responsibility Principle. Now, the concerns of data access and caching policy is in the same class, and if either of these changes, you'll need to change the class.

    A better approach would be to use a Proxy pattern to apply the caching logic in a separate type like CachedRosieRepository.

    enhancement 
    opened by RPallas92 1
  • NoSuchMethodError. No ButterKnife.bind(Activity) method

    NoSuchMethodError. No ButterKnife.bind(Activity) method

    Hi,

    I got weird crash when extending RosieAppCompatActivity. http://crashes.to/s/129c15fd43c

    Fatal Exception: java.lang.NoSuchMethodError: No static method bind(Landroid/app/Activity;)V in class Lbutterknife/ButterKnife; or its super classes (declaration of 'butterknife.ButterKnife' appears in /data/app/com.anotherdev.photos500.debug-1/base.apk)
           at com.karumi.rosie.view.RosieAppCompatActivity.onCreate(RosieAppCompatActivity.java:50)
    

    Except from my dependencies:

    compile 'com.jakewharton:butterknife:8.0.1'
    apt 'com.jakewharton:butterknife-compiler:8.0.1'
    compile 'com.karumi.rosie:rosie:2.1.0'
    
    opened by anotherdev 1
Releases(3.0.2)
  • 3.0.2(Aug 3, 2018)

  • 3.0.1(Mar 16, 2018)

  • 3.0.0(Mar 7, 2017)

    With this release, we are removing some dependencies from the library to make it easier for you to import it into your projects! To be precise, we have removed Butterknife and Renderers.

    If you want to update to this version of the library, just follow the steps described in #86

    Source code(tar.gz)
    Source code(zip)
  • 2.1.2(Mar 6, 2017)

    This release can be considered a hotfix, we have reverted the removal of butterknife and renderers from the library itself. We will move that commit to version 3.0.0

    Source code(tar.gz)
    Source code(zip)
  • 2.1.1(Feb 20, 2017)

  • 2.1.0(Apr 4, 2016)

  • 2.0.0(Mar 28, 2016)

    Model View Presenter implementation improvements. Given all the feedback received related to the MVP implementation we have decided to update some code related to this feature in order to be able to use it without RosieActivity or RosieFragment.

    • Change PresenterLifeCycleLinker visibility to be public.
    • Change RosiePresenter lifecycle methods and setView method to be public and improve testability from the unit testing point of view.
    • Update some dependencies like Renderers or Priority Job Queue.
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Mar 11, 2016)

  • rosie-1.0.0(Feb 25, 2016)

Owner
Karumi
Karumi, the Rock Solid Code studio
Karumi
Pet project using Clean Architecture + MVVM + Reactive Extensions + Android Architecture Components. The data are fetched from LondonTheatreDirect API. 🎭

Theatre Pet project using Clean Architecture + MVVM + Reactive Extensions + Android Architecture Components. The data is fetched from LondonTheatreDir

André Mion 646 Jan 9, 2023
A small, yet full-featured framework that allows building View-based Android applications

Conductor A small, yet full-featured framework that allows building View-based Android applications. Conductor provides a light-weight wrapper around

BlueLine Labs 3.9k Jan 6, 2023
Source++ is an open-source live coding platform. Add breakpoints, logs, metrics, and tracing to live production applications

Source++ is an open-source live coding platform. Add breakpoints, logs, metrics, and distributed tracing to live production software in real-time on-d

Source++ 40 Dec 14, 2022
Create kotlin android project with one line of command.

README This is an android application template project built with kotlin language and some useful libraries. It provides a creator script to quickly c

nekocode 1.6k Dec 20, 2022
Create kotlin android project with one line of command.

README This is an android application template project built with kotlin language and some useful libraries. It provides a creator script to quickly c

nekocode 1.6k Dec 20, 2022
LiteOrm is a fast, small, powerful ORM framework for Android. LiteOrm makes you do CRUD operarions on SQLite database with a sigle line of code efficiently.

#LiteOrm:Android高性能数据库框架 A fast, small, powerful ORM framework for Android. LiteOrm makes you do CRUD operarions on SQLite database with a sigle line

马天宇 1.5k Nov 19, 2022
A data-binding Presentation Model(MVVM) framework for the Android platform.

PLEASE NOTE, THIS PROJECT IS NO LONGER BEING MAINTAINED. As personal time contraints, I am currently unable to keep up. Please use official android da

RoboBinding open source 1.3k Dec 9, 2022
Android Plugin Framework

Android Plugin Framework This project is pre-mature and may be changed very frequently. Introduction Android Plugin Framework (APF) aims to providing

Umeng Limited 322 Nov 17, 2022
MVVM framework for Android

RoboMVVM - MVVM Framework For Android RoboMVVM is an open source library that facilitates the use of the MVVM pattern in Android apps. The MVVM patter

Debdatta Basu 55 Nov 24, 2020
kotlin-core - A full framework for making Android apps. Based on Anko and Kotson.

kotlin-core This package is not Android-specific, and can be used across platforms. However, for a good example of use in Android, take a look at kotl

Lightning Kite 36 Oct 3, 2022
Cross-platform framework for building truly native mobile apps with Java or Kotlin. Write Once Run Anywhere support for iOS, Android, Desktop & Web.

Codename One - Cross Platform Native Apps with Java or Kotlin Codename One is a mobile first cross platform environment for Java and Kotlin developers

Codename One 1.4k Dec 23, 2022
🔥 Android component-based routing framework

README-CN Latest version module krouter-core krouter-compiler krouter-annotation krouter-plugin version Features 支持通过路由获取intent 支持方法注解,通过路由调用方法 支持给fra

Jiaming Gu 6 Jun 24, 2022
VasSonic is a lightweight and high-performance Hybrid framework developed by tencent VAS team, which is intended to speed up the first screen of websites working on Android and iOS platform.

VasSonic: A Lightweight And High-performance Hybrid Framework VasSonic is a lightweight and high-performance Hybrid framework developed by tencent VAS

Tencent 11.6k Dec 30, 2022
🔪 AOP development framework implemented through *Annotation + ASM + Gradle Transform API* for Android🤖

?? AOP development framework implemented through *Annotation + ASM + Gradle Transform API* for Android??

Pumpkin 325 Nov 22, 2022
UltimateAndroid is a rapid development framework for developing your apps

UltimateAndroid Version:0.10.2 UltimateAndroid is a rapid development framework for developing apps Master branch: Dev branch: V0.7.0 Ui Demo screensh

MarshalChen 2.1k Dec 26, 2022
A framework for hook java methods.

Legend Projects are out of date, plese move to: Whale Hook What is Legend? Legend is a Hook framework for Android Development, it allows you to Hook J

Lody 1.6k Dec 15, 2022
A modern framework for full stack web apps in Kotlin

Kobweb is an opinionated Kotlin framework for creating websites and web apps, built on top of Web Compose and inspired by Next.js and Chakra UI.

Varabyte 425 Jan 8, 2023
Android part of the Android Studio(IntellijIDEA) OkHttp Profiler plugin

OkHttpProfiler Android Library Created by LocaleBro.com - Android Localization Platform The OkHttp Profiler plugin can show requests from the OkHttp l

Ievgenii 261 Dec 8, 2022
Android common lib, include ImageCache, HttpCache, DropDownListView, DownloadManager, Utils and so on

android-common-lib 关于我,欢迎关注 微博:Trinea 主页:trinea.cn 邮箱:trinea.cn#gmail.com 微信:codek2 主要包括:缓存(图片缓存、预取缓存、网络缓存)、公共View(下拉及底部加载更多ListView、底部加载更多ScrollView、

Trinea 5k Dec 30, 2022