{"id":13437823,"url":"https://github.com/square/mortar","last_synced_at":"2025-12-30T14:50:14.356Z","repository":{"id":11725288,"uuid":"14247776","full_name":"square/mortar","owner":"square","description":"A simple library that makes it easy to pair thin views with dedicated controllers, isolated from most of the vagaries of the Activity life cycle.","archived":false,"fork":false,"pushed_at":"2023-03-18T23:32:22.000Z","size":884,"stargazers_count":2157,"open_issues_count":33,"forks_count":156,"subscribers_count":104,"default_branch":"master","last_synced_at":"2024-10-01T16:20:16.603Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/square.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2013-11-09T00:01:50.000Z","updated_at":"2024-08-13T18:56:05.000Z","dependencies_parsed_at":"2022-09-26T21:51:18.824Z","dependency_job_id":"2075bcbd-ebc1-4709-878f-5540b0d756ea","html_url":"https://github.com/square/mortar","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/square%2Fmortar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/square%2Fmortar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/square%2Fmortar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/square%2Fmortar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/square","download_url":"https://codeload.github.com/square/mortar/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219846720,"owners_count":16556429,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T03:01:00.503Z","updated_at":"2025-12-18T00:05:48.510Z","avatar_url":"https://github.com/square.png","language":"Java","funding_links":[],"categories":["Java","Table of content"],"sub_categories":["Repos"],"readme":"# Mortar\n\n## Deprecated\n\nMortar had a good run and served us well, but new use is strongly discouraged. The app suite at Square that drove its creation is in the process of replacing Mortar with [Square Workflow](https://square.github.io/workflow/).\n\n## What's a Mortar?\n\nMortar provides a simplified, composable overlay for the Android lifecycle,\nto aid in the use of [Views as the modular unit of Android applications][rant].\nIt leverages [Context#getSystemService][services] to act as an a la carte supplier\nof services like dependency injection, bundle persistence, and whatever else\nyour app needs to provide itself.\n\nOne of the most useful services Mortar can provide is its [BundleService][bundle-service],\nwhich gives any View (or any object with access to the Activity context) safe access to\nthe Activity lifecycle's persistence bundle. For fans of the [Model View Presenter][mvp]\npattern, we provide a persisted [Presenter][presenter] class that builds on BundleService.\nPresenters are completely isolated from View concerns. They're particularly good at\nsurviving configuration changes, weathering the storm as Android destroys your portrait\nActivity and Views and replaces them with landscape doppelgangers.\n\nMortar can similarly make [Dagger][dagger] ObjectGraphs (or [Dagger2][dagger2]\nComponents) visible as system services. Or not \u0026mdash; these services are\ncompletely decoupled.\n\nEverything is managed by [MortarScope][scope] singletons, typically\nbacking the top level Application and Activity contexts. You can also spawn\nyour own shorter lived scopes to manage transient sessions, like the state of\nan object being built by a set of wizard screens.\n\n\u003c!-- \n  This example is a little bit confusing. Maybe explain why you would want to have an extended graph for a wizard, then explain how Mortar shadows the parent graph with that extended graph.\n--\u003e\n\nThese nested scopes can shadow the services provided by higher level scopes.\nFor example, a [Dagger extension graph][ogplus] specific to your wizard session\ncan cover the one normally available, transparently to the wizard Views.\nCalls like `ObjectGraphService.inject(getContext(), this)` are now possible\nwithout considering which graph will do the injection.\n\n## The Big Picture\n\nAn application will typically have a singleton MortarScope instance.\nIts job is to serve as a delegate to the app's `getSystemService` method, something like:\n\n```java\npublic class MyApplication extends Application {\n  private MortarScope rootScope;\n\n  @Override public Object getSystemService(String name) {\n    if (rootScope == null) rootScope = MortarScope.buildRootScope().build(getScopeName());\n\n    return rootScope.hasService(name) ? rootScope.getService(name) : super.getSystemService(name);\n  }\n}\n```\n\nThis exposes a single, core service, the scope itself. From the scope you can\nspawn child scopes, and you can register objects that implement the\n[Scoped](https://github.com/square/mortar/blob/master/mortar/src/main/java/mortar/Scoped.java#L18)\ninterface with it for setup and tear-down calls.\n\n  * `Scoped#onEnterScope(MortarScope)`\n  * `Scoped#onExitScope(MortarScope)`\n\nTo make a scope provide other services, like a [Dagger ObjectGraph][og], \nyou register them while building the scope. That would make our Application's\n`getSystemService` method look like this:\n\n```java\n  @Override public Object getSystemService(String name) {\n    if (rootScope == null) {\n      rootScope = MortarScope.buildRootScope()\n        .with(ObjectGraphService.SERVICE_NAME, ObjectGraph.create(new RootModule()))\n        .build(getScopeName());\n    }\n\n    return rootScope.hasService(name) ? rootScope.getService(name) : super.getSystemService(name);\n  }\n```\n\nNow any part of our app that has access to a `Context` can inject itself:\n\n```java\npublic class MyView extends LinearLayout {\n  @Inject SomeService service;\n\n  public MyView(Context context, AttributeSet attrs) {\n    super(context, attrs);\n    ObjectGraphService.inject(context, this);\n  }\n}\n```\n\nTo take advantage of the BundleService describe above, you'll put similar code\ninto your Activity. If it doesn't exist already, you'll\nbuild a sub-scope to back the Activity's `getSystemService` method, and \nwhile building it set up the `BundleServiceRunner`. You'll also notify \nthe BundleServiceRunner each time `onCreate` and `onSaveInstanceState` are \ncalled, to make the persistence bundle available to the rest of the app. \n\n```java\npublic class MyActivity extends Activity {\n  private MortarScope activityScope;\n\n  @Override public Object getSystemService(String name) {\n    MortarScope activityScope = MortarScope.findChild(getApplicationContext(), getScopeName());\n\n    if (activityScope == null) {\n      activityScope = MortarScope.buildChild(getApplicationContext()) //\n          .withService(BundleServiceRunner.SERVICE_NAME, new BundleServiceRunner())\n          .withService(HelloPresenter.class.getName(), new HelloPresenter())\n          .build(getScopeName());\n    }\n\n    return activityScope.hasService(name) ? activityScope.getService(name)\n        : super.getSystemService(name);\n  }\n\n  @Override protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    BundleServiceRunner.getBundleServiceRunner(this).onCreate(savedInstanceState);\n    setContentView(R.layout.main_view);\n  }\n\n  @Override protected void onSaveInstanceState(Bundle outState) {\n    super.onSaveInstanceState(outState);\n    BundleServiceRunner.getBundleServiceRunner(this).onSaveInstanceState(outState);\n  }\n}\n```\n\nWith that in place, any object in your app can sign up with the `BundleService`\nto save and restore its state. This is nice for views, since Bundles are less\nof a hassle than the `Parcelable` objects required by `View#onSaveInstanceState`,\nand a boon to any business objects in the rest of your app. \n\nDownload\n--------\n\nDownload [the latest JAR][jar] or grab via Maven:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.squareup.mortar\u003c/groupId\u003e\n    \u003cartifactId\u003emortar\u003c/artifactId\u003e\n    \u003cversion\u003e(insert latest version)\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nGradle:\n\n```groovy\ncompile 'com.squareup.mortar:mortar:(latest version)'\n```\n\n## Full Disclosure\n\nThis stuff has been in \"rapid\" development over a pretty long gestation period, \nbut is finally stabilizing. We don't expect drastic changes before cutting a\n1.0 release, but we still cannot promise a stable API from release to release.\n\nMortar is a key component of multiple Square apps, including our flagship\n[Square Register][register] app.\n\nLicense\n--------\n\n    Copyright 2013 Square, Inc.\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n\n[bundle-service]: https://github.com/square/mortar/blob/master/mortar/src/main/java/mortar/bundler/BundleService.java\n[mvp]: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter\n[dagger]: http://square.github.io/dagger/\n[dagger2]: http://google.github.io/dagger/\n[jar]: http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy\u0026g=com.squareup.mortar\u0026a=mortar\u0026v=LATEST\n[og]: https://square.github.io/dagger/1.x/dagger/dagger/ObjectGraph.html\n[ogplus]: https://github.com/square/dagger/blob/dagger-parent-1.1.0/core/src/main/java/dagger/ObjectGraph.java#L96\n[presenter]: https://github.com/square/mortar/blob/master/mortar/src/main/java/mortar/Presenter.java\n[rant]: http://corner.squareup.com/2014/10/advocating-against-android-fragments.html\n[register]: https://play.google.com/store/apps/details?id=com.squareup\n[scope]: https://github.com/square/mortar/blob/master/mortar/src/main/java/mortar/MortarScope.java\n[services]: http://developer.android.com/reference/android/content/Context.html#getSystemService(java.lang.String)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsquare%2Fmortar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsquare%2Fmortar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsquare%2Fmortar/lists"}