All Projects → GCX-HCI → Thirtyinch

GCX-HCI / Thirtyinch

Licence: apache-2.0
a MVP library for Android favoring a stateful Presenter

Programming Languages

java
68154 projects - #9 most used programming language

Projects that are alternatives of or similar to Thirtyinch

Androidproject
Android 技术中台,但愿人长久,搬砖不再有
Stars: ✭ 4,398 (+318.06%)
Mutual labels:  rxjava, rxjava2, mvvm, mvp
Rxlifecycle
Rx binding of stock Android Activities & Fragment Lifecycle, avoiding memory leak
Stars: ✭ 131 (-87.55%)
Mutual labels:  rxjava, rxjava2, activity, fragment
Android Clean Architecture Boilerplate
Apply clean architecture on Android
Stars: ✭ 141 (-86.6%)
Mutual labels:  rxjava, rxjava2, architecture, mvp
Rxbus
Android reactive event bus that simplifies communication between Presenters, Activities, Fragments, Threads, Services, etc.
Stars: ✭ 79 (-92.49%)
Mutual labels:  rxjava, rxjava2, activity, fragment
Viabus Architecture
让 Android 开发可以像流水线一样高效的,职责分离架构 ⚡ 不同于 MVP 的配置解耦,也不能和 似是而非 的 MVVM - Clean 同日而语。VIABUS 是世界范围内首个明确提出,通过职责分离,来真正实现 UI 和 业务并行开发的 Android 项目级开发架构和设计模式理念。
Stars: ✭ 485 (-53.9%)
Mutual labels:  rxjava, architecture, mvvm, mvp
TpHulk
androidx,mvp,mvvm,jetpack
Stars: ✭ 19 (-98.19%)
Mutual labels:  mvp, mvvm, rxjava2
T Mvp
Android AOP Architecture by Apt, AspectJ, Javassisit, based on Realm+Databinding+MVP+Retrofit+Rxjava2
Stars: ✭ 2,740 (+160.46%)
Mutual labels:  rxjava, rxjava2, mvp
Rx.observe
Transform any method to an Rx Observable ! (VIPER)
Stars: ✭ 34 (-96.77%)
Mutual labels:  rxjava, rxjava2, architecture
Android Mvp Architecture
This repository contains a detailed sample app that implements MVP architecture using Dagger2, GreenDao, RxJava2, FastAndroidNetworking and PlaceholderView
Stars: ✭ 4,360 (+314.45%)
Mutual labels:  rxjava, rxjava2, mvp
Mvvm Kotlin Android Architecture
MVVM + Kotlin + Retrofit2 + Hilt + Coroutines + Kotlin Flow + mockK + Espresso + Junit5
Stars: ✭ 1,014 (-3.61%)
Mutual labels:  rxjava, mvvm, mvp
Karchi
Repository that showcases 3 different Android app architectures, all with Java and Kotlin versions: "Standard Android", MVP and MVVM. The exact same app is built 6 times following the different patterns.
Stars: ✭ 20 (-98.1%)
Mutual labels:  architecture, mvvm, mvp
iMoney
iMoney 金融项目
Stars: ✭ 55 (-94.77%)
Mutual labels:  rxjava, mvp, rxjava2
Relax
☘☘Relax 基于Kotlin语言编写的一套组件化框架,不紧整体组件化、内部也高度组件化🎋你可配置MVP、MVVM的开发模式、也可以配置所需要的业务组件🍁🍁
Stars: ✭ 253 (-75.95%)
Mutual labels:  rxjava, mvvm, mvp
Kotlinmvp
🔥 基于Kotlin+MVP+Retrofit+RxJava+Glide 等架构实现短视频类小项目,简约风格及详细注释,欢迎 star or fork!
Stars: ✭ 3,488 (+231.56%)
Mutual labels:  rxjava, rxjava2, mvp
Android Mvvm Architecture
This repository contains a detailed sample app that implements MVVM architecture using Dagger2, Room, RxJava2, FastAndroidNetworking and PlaceholderView
Stars: ✭ 2,720 (+158.56%)
Mutual labels:  rxjava, rxjava2, mvvm
Androidbasemvp
🚀一个快速搭建MVP+RxJava2+Retrofit 基础框架,主要是封装有Http网络请求、日志、缓存、加载等待、toast、页面状态布局管理、权限、RxBus、Glide图片加载等组件,方便快速开发新项目、减少开发成本。
Stars: ✭ 184 (-82.51%)
Mutual labels:  rxjava, rxjava2, mvp
Devutils
🔥 ( 持续更新,目前含 160+ 工具类 ) DevUtils 是一个 Android 工具库,主要根据不同功能模块,封装快捷使用的工具类及 API 方法调用。该项目尽可能的便于开发人员,快捷、高效开发安全可靠的项目。
Stars: ✭ 680 (-35.36%)
Mutual labels:  mvvm, mvp, activity
Android Kotlin Mvp Architecture
This repository contains a detailed sample app that implements MVP architecture in Kotlin using Dagger2, Room, RxJava2, FastAndroidNetworking and PlaceholderView
Stars: ✭ 615 (-41.54%)
Mutual labels:  rxjava, rxjava2, mvp
Ribs
Uber's cross-platform mobile architecture framework.
Stars: ✭ 6,641 (+531.27%)
Mutual labels:  architecture, mvvm, mvp
Jd Mall Master
一款高仿京东商城的UI,基于MVP的Retrofit2(okhttp3)+rxjava+dagger2+greendao+glide。该项目系仿京东商城,属于独立开发者作品,仅供参考学习,拒绝做一切商业用途,如有侵权,请告知删除
Stars: ✭ 151 (-85.65%)
Mutual labels:  rxjava, rxjava2, mvp

