{"id":13613613,"url":"https://github.com/rh-id/a-navigator","last_synced_at":"2026-03-06T02:42:48.870Z","repository":{"id":45830249,"uuid":"409246851","full_name":"rh-id/a-navigator","owner":"rh-id","description":"a pure java navigation framework for Android projects.","archived":false,"fork":false,"pushed_at":"2024-12-17T02:13:53.000Z","size":503,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-02T14:40:02.270Z","etag":null,"topics":["android","android-library","android-navigation","android-navigation-java","navigation"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rh-id.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-22T14:57:00.000Z","updated_at":"2024-12-17T02:13:57.000Z","dependencies_parsed_at":"2024-11-07T21:34:11.924Z","dependency_job_id":"3f518867-2a29-4d39-ba1b-e3934a6096b9","html_url":"https://github.com/rh-id/a-navigator","commit_stats":null,"previous_names":[],"tags_count":70,"template":false,"template_full_name":null,"purl":"pkg:github/rh-id/a-navigator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh-id%2Fa-navigator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh-id%2Fa-navigator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh-id%2Fa-navigator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh-id%2Fa-navigator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rh-id","download_url":"https://codeload.github.com/rh-id/a-navigator/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh-id%2Fa-navigator/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266808517,"owners_count":23987449,"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","status":"online","status_checked_at":"2025-07-24T02:00:09.469Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["android","android-library","android-navigation","android-navigation-java","navigation"],"created_at":"2024-08-01T20:00:51.679Z","updated_at":"2026-03-06T02:42:48.825Z","avatar_url":"https://github.com/rh-id.png","language":"Java","funding_links":[],"categories":["Android"],"sub_categories":[],"readme":"# a-navigator\n\n![Languages](https://img.shields.io/github/languages/top/rh-id/a-navigator)\n![JitPack](https://img.shields.io/jitpack/v/github/rh-id/a-navigator)\n![Downloads](https://jitpack.io/v/rh-id/a-navigator/week.svg)\n![Downloads](https://jitpack.io/v/rh-id/a-navigator/month.svg)\n![Android CI](https://github.com/rh-id/a-navigator/actions/workflows/gradlew-build.yml/badge.svg)\n\nThis is a navigation framework for Android projects.\nThis framework doesn't follow common Model View View Model (MVVM) approach.\nIt follows what in my opinion called Model Stateful View (MSV) approach.\nIn MVVM pattern, ViewModel hold the state and \"glue\" both UI and business logic, and Activity/Fragment handles View and lifecycle logic.\nIn MSV pattern, StatefulView hold state, \"glue\", View creation, and Lifecycle when necessary.\n\nTo put it simply, imagine Fragment and ViewModel as one component, a fragment that hold its state AND with an easy to use navigator.\nOne navigator controls one activity.\n\nNested navigator supported through `INavigator.createViewNavigator`.\nyou will need to have one navigator as root and setup view navigator by calling that method.\n\nThis navigator can handle Jetpack Compose View as well,\nsee `AppCompatExampleActivity.java` and `ExampleComposePage.kt` for example implementation.\nIt is best to use Java language rather than Kotlin when using this library especially when save state is enabled.\nIf you are not planning to use save state then it should be fine.\n\nThe StatefulViews and its stack can survive process death.\nAny fields that are serializable and not marked as `transient` will be saved into the save state file following java object serialization standard.\nBecause of this you do not need to think on how to save and restore state using saveStateInstance bundle as long as java object serialization standards are followed.\n\n## Example Usage\n\nFor example usage ![see project example](https://github.com/rh-id/a-navigator/tree/master/example), for example production app see ![a-news-provider](https://github.com/rh-id/a-news-provider)\n\nThis project support jitpack, in order to use this, you need to add jitpack to your project root build.gradle:\n```\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven { url \"https://jitpack.io\" }\n    }\n}\n```\n\nInclude this to your module dependency (module build.gradle)\n```\ndependencies {\n    // this will include navigator module and all its extension module\n    implementation 'com.github.rh-id:a-navigator:v0.0.1'\n    \n    // use these if you want the navigator with its extension separately\n    implementation 'com.github.rh-id.a-navigator:a-navigator:v0.0.1'\n    implementation 'com.github.rh-id.a-navigator:a-navigator-extension-dialog:v0.0.1'\n}\n```\n\nNext for code part, create a home page by extending `StatefulView` (see example package in example folder)\n\n```\npublic class HomePage extends StatefulView\u003cActivity\u003e {\n\n    @Override\n    protected void initState(Activity activity) {\n        // init your state here\n    }\n\n    @Override\n    protected View createView(Activity activity) {\n        // inflate and setup your view here\n        View view = activity.getLayoutInflater().inflate(R.layout.page_home, null, false);\n        return view;\n    }\n\n    @Override\n    public void dispose(Activity activity) {\n        // do cleanup when navigator pop this StatefulView from stack\n    }\n}\n```\n\nInitialize on your application for global access\n\n```\npublic class MyApplication extends Application {\n\n    private Navigator\u003cMainActivity, StatefulView\u003cActivity\u003e\u003e\n                mainActivityNavigator;\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        Map\u003cString, StatefulViewFactory\u003cMainActivity, StatefulView\u003cActivity\u003e\u003e\u003e navMap = new HashMap\u003c\u003e();\n        // this is where you map all your StatefulView implementations\n        navMap.put(\"/\", (args, activity) -\u003e new HomePage());\n\n        // make sure to set initial route to home page which is \"/\"\n        NavConfiguration.Builder\u003cMainActivity, StatefulView\u003cActivity\u003e\u003e navBuilder = new NavConfiguration.Builder\u003c\u003e(\"/\", navMap);\n\n        // set custom File location to save state \n        navBuilder.setSaveStateFile(new File(getCacheDir(), \"navigator1State\"));\n\n        NavConfiguration\u003cMainActivity, StatefulView\u003cActivity\u003e\u003e navConfiguration =\n                navBuilder.build();\n        mainActivityNavigator =\n                new Navigator\u003c\u003e(MainActivity.class, navConfiguration);\n\n        // make sure to register navigator as callbacks to work properly\n        registerActivityLifecycleCallbacks(mainActivityNavigator);\n        registerComponentCallbacks(mainActivityNavigator);\n\n        // Extra example setup if you have nested navigator, for example when using bottom navigation\n        Map\u003cString, StatefulViewFactory\u003cRawActivity, StatefulView\u003cActivity\u003e\u003e\u003e bottomPageMap = new HashMap\u003c\u003e();\n        bottomPageMap.put(\"/\", (args, activity) -\u003e new BottomHomePage());\n        bottomPageMap.put(\"/page1\", (args, activity) -\u003e new Bottom1Page());\n        bottomPageMap.put(\"/page2\", (args, activity) -\u003e new Bottom2Page());\n        NavConfiguration.Builder\u003cRawActivity, StatefulView\u003cActivity\u003e\u003e navBuilderBottom = new NavConfiguration.Builder\u003c\u003e(\"/\", bottomPageMap);\n        // you could also set custom save state file for this nested/view navigator\n        navBuilderBottom.setSaveStateFile(new File(getCacheDir(), \"navigatorBottomState\"));\n        navigator.createViewNavigator(navBuilderBottom.build(), R.id.unique_container1);\n\n    }\n\n    public Navigator\n        getNavigator(Activity activity) {\n            if (activity instanceof MainActivity) {\n                return mainActivityNavigator;\n            }\n            return null;\n        }\n}\n```\n\nConfigure your main activity to allow the navigator to listen when back button is pressed\n\n```\npublic class MainActivity extends AppCompatActivity {\n    @Override\n    protected void onResume() {\n        super.onResume();\n        // this is required to let navigator handle the back button\n        getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) {\n            @Override\n            public void handleOnBackPressed() {\n                MyApplication.of(MainActivity.this)\n                        .getNavigator(MainActivity.this).onBackPressed();\n            }\n        });\n    }\n}\n```\n\nIf you are not extending `AppCompatActivity` you need to configure it like this:\n\n```\n/**\n * Example to use the framework by extending Activity directly\n */\npublic class MainActivity extends Activity {\n\n    @Override\n    public void onBackPressed() {\n        // this is required to let navigator handle the back button\n        MyApplication.of(this).getNavigator(this).onBackPressed();\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        // this is required to let navigator handle onActivityResult\n        MyApplication.of(this).getNavigator(this).onActivityResult(requestCode, resultCode, data);\n    }\n}\n```\nThis framework also support injection by annotating fields with `@NavInject`\n```\npublic class HomePage extends StatefulView\u003cActivity\u003e {\n\n    // navigator will be injected before initState is being called\n    @NavInject\n    private transient INavigator mNavigator;\n\n    @NavInject\n    private StatefulView mReuseStatefulView;\n    // If save state is enabled, \n    // any serializable field will be saved to the file\n    // and restored when deserialized\n    private String mExampleStringField;\n\n    // this can be injected when setting up NavConfiguration with the required component\n    @NavInject\n    private transient MyGlobalComponent mMyGlobalComponent;\n\n    public HomePage(){\n        // still need to instantiate StatefulView manually,\n        // navigator will only inject if this StatefulView is not null\n        mReuseStatefulView = new ReuseStatefulView();\n    }\n\n    @Override\n    protected void initState(Activity activity) {\n        // init your state here\n    }\n\n    @Override\n    protected View createView(Activity activity) {\n        // inflate and setup your view here\n        View view = activity.getLayoutInflater().inflate(R.layout.page_home, null, false);\n        return view;\n    }\n\n    @Override\n    public void dispose(Activity activity) {\n        // do cleanup when navigator pop this StatefulView from stack\n    }\n}\n```\nInjection by annotations is using reflection under the hood which might be slow.\n\nIf you find that navigator seemed to cause slowness, try to disable annotations injection and use `INavigator.injectRequired`,\nto manually inject the StatefulViews.\n\nNOTE:\nIt is better to just use this feature for convenience (Really, a HUGE convenience),\nreflection performance might be slow but this framework mitigate it by processing reflection concurrently.\nMeasure it first before decide if the slowness really comes from this framework.\n```\npublic class MyApplication extends Application {\n\n    private Navigator\u003cMainActivity, StatefulView\u003cActivity\u003e\u003e\n                mainActivityNavigator;\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        Map\u003cString, StatefulViewFactory\u003cMainActivity, StatefulView\u003cActivity\u003e\u003e\u003e navMap = new HashMap\u003c\u003e();\n        // this is where you map all your StatefulView implementations\n        navMap.put(\"/\", (args, activity) -\u003e new HomePage());\n\n        // make sure to set initial route to home page which is \"/\"\n        NavConfiguration.Builder\u003cMainActivity, StatefulView\u003cActivity\u003e\u003e navBuilder = new NavConfiguration.Builder\u003c\u003e(\"/\", navMap);\n\n        // set custom File location to save state \n        navBuilder.setSaveStateFile(new File(getCacheDir(), \"navigator1State\"));\n\n        // disable annotations functionality\n        navBuilder.setEnableAnnotationInjection(false);\n    }\n\n    public Navigator\n        getNavigator(Activity activity) {\n            if (activity instanceof MainActivity) {\n                return mainActivityNavigator;\n            }\n            return null;\n        }\n}\n```\n\n## Proguard Configuration\nIf you decide to enable minify and obfuscation you could use below rules to ensure this framework works.\n\n```\n-keep class m.co.rh.id.anavigator.**\n-keep interface m.co.rh.id.anavigator.**\n-keep enum m.co.rh.id.anavigator.**\n\n-keep @m.co.rh.id.anavigator.annotation.** class * {*;}\n\n-keepclasseswithmembers class * {\n    @m.co.rh.id.anavigator.annotation.** \u003cmethods\u003e;\n}\n\n-keepclasseswithmembers class * {\n    @m.co.rh.id.anavigator.annotation.** \u003cfields\u003e;\n}\n\n-keepclasseswithmembers class * {\n    @m.co.rh.id.anavigator.annotation.** \u003cinit\u003e(...);\n}\n\n-keepclassmembers class * implements java.io.Serializable {\n    static final long serialVersionUID;\n    static final java.io.ObjectStreamField[] serialPersistentFields;\n    private void writeObject(java.io.ObjectOutputStream);\n    private void readObject(java.io.ObjectInputStream);\n    java.lang.Object writeReplace();\n    java.lang.Object readResolve();\n}\n\n-keepclassmembers class * implements java.io.Externalizable {\n    static final long serialVersionUID;\n    static final java.io.ObjectStreamField[] serialPersistentFields;\n    private void writeObject(java.io.ObjectOutputStream);\n    private void readObject(java.io.ObjectInputStream);\n    java.lang.Object writeReplace();\n    java.lang.Object readResolve();\n    void readExternal(java.io.ObjectInput);\n    void writeExternal(java.io.ObjectInput);\n}\n```\n\n## Example Projects\n\u003cul\u003e\n\u003cli\u003ehttps://github.com/rh-id/a-news-provider\u003c/li\u003e\n\u003cli\u003ehttps://github.com/rh-id/a-flash-deck (Multi activity)\u003c/li\u003e\n\u003cli\u003ehttps://github.com/rh-id/a-medic-log\u003c/li\u003e\n\u003cli\u003ehttps://github.com/rh-id/a-personal-stuff (Multi module)\u003c/li\u003e\n\u003c/ul\u003e\n\n## Support this project\nConsider donation to support this project\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"https://trakteer.id/rh-id\"\u003ehttps://trakteer.id/rh-id\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frh-id%2Fa-navigator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frh-id%2Fa-navigator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frh-id%2Fa-navigator/lists"}