{"id":20943406,"url":"https://github.com/kongzue/runner","last_synced_at":"2025-05-14T00:32:40.547Z","repository":{"id":62627700,"uuid":"488523801","full_name":"kongzue/Runner","owner":"kongzue","description":"Kongzue Runner 旨在快速完成 App 逻辑构建，协助开发者低成本完成业务开发。提供一个独立的消息事件传递总线，不依赖 Intent 可以独立传递数据、执行事件，提供一套近乎全自动化的 ViewModel 框架，能够依据数据和 View 的对应关系自动实现数据绑定和界面适配","archived":false,"fork":false,"pushed_at":"2022-10-21T13:52:45.000Z","size":627,"stargazers_count":23,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-02T10:21:46.394Z","etag":null,"topics":["android","datawatcher","eventbus","kongzue","mvvm","mvvm-android","viewmodel"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kongzue.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-05-04T09:18:15.000Z","updated_at":"2024-06-18T08:01:36.000Z","dependencies_parsed_at":"2022-11-03T23:13:19.569Z","dependency_job_id":null,"html_url":"https://github.com/kongzue/Runner","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kongzue%2FRunner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kongzue%2FRunner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kongzue%2FRunner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kongzue%2FRunner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kongzue","download_url":"https://codeload.github.com/kongzue/Runner/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254046528,"owners_count":22005614,"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":["android","datawatcher","eventbus","kongzue","mvvm","mvvm-android","viewmodel"],"created_at":"2024-11-18T23:36:27.836Z","updated_at":"2025-05-14T00:32:35.538Z","avatar_url":"https://github.com/kongzue.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Kongzue 的消息总线\n\nKongzue Runner 旨在快速完成 App 逻辑构建，协助开发者低成本完成业务开发。\n\nRunner 提供一个独立的消息事件传递总线，不依赖 Intent，可以独立传递数据、执行事件，亦可以对尚未运行的 Activity 预设需要执行的事件，或者跨界面预设接下来要执行的事件。\n\n还提供一套近乎全自动化的 ViewModel 框架，能够依据数据和 View 的对应关系自动实现数据绑定和界面适配（Beta）。\n\n![Kongzue Runner](readme/what_is_runner.jpg)\n\n## 优势\n\n- 操作简单易上手；\n\n- 不需要重写任何接口，无需繁琐的设置，不需要你做任何继承或者重写接口；\n\n- 可以对一个不存在，还没启动的 Activity 也能生效；\n\n- 跨类操作直接对内部成员赋值；\n\n- 自动化，默认主线程执行，操作 UI 更方便；\n\n- 直接丢就完事了，这货就是个挂；\n\n## 引入\n\n\u003cdiv\u003e\n\u003cb\u003e最新版本：\u003c/b\u003e\n\u003ca href=\"https://jitpack.io/#kongzue/Runner\"\u003e\n\u003cimg src=\"https://jitpack.io/v/kongzue/Runner.svg\" alt=\"Jitpack.io\"\u003e\n\u003c/a\u003e \n\u003c/div\u003e\n\n\n1) 在 project 的 build.gradle 文件中找到 `allprojects{}` 代码块添加以下代码：\n\n```\nallprojects {\n    repositories {\n        google()\n        jcenter()\n        maven { url 'https://jitpack.io' }      //增加 jitPack Maven 仓库\n    }\n}\n```\n\n⚠️请注意，使用 Android Studio 北极狐版本（Arctic Fox）创建的项目，需要您前往 settings.gradle 添加上述 jitpack 仓库配置。\n\n2) 在 app 的 build.gradle 文件中找到 `dependencies{}` 代码块，并在其中加入以下语句：\n\n```\nimplementation 'com.github.kongzue:Runner:0.0.3'\n```\n\n\n## 怎么丢？\n\n首先你得初始化，建议在 Application#onCreate 里进行：\n\n```java\nRunner.init(this);\n```\n\n然后就可以愉快的丢东西了！\n\n### 丢事件\n\n在已经实例化的 Activity 上执行操作：\n\n```java\n//MainActivity.getInstance() 指向 MainActivity 的实例化对象，此处只做演示用，不建议这样用有内存泄漏的风险\nEvent.runOnActivity(MainActivity.getInstance(), new ActivityRunnable() {\n    @Override\n    public void run(Activity activity) {\n        //Todo...\n    }\n});\n```\n\n不确定，或尚未实例化的情况下，在指定 Activity 上执行操作（会在实例化之后执行）：\n\n```java\nEvent.runOnActivity(Activity2.class, new ActivityRunnable() {\n    @Override\n    public void run(Activity activity) {\n        //Todo...\n    }\n});\n```\n\n甚至不知道 class，只有个 Activity 的名字，在指定名字的 Activity 上执行操作（会在实例化之后执行）：\n\n```java\nEvent.runOnActivity(\"Activity2\", new ActivityRunnable() {\n    @Override\n    public void run(Activity activity) {\n        //Todo...\n    }\n});\n```\n\n额外说明，ActivityRunnable 具有泛型，你可以直接指定泛型为你的目标 Activity，这样就可以直接操作其内部的 public 修饰的成员或方法了：\n\n```java\nEvent.runOnActivity(\"Activity2\", new ActivityRunnable\u003cActivity2\u003e() {\n    @Override\n    public void run(Activity2 activity2) {\n        activity2.execPublicFunction();\n    }\n});\n```\n\n#### 在回到此界面时执行\n\n除了 runOnActivity 外，还有 runOnResume，此方法与 runOnActivity 的操作基本一致，但它的执行条件是\n\n- 当返回该界面时；\n\n- 当处于该界面时；\n\n即若当前指定界面处于顶层，runOnResume 会立即执行，若处于后台或者非顶层，则当界面恢复到顶层时执行。\n\n### 丢内容\n\n首先，你需要在目标 Activity 上编写一个成员，例如：\n\n```java\nBitmap bitmapResult;\n```\n\n对已经实例化的 Activity 中的成员直接赋值：\n\n```java\n//activity2 为已经实例化的 Activity2\nData.sendToActivity(activity2, \"bitmapResult\", BitmapFactory.decodeResource(getResources(),R.mipmap.img_bug));\n```\n\n不确定，或尚未实例化的情况下，在指定 Activity 中的成员直接赋值（会在实例化之后执行）：\n\n```java\nData.sendToActivity(Activity2.class, \"bitmapResult\", BitmapFactory.decodeResource(getResources(),R.mipmap.img_bug));\n```\n\n至不知道 class，只有个 Activity 的名字，在指定 Activity 中的成员直接赋值（会在实例化之后执行）：\n\n```java\nData.sendToActivity(\"Activity2\", \"bitmapResult\", BitmapFactory.decodeResource(getResources(),R.mipmap.img_bug));\n```\n\n要是担心混淆导致成员名称发生变化，可以使用注解，在 Activity2 中对成员进行注解标注其接收的 key：\n\n```java\n@SenderTarget(\"bitmapResult\")\nBitmap bitmap;\n```\n\n### 对于任意类的成员内容更新\n\n比如现在有一个数据存储类 User，请在其构造函数或初始化方法中添加 `Runner.bindAnyObject(object)`，例如：\n\n```java\npublic class User {\n    \n    public User() {\n        Runner.bindAnyObject(this);\n    }\n    \n    private String name;\n    private int age;\n    @SenderTarget(\"avatar\")\n    private Bitmap bitmap;\n    \n    //...\n}\n```\n\n若当前已存在实例化的对象 user，那么可以通过以下代码更新其内容：\n\n```java\nData.sendToAnyObject(user, \"name\", \"ZhangSan\");\n```\n\n若担心混淆，可使用 `@SenderTarget(...)` 注解标注其接收的 key。\n\n若当前 User 不确定是否实例化，可使用其 Class 或类名来代替设置：\n\n```java\nData.sendToAnyObject(User.class , \"name\", \"ZhangSan\");\n```\n\nKongzue Runner 的优势在于，你可以在程序的任何地方指定修改它的值，例如 Demo 中演示了，在 Activity2 中对 MainActivity 中的 user 对象内容进行操作 [查看代码](https://github.com/kongzue/Runner/blob/5c5ae5e235a910e383289d75d517e4318803100c/app/src/main/java/com/kongzue/messagebusdemo/Activity2.java#L131)。\n\n另外，Kongzue Runner 配备了完善的弱引用，您无需担心内存泄漏的问题，若出于项目中存在多个实例化的相同对象的数据操作，建议使用实例化后的对象进行操作，或者在不需要处理其数据时，使用以下方法解绑对象：\n\n```java\nRunner.unbindAnyObject(obj);\n```\n\nKongzue Runner 操作数据遵循**栈**的处理方式，即，若存在多个相同类型的对象，使用 Class/className 模式操作时遵循后入栈的优先操作的方式，且不会对所有相同类型对象都进行处理。\n\n## 随时更新 View 的内容\n\n你可以为 View 指定一个注解，当对应 key 的广播执行时，**所有** （包括其他界面）拥有该注解的 View 的内容会被更新。\n\n例如：\n\n```java\n@DataWatcher(\"subscriberA\")\nprivate TextView txtSubscribeMessage;\n```\n\n发送更新内容通知：\n\n```java\nData.changeData(\"subscriberA\", \"Test Message\");\n```\n\n随时更新会根据 View 组件的类型和数据类型进行匹配，例如当 View 为 TextView 内容为 int 时调用 textview.setText(resId) 去设置内容，此外还支持基本组件：\n\n| View      | 数据类型                                                     |\n| --------- | ------------------------------------------------------------ |\n| TextView  | String、int（资源id）、CharSequence                          |\n| ImageView | Bitmap、int（资源id）、Drawable、Icon、Uri                   |\n| ListView  | ListAdapter、List（仅支持执行对应 adapter 的数据更新操作 notifyDataSetChanged） |\n\n此外，你还可以通过注解 `@DataWatchers` 设置订阅多个广播：\n\n```java\n@DataWatchers({\"subscriberA\", \"subscriberB\"})\nprivate TextView txtSubscribeMessage;\n```\n\n#### 根据 View 的 Tag 更新内容\n\n你还可以使用以下代码根据 View 设置的 Tag 来修改内容，对所有界面同 Tag 全部生效。\n\n```java\nData.changeDataByTag(\"subscriberB\", \"Hello World\");\n```\n\n### 对于任意类的 UI 内容更新\n\n对于 Fragment 等无法统一获得管理的 UI 组件，可以使用：\n\n```java\nRunner.bindAnyObject(this);\n```\n\n对该组件完成绑定，对于 LifecycleOwner 的实现成员，例如 Fragment，会在销毁时自动解绑。\n\n更新成员中的 View 内容：\n\n```java\nData.changeData(\"subscriberA\", \"Test Message\");\n```\n\n要依据 Tag 更新成员内容，请额外为该类实现接口`RootViewInterface`：\n\n```java\npublic class SettingsFragment extends Fragment implements RootViewInterface {\n    \n    //rootView 指向您 UI 的根布局，必须为 ViewGroup\n    ViewGroup rootView;\n    \n    @Override\n    public View getRootView(){\n        return rootView;\n    }\n    \n    @Override\n    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        Runner.bindAnyObject(this);\n        //create view...\n        return rootView;\n    }\n}\n```\n\n即可完成绑定，使用以下方法更新 UI 内容：\n\n```java\nData.changeDataByTag(\"subscriberB\", \"Hello World\");\n```\n\n#### 自定义设置器\n\n对于未预设的 View 或者你需要其他方式方法设置 View 的数据更新，可以使用设置自定义数据处理器：\n\n```java\nData.customDataSetter = new CustomDataSetter() {\n@Override\npublic boolean setData(View view, Object data) {\n        if (view instanceof CustomView) {\n        //自定义设置数据类型\n        view.setData((CustomData) data.getData());\n        //返回 true 表示让 Runner 不再继续判断处理\n        return true;\n        }\n        return false;\n        }\n        };\n```\n\n## 自动化 ViewModel\n\nRunner 提供 View 和 Model 数据更新的双向绑定，即 Model 中的数据与界面上的 View 绑定，当数据发生变化时界面上的 View 内容自动更新，当界面中存在的可交互控件，例如 EditText、CheckBox，内容或状态发生变化时也将自动同步给 Model 中的内容。\n\n要实现这些功能，首先请确保你的数据字段和 XML 中的 View 配置的 `android:tag` 属性保持一致，例如：\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".Activity3\"\n    android:orientation=\"vertical\"\u003e\n\n    \u003cEditText\n        android:tag=\"username\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/\u003e\n\n    \u003cEditText\n        android:tag=\"password\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/\u003e\n\n    \u003cCheckBox\n        android:tag=\"isRememberLogin\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"记住登录状态\"/\u003e\n\n\u003c/LinearLayout\u003e\n```\n\n则 Model 代码为：\n\n```java\npublic class LoginInfo {\n    \n    private String username;\n    private String password;\n    private boolean isRememberLogin;\n    \n    //省略对应的 get/set 方法...\n}\n```\n\n此时，在 Activity 中初始化 LoginInfo 后，使用 `@BindModel` 注解标注 LoginInfo ，执行 `ViewModel.bindActivity(this);` 即可绑定界面元素：\n\n```java\npublic class Activity4 extends AppCompatActivity {\n    \n    @BindModel\n    ListData listData;\n    \n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_4);\n        \n        listData = new ListData();\n        ViewModel.bindActivity(this);\n    }\n}\n```\n\n要实现数据修改实时对界面更新，需要使 Model 继承 BaseModel 并在 set 方法后执行 `refreshUI();`\n\n### 一点骚操作\n\n对于简单的单布局 ListView 也可实现一键自动适配器，首先使 ListView 继承 `AutoCreateListViewInterface` 并回传子布局：\n\n```java\npublic class AutoCreateListView extends ListView implements AutoCreateListViewInterface {\n    \n    public AutoCreateListView(Context context) {\n        super(context);\n    }\n    \n    public AutoCreateListView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n    \n    public AutoCreateListView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n    \n    @Override\n    public int itemLayoutRes() {\n        return R.layout.item_list_test;\t\t//回传子布局\n    }\n}\n```\n\n制作数据 Model：\n\n```java\npublic class ListData {\n    \n    List\u003cData\u003e list;\n    \n    public ListData() {\n        list = new ArrayList\u003c\u003e();\n        //list.add... 省略数据添加步骤\n    }\n    \n    class Data{\n        String title;\n        String tip;\n    }\n}\n```\n\n绑定到 Activity：\n\n```java\npublic class Activity4 extends AppCompatActivity {\n    \n    @BindModel\n    ListData listData;\n    \n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_4);\n        \n        listData = new ListData();\n        ViewModel.bindActivity(this);\n    }\n}\n```\n\nViewModel 会自动配置 Adapter，你无需关心任何事情。\n\n## 开源协议\n\n```\nCopyright Kongzue Runner\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkongzue%2Frunner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkongzue%2Frunner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkongzue%2Frunner/lists"}