{"id":15027810,"url":"https://github.com/luckybilly/cc","last_synced_at":"2025-10-05T08:23:23.609Z","repository":{"id":43613040,"uuid":"111830550","full_name":"luckybilly/CC","owner":"luckybilly","description":"业界首个支持渐进式组件化改造的Android组件化开源框架，支持跨进程调用。Componentize your android project gradually.","archived":false,"fork":false,"pushed_at":"2023-10-11T01:51:19.000Z","size":32435,"stargazers_count":4053,"open_issues_count":28,"forks_count":637,"subscribers_count":115,"default_branch":"master","last_synced_at":"2025-05-28T20:49:29.512Z","etag":null,"topics":["android-architecture","android-component","architecture-components","cc","component","componentization"],"latest_commit_sha":null,"homepage":"https://luckybilly.github.io/CC-website/","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/luckybilly.png","metadata":{"files":{"readme":"README-en-US.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}},"created_at":"2017-11-23T16:26:33.000Z","updated_at":"2025-05-27T05:59:16.000Z","dependencies_parsed_at":"2023-02-16T18:02:20.260Z","dependency_job_id":"252a7c3c-c42a-47e1-976e-435b04c54c0b","html_url":"https://github.com/luckybilly/CC","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/luckybilly/CC","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckybilly%2FCC","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckybilly%2FCC/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckybilly%2FCC/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckybilly%2FCC/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luckybilly","download_url":"https://codeload.github.com/luckybilly/CC/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luckybilly%2FCC/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278426700,"owners_count":25984920,"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-10-05T02:00:06.059Z","response_time":54,"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-architecture","android-component","architecture-components","cc","component","componentization"],"created_at":"2024-09-24T20:07:05.775Z","updated_at":"2025-10-05T08:23:23.595Z","avatar_url":"https://github.com/luckybilly.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"## CC : Component Caller (Modularization Architecture for Android)\n\n[中文文档](README.md)\n\n[![Join the chat at https://gitter.im/billy_home/CC](https://badges.gitter.im/billy_home/CC.svg)](https://gitter.im/billy_home/CC?utm_source=share-link\u0026utm_medium=link\u0026utm_campaign=share-link)\n\nName|CC|cc-register\n---|---|---\nVersion| [![Download](https://api.bintray.com/packages/hellobilly/android/cc/images/download.svg)](https://bintray.com/hellobilly/android/cc/_latestVersion)| [![Download](https://api.bintray.com/packages/hellobilly/android/cc-register/images/download.svg)](https://bintray.com/hellobilly/android/cc-register/_latestVersion)\n\n## demo download\n\n[Main App(contains demo and demo_component_a)](https://github.com/luckybilly/CC/raw/master/demo-debug.apk)\n\n[demo_component_b (Demo_B)](https://github.com/luckybilly/CC/raw/master/demo_component_b-debug.apk)\n\ndemo shows cc works on component in or not in main app.\nit looks like below via running both of above app on your device and launch demo app.\n\n\n        Notice: calling across apps is only compat for develop time\n        you need to turnon the permission 'auto start' for Demo_B to make it worked if the process of Demo_B is not alive. \n\n![image](https://raw.githubusercontent.com/luckybilly/CC/master/image/CC.gif)\n\n## What`s different?\n\n- Easy to use, only 4 steps:\n        \n    - Add [AutoRegister](https://github.com/luckybilly/AutoRegister) plug-in classpath in projectRoot/build.gradle \n    - Add gradle file apply in module/build.gradle\n    - Implements a component class for IComponent in the module\n        - specified a name for this component in method: getNae()\n        - call CC.sendCCResult(cc.getCallId, CCResule.success()) in method: onCall(cc).\n    - Then you can call this component at everywhere in you app:\n        - CC.obtainBuilder(\"component_name\").build().call()\n        - CC.obtainBuilder(\"component_name\").build().callAsync()\n    \n- Feature-rich\n\n\n        1. Support inter-component invocation (not just Activity router, call\u0026callback for almost all instructions)\n        2. Support component invocation is associated with Activity and Fragment lifecycle (requires: android api level \u003e= 14, support lib version \u003e= 5.1.0)\n        3. Support inter-app component invocation (component development/commissioning can be run separately as an app)\n        4. Support to switch and permission Settings of the invocation between apps (Meets for the security requirements of different levels, default status: enabled and do not require permission).\n        5. Support for synchronous/asynchronous invocation\n        6. Supports synchronous/asynchronous implementation of components\n        7. The invocation method is unrestricted by implementation (for example, asynchronous implementation of another component can be invoked synchronously. Note: do not use time-consuming operation in the main thread.\n        8. Support for adding custom interceptors (executed in the order of addition)\n        9. Support for timeout Settings (in milliseconds, 0: no timeout, synchronous invocation set as 1000 ms by default)\n        10. Support manual cancellation\n        11. Automatic registration of components (IComponent) at compile time without manually maintain the component registry (implemented by using ASM to modify bytecode)\n        12. Support for dynamic registration/unregistration components (IDynamicComponent)\n        13. Support for non-basic types of objects, such as passing fragments between components\n        14. Try to solve the crash that is caused by incorrect usage:\n            14.1 component invocation, callback, and component implementation crash are all caught within the cc framework\n            14.2 The CCResult object of synchronous return or asynchronous callback must not be null to avoid null pointer\n\n- low cost to convert original code to CC\n\n    Some guys worry about that it's too expensive to convert the code in the old project to a high degree of code coupling\n    CC can only take 2 steps to solve this problem:\n    1. Create a component class (IComponent implementation class) that provides functionality that was previously implemented by class dependencies\n    2. Then change the direct class call to CC call mode\n    \n- monitor the execution process log\n    Developer can monitor execution process logs with Logcat\n    CC disabled this function by default, enable it with code: `CC.enableVerboseLog(true);`        \n        \n## The directory structure\n\n        //core\n        - cc                            core library of CC framework\n        - cc-settings.gradle            common gradle file for user\n        \n        //demos\n        - demo                          demo main app module\n        - demo_component_a              demo ComponentA \n        - demo_component_b              demo ComponentB\n        - cc-settings-demo.gradle       actionProcessor自动注册的配置脚本demo\n        - demo-debug.apk                demo apk(contains demo and demo_component_a)\n        - demo_component_b-debug.apk    apk for demo_component_b only\n\n## How to Use\n\n#### 1. add classpath\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath 'com.billy.android:autoregister:x.x.x'\n    }\n}\n```\n#### 2. modify build.gradle for all component and main app modules：\n```groovy\napply plugin: 'com.android.library'\n//or\napply plugin: 'com.android.application'\n\n//replace to\napply from: cc-settings-2.gradle\n```\n\nsee [demo_component_a/build.gradle](https://github.com/luckybilly/CC/blob/master/demo_component_a/build.gradle)\n\nmodule is setting as library by default. there are 2 ways to set as application for single launch apk:\n\n2.1 modify local.properties\n```properties\ndemo_component_b=true # run as application for module: demo_component_b\n```\n2.2 modify module/build.gradle: add `ext.runAsApp = true` before `apply from: '...cc-settings.gradle'`,ext.runAsApp priority is higher than local.properties.\n```groovy\next.runAsApp = true\napply from: cc-settings-2.gradle\n```\n#### 3. Define a component ([IComponent](https://github.com/luckybilly/CC/blob/master/cc/src/main/java/com/billy/cc/core/component/IComponent.java))\n```java\npublic class ComponentA implements IComponent {\n    \n    @Override\n    public String getName() {\n        // specify the name for this component\n        return \"demo.ComponentA\";\n    }\n\n    @Override\n    public boolean onCall(CC cc) {\n        Context context = cc.getContext();\n        Intent intent = new Intent(context, ActivityComponentA.class);\n        if (!(context instanceof Activity)) {\n            // context maybe an application object if caller dose not setContext \n            // or call across apps\n            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        }\n        context.startActivity(intent);\n        //send result to caller\n        CC.sendCCResult(cc.getCallId(), CCResult.success());\n        // onCall return false if result is sent synchronization before this method returned\n        return false;\n    }\n}\n```\n#### 4. Call component by CC\n```java\n// Synchronous call, get CCResult by method return\nCCResult result = CC.obtainBuilder(\"demo.ComponentA\").build().call();\n// Asynchronous call, do not need result callback\nString callId = CC.obtainBuilder(\"demo.ComponentA\").build().callAsync();\n// Asynchronous call, result callback in thread pool\nString callId = CC.obtainBuilder(\"demo.ComponentA\").build().callAsync(new IComponentCallback(){...});\n//Asynchronous call, result callback on main thread\nString callId = CC.obtainBuilder(\"demo.ComponentA\").build().callAsyncCallbackOnMainThread(new IComponentCallback(){...});\n```\n\n#### More: Add dependencies in main app module for all component modules like below:\n\n```groovy\ndependencies {\n    addComponent 'demo_component_a' //default add dependency: project(':demo_component_a')\n    addComponent 'demo_component_kt', project(':demo_component_kt')\n    addComponent 'demo_component_b', 'com.billy.demo:demo_b:1.1.0'\n}\n\n\n## Advance usage\n\n```java\n// enable/disable debug log\nCC.enableDebug(trueOrFalse);    \n// enable/disable cc process detail log\nCC.enableVerboseLog(trueOrFalse); \n// enable/disable caller across apps\nCC.enableRemoteCC(trueOrFalse)  \n// cancel a cc by callId\nCC.cancel(callId)\n// set context for cc\nCC.obtainBuilder(\"demo.ComponentA\")...setContext(context)...build().callAsync()\n// cc will cancel automaticly on activity destroyed\nCC.obtainBuilder(\"demo.ComponentA\")...cancelOnDestroyWith(activity)...build().callAsync()\n// cc will cancel automaticly on fragment destroyed\nCC.obtainBuilder(\"demo.ComponentA\")...cancelOnDestroyWith(fragment)...build().callAsync()\n// set cc actionName\nCC.obtainBuilder(\"demo.ComponentA\")...setActionName(actionName)...build().callAsync()\n// set cc timeout in milliseconds\nCC.obtainBuilder(\"demo.ComponentA\")...setTimeout(1000)...build().callAsync()\n// add extenal params\nCC.obtainBuilder(\"demo.ComponentA\")...addParam(\"name\", \"billy\").addParam(\"id\", 12345)...build().callAsync()\n\n\n// build a success CCResult\nCCResult.success(key1, value1).addData(key2, value2)\n// build a failed CCResult\nCCResult.error(message).addData(key, value)\n// send CCResult to caller (you should make sure this method called for each onCall(cc) invoked)\nCC.sendCCResult(cc.getCallId(), ccResult)\n// get cc result if success or not\nccResult.isSuccess()\n// success code(0:success, \u003c0: failed, 1:component reached but result is failed)\nccResult.getCode()\n// get error message\nccResult.getErrorMessage()  \n// get external data\nMap\u003cString, Object\u003e data = ccResult.getDataMap();\nif (data != null) {\n    Object value = data.get(key)   \n}\n```\nCCResult code list:\n\n| code        | error status    |\n| --------   | :----- |\n| 0 | success |\n| 1 | business failed in component |\n| -1 | default error. not used yet |\n| -2 | component name is empty |\n| -3 | CC.sendCCResult(callId, null) or interceptor returns null |\n| -4 | An exception was thrown during cc |\n| -5 | no component object found for the specified component_name |\n| -6 | context is null and get application failed by reflection |\n| -7 | connect failed during cc across apps |\n| -8 | cc is canceled |\n| -9 | cc is timeout |\n| -10 | component.onCall(cc) return false, bus no CCResult found |\n\n- Custom interceptors\n\n    1. Create a class that implements the ICCInterceptor interface\n    2. Call the chain.proceed() method to keep the call chain down and not call to block the CC\n    2. You can modify the parameters of the CC object before calling chain.proceed()\n    3. After calling the chain.proceed() method, you can modify CCResult\n    \n    see demo: [MissYouInterceptor.java](https://github.com/luckybilly/CC/blob/master/demo/src/main/java/com/billy/cc/demo/MissYouInterceptor.java)\n    \n- register/unregister dynamic component\n\nDefinition: Unlike the static component (IComponent), which is automatically registered to ComponentManager at compile time, \ndynamic components do not automatically register and work through manual registration/unregistration\n\n        1. Dynamic components need to implement interfaces: IDynamicComponent\n        2. It is necessary to call CC.registerComponent(component) manually, similar to the BroadcastReceiver dynamic registration\n        3. It is necessary to call CC.unregisterComponent(component) manually, similar to the BroadcastReceiver dynamic unregistration\n        4. Other usage are the same as static components\n\n- You can have multiple modules include in a module\n\n\n        In a module, you can have multiple implementation classes for the IComponent interface (or IDynamicComponent interface)\n        IComponents are automatically registered to the component management class ComponentManager at compile time\n        IDynamicComponents are not\n\n- A component can process multiple actions\n\n        In the onCall(CC cc) method, gets actions to handle separately via cc.getActionName()\n\n    see：[ComponentA](https://github.com/luckybilly/CC/blob/master/demo_component_a/src/main/java/com/billy/cc/demo/component/a/ComponentA.java)\n- Auto register Custom ActionProcessor into component\n\n    see[ComponentB](https://github.com/luckybilly/CC/blob/master/demo_component_b/src/main/java/com/billy/cc/demo/component/b/ComponentB.java)\n    and[cc-settings-demo.gradle](https://github.com/luckybilly/CC/blob/master/cc-settings-demo.gradle)\n\n\n##### watch the sourcecode of demo, demo_component_a and demo_component_b for more details\n\n## More usage\n\nYou can easily do AOP work with CC, such as:\n\n1. Activity open requires user login: \n\n- check the user login status before startActivity\n- already login: \n    - startActivity immediately and CC.sendCCResult(callId, CCResult.success());\n- not login: \n    - start login activity and wait for result\n    - login success: startActivity and CC.sendCCResult(callId, CCResult.success());\n    - login failed: CC.sendCCResult(callId, CCResult.error(\"login failed\"));\n\ndemo: see[LifecycleComponent.java](https://github.com/luckybilly/CC/blob/master/demo/src/main/java/com/billy/cc/demo/LifecycleComponent.java)\n\n2. Pre-load activity data before context.startActivity with [PreLoader](https://github.com/luckybilly/PreLoader)\n\n- define a component for open the activity\n```java\npublic class ComponentA implements IComponent {\n\n    @Override\n    public String getName() {\n        return \"demo.ComponentA\";\n    }\n\n    @Override\n    public boolean onCall(CC cc) {\n        int preLoaderId = PreLoader.preLoad(new Loader());\n        Intent intent = new Intent(this, PreLoadBeforeLaunchActivity.class);\n        intent.putExtra(\"preLoaderId\", preLoaderId);\n        startActivity(intent);\n        CC.sendCCResult(cc.getCallId(), CCResult.success());\n        return false;\n    }\n}\n```\n\n- call that component by CC to open activity\n```java\n// pre-load is needless here, the logistic of component are all inside that component itself\nCC.obtainBuilder(\"demo.ComponentA\").build().call();\n```\n\n\n## Proguard\n\nnone\n\n## Contact me\n\nqiyilike@163.com\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluckybilly%2Fcc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluckybilly%2Fcc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluckybilly%2Fcc/lists"}