DEPRECATED - no longer actively maintained

Build Status License Gitter

ThirtyInch - a MVP library for Android

This library adds Presenters to Activities and Fragments. It favors the stateful Presenter pattern, where the Presenter survives configuration changes and dumb View pattern, where the View only sends user events and receives information from the Presenter but never actively asks for data. This makes testing very easy because no logic lives in the View (Activity, Fragment) except for fancy animations which anyways aren't testable.

The name

Keep Android At Arm’s Length

— Kevin Schultz, Droidcon NYC '14

The perfect distance to the Android Framework is approximately thirty inches, the average length of the human arm, shoulder to fingertips.

Story

Read the introduction article on Medium

See the slides of the latest talk on Speakerdeck

Get it

GitHub Packages

repositories {
    maven {
        url = uri("https://maven.pkg.github.com/GCX-HCI/ThirtyInch")
    }
}

dependencies {
    implementation "net.grandcentrix.thirtyinch:thirtyinch:$thirtyinchVersion"
    implementation "net.grandcentrix.thirtyinch:thirtyinch-rx2:$thirtyinchVersion"
    implementation "net.grandcentrix.thirtyinch:thirtyinch-logginginterceptor:$thirtyinchVersion"
    implementation "net.grandcentrix.thirtyinch:thirtyinch-kotlin:$thirtyinchVersion"
    implementation "net.grandcentrix.thirtyinch:thirtyinch-kotlin-coroutines:$thirtyinchVersion"
    
    // Legacy dependencies
    implementation "net.grandcentrix.thirtyinch:thirtyinch-rx:$thirtyinchVersion"
}

JCenter (deprecated)

repositories {
    jcenter()
}

dependencies {
    implementation "net.grandcentrix.thirtyinch:thirtyinch:$thirtyinchVersion"
    implementation "net.grandcentrix.thirtyinch:thirtyinch-rx2:$thirtyinchVersion"
    implementation "net.grandcentrix.thirtyinch:thirtyinch-logginginterceptor:$thirtyinchVersion"
    implementation "net.grandcentrix.thirtyinch:thirtyinch-kotlin:$thirtyinchVersion"
    implementation "net.grandcentrix.thirtyinch:thirtyinch-kotlin-coroutines:$thirtyinchVersion"
    
    // Lagacy dependencies
    implementation "net.grandcentrix.thirtyinch:thirtyinch-rx:$thirtyinchVersion"
}

Hello World MVP example with ThirtyInch

HelloWorldActivity.java

public class HelloWorldActivity 
        extends TiActivity<HelloWorldPresenter, HelloWorldView> 
        implements HelloWorldView {

    private TextView mOutput;

    @NonNull
    @Override
    public HelloWorldPresenter providePresenter() {
        return new HelloWorldPresenter();
    }

    @Override
    public void showText(final String text) {
        mOutput.setText(text);
    }

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hello_world);

        mOutput = (TextView) findViewById(R.id.output);
    }
}

HelloWorldView.java

public interface HelloWorldView extends TiView {

    @CallOnMainThread
    void showText(final String text);
}

HelloWorldPresenter.java

public class HelloWorldPresenter extends TiPresenter<HelloWorldView> {

    @Override    
    protected void onAttachView(@NonNull final HelloWorldView view) {
        super.onAttachView(view);
        view.showText("Hello World!");
    }
}

ThirtyInch features

Presenter

  • survives configuration changes
  • survives when the Activity got killed in background
  • is not a singleton
  • dies when the Activity gets finished
Lifecycle

The TiPresenter lifecycle is very easy.

It can be CREATED and DESTROYED. The corresponding callbacks onCreate() and onDestroy() will be only called once!

The TiView can either be ATTACHED or DETACHED. The corresponding callbacks are onAttachView(TiView) and onDetachView() which maps to onStart() and onStop().

public class MyPresenter extends TiPresenter<MyView> {

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

    @Override
    protected void onAttachView(@NonNull final HelloWorldView view) {
        super.onAttachView(view);
    }

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

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

The lifecycle can be observed using TiLifecycleObserver

There is no callback for onResume() and onPause() in the TiPresenter. This is something the view layer should handle. Read more about this here Hannes Dorfmann - Presenters don't need lifecycle events

Configuration

The default behaviour might not fit your needs. You can disable unwanted features by providing a configuration in the TiPresenter constructor.

public class HelloWorldPresenter extends TiPresenter<HelloWorldView> {

    public static final TiConfiguration PRESENTER_CONFIG = 
            new TiConfiguration.Builder()
                .setRetainPresenterEnabled(true) 
                .setCallOnMainThreadInterceptorEnabled(true)
                .setDistinctUntilChangedInterceptorEnabled(true)
                .build();
            
    public HelloWorldPresenter() {
        super(PRESENTER_CONFIG);
    }
}

Or globally for all TiPresenters

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        TiPresenter.setDefaultConfig(MY_DEFAULT_CONFIG);
    }
}

TiView Annotations

Two awesome annotations for the TiView interface made it already into Ti saving you a lot of time.

public interface HelloWorldView extends TiView {

    @CallOnMainThread
    @DistinctUntilChanged
    void showText(final String text);
}
@CallOnMainThread

Whenever you call this method it will be called on the Android main thread. This allows to run code off the main thread but send events to the UI without dealing with Handlers and Loopers.

Requires to be a void method. Works only for TiView interfaces implemented by "Android Views" (TiActivity, TiFragment).

Enabled by default, can be disabled with the TiConfiguration

@DistinctUntilChanged

When calling this method the View receives no duplicated calls. The View swallows the second call when a method gets called with the same (hashcode) parameters twice.

Usecase: The Presenter binds a huge list to the View. The app loses focus (onDetachView()) and the exact same Activity instance gains focus again (onAttachView(view)). The Activity still shows the huge list. The Presenter binds the huge list again to the View. When the data has changed the list will be updated. When the data hasn't changed the call gets swallowed and prevents flickering.

Requires to be a void method and has at least one parameter.

Enabled by default, can be disabled with the TiConfiguration

View binding interceptors

View Annotations only work because ThirtyInch supports interceptors. Add interceptors (BindViewInterceptor) to TiActivity or TiFragment to intercept the binding process from TiView to TiPresenter. Interceptors are public API waiting for other great ideas.

public class HelloWorldActivity extends TiActivity<HelloWorldPresenter, HelloWorldView>
        implements HelloWorldView {

    public HelloWorldActivity() {
        addBindViewInterceptor(new LoggingInterceptor());
    }
}

LoggingInterceptor is available as module and logs all calls to the view.

Kotlin

Using Kotlin these days is a no-brainer. ThirtyInch provides some extension methods to improve itself even further!

SendToView

When using sendToView, repeating it.* inside the lambda is quite annoying. It's clear that the methods are called on the view. With the kotlin extension deliverToView the TiView will be give over to the lambda as this.

class HelloWorldPresenter : TiPresenter<HelloWorldView> {

  override fun onCreate() {
      // normal java API
      sendToView {
          it.showText("Hello World")
      }
      
      // kotlin extension
      deliverToView {
          showText("Hello World")
      }
  }
}

interface HelloWorldView : TiView {
    fun showText(text: String)
}

Back in the Java days we had to use it inside the sendToView-lambda.

Coroutines

If you're using Kotlin's Coroutines we offer a CoroutineScope that scopes to a presenter's lifecycle.

class HelloWorldPresenter : TiPresenter<HelloWorldView> {

  private val scope = TiCoroutineScope(this, Dispatchers.Default)

  override fun onCreate() {
      scope.launch { ... }
  }
}

The created Job will automatically be cancelled when the presenter is destroyed.

Alternatively, you can launch jobs that get cancelled when a TiView detaches:

class HelloWorldPresenter : TiPresenter<HelloWorldView> {

  private val scope = TiCoroutineScope(this, Dispatchers.Default)

  override fun onAttachView(view: HelloWorldView) {
      scope.launchUntilViewDetaches { ... }
  }
}

However, be careful that launchUntilViewDetaches can only be called when there is a view attached!

RxJava

Using RxJava for networking is very often used. Observing a Model is another good usecase where Rx can be used inside of a TiPresenter. The Rx package provides helper classes to deal with Subscription or wait for an attached TiView.

public class HelloWorldPresenter extends TiPresenter<HelloWorldView> {

    // add the subscription helper to your presenter
    private RxTiPresenterSubscriptionHandler rxHelper = new RxTiPresenterSubscriptionHandler(this);

    @Override
    protected void onCreate() {
        super.onCreate();
        
        // automatically unsubscribe in onDestroy()
        rxHelper.manageSubscription(
                Observable.interval(0, 1, TimeUnit.SECONDS)
                    // cache the latest value when no view is attached
                    // emits when the view got attached
                    .compose(RxTiPresenterUtils.<Long>deliverLatestToView(this))
                    .subscribe(uptime -> getView().showPresenterUpTime(uptime))
        );
    }

    @Override
    protected void onAttachView(@NonNull final HelloWorldView view) {
        super.onAttachView(view);
        
        // automatically unsubscribe in onDetachView(view)
        rxHelper.manageViewSubscription(anotherObservable.subscribe());
    }
}

You can make Disposable handling even less intrusive in Kotlin. Just create the following interface and make your presenters implement it:

interface DisposableHandler {

    // Initialize with reference to your TiPresenter instance
    val disposableHandler: RxTiPresenterDisposableHandler

    // Dispose of Disposables dependent on the TiPresenter lifecycle
    fun Disposable.disposeWhenDestroyed(): Disposable = disposableHandler.manageDisposable(this)

    // Dispose of Disposables dependent on the TiView attached/detached state
    fun Disposable.disposeWhenViewDetached(): Disposable = disposableHandler.manageViewDisposable(this)
} 

Then just implement the interface in your presenter and you can use created extension functions to manage Disposables:

class MyPresenter : TiPresenter<MyView>(), DisposableHandler {

    override val disposableHandler = RxTiPresenterDisposableHandler(this)

    override fun onCreate() {
        super.onCreate()

        // Presenter lifecycle dependent Disposable
        myObservable
            .subscribe()
            .disposeWhenDestroyed()
    }

    override fun onAttachView(view: MyView) {
        super.onAttachView(view)

        // View attached/detached dependent Disposable
        myViewObservable
            .subscribe()
            .disposeWhenViewDetached()
    }
}

License

Copyright 2016 grandcentrix GmbH

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.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].