Name | CC | AutoRegister |
---|---|---|
Version |
Main App(contains demo and demo_component_a)
demo shows cc works on component in or not in main app. it looks like below via running both of above app on your device and launch demo app.
Notice: calling across apps is only compat for develop time
you need to turnon the permission 'auto start' for Demo_B to make it worked if the process of Demo_B is not alive.
-
Easy to use, only 4 steps:
- Add AutoRegister plug-in classpath in projectRoot/build.gradle
- Add gradle file apply in module/build.gradle
- Implements a component class for IComponent in the module
- specified a name for this component in method: getNae()
- call CC.sendCCResult(cc.getCallId, CCResule.success()) in method: onCall(cc).
- Then you can call this component at everywhere in you app:
- CC.obtainBuilder("component_name").build().call()
- CC.obtainBuilder("component_name").build().callAsync()
-
Feature-rich
1. Support inter-component invocation (not just Activity router, call&callback for almost all instructions) 2. Support component invocation is associated with Activity and Fragment lifecycle (requires: android api level >= 14, support lib version >= 5.1.0) 3. Support inter-app component invocation (component development/commissioning can be run separately as an app) 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). 5. Support for synchronous/asynchronous invocation 6. Supports synchronous/asynchronous implementation of components 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. 8. Support for adding custom interceptors (executed in the order of addition) 9. Support for timeout Settings (in milliseconds, 0: no timeout, synchronous invocation set as 1000 ms by default) 10. Support manual cancellation 11. Automatic registration of components (IComponent) at compile time without manually maintain the component registry (implemented by using ASM to modify bytecode) 12. Support for dynamic registration/unregistration components (IDynamicComponent) 13. Support for non-basic types of objects, such as passing fragments between components 14. Try to solve the crash that is caused by incorrect usage: 14.1 component invocation, callback, and component implementation crash are all caught within the cc framework 14.2 The CCResult object of synchronous return or asynchronous callback must not be null to avoid null pointer
-
low cost to convert original code to CC
Some guys worry about that it's too expensive to convert the code in the old project to a high degree of code coupling CC can only take 2 steps to solve this problem:
- Create a component class (IComponent implementation class) that provides functionality that was previously implemented by class dependencies
- Then change the direct class call to CC call mode
-
monitor the execution process log Developer can monitor execution process logs with Logcat CC disabled this function by default, enable it with code:
CC.enableVerboseLog(true);
//core
- cc core library of CC framework
- cc-settings.gradle common gradle file for user
//demos
- demo demo main app module
- demo_component_a demo ComponentA
- demo_component_b demo ComponentB
- component_protect_demo demo for add permission settings,as dependencies within cc-settings-demo-b.gradle
- cc-settings-demo-b.gradle actionProcessor自动注册的配置脚本demo
- demo-debug.apk demo apk(contains demo and demo_component_a)
- demo_component_b-debug.apk apk for demo_component_b only
buildscript {
dependencies {
classpath 'com.billy.android:autoregister:x.x.x'
}
}
apply plugin: 'com.android.library'
//or
apply plugin: 'com.android.application'
//replace to
apply from: 'https://raw.githubusercontent.com/luckybilly/CC/master/cc-settings.gradle'
see demo_component_a/build.gradle
module is setting as library by default. there are 2 ways to set as application for single launch apk:
2.1 modify local.properties
demo_component_b=true # run as application for module: demo_component_b
2.2 modify module/build.gradle: add ext.runAsApp = true
before apply from: '...cc-settings.gradle'
,ext.runAsApp priority is higher than local.properties.
ext.runAsApp = true
apply from: 'https://raw.githubusercontent.com/luckybilly/CC/master/cc-settings.gradle'
3. Define a component (IComponent)
public class ComponentA implements IComponent {
@Override
public String getName() {
// specify the name for this component
return "demo.ComponentA";
}
@Override
public boolean onCall(CC cc) {
Context context = cc.getContext();
Intent intent = new Intent(context, ActivityComponentA.class);
if (!(context instanceof Activity)) {
// context maybe an application object if caller dose not setContext
// or call across apps
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
//send result to caller
CC.sendCCResult(cc.getCallId(), CCResult.success());
// onCall return false if result is sent synchronization before this method returned
return false;
}
}
// Synchronous call, get CCResult by method return
CCResult result = CC.obtainBuilder("demo.ComponentA").build().call();
// Asynchronous call, do not need result callback
String callId = CC.obtainBuilder("demo.ComponentA").build().callAsync();
// Asynchronous call, result callback in thread pool
String callId = CC.obtainBuilder("demo.ComponentA").build().callAsync(new IComponentCallback(){...});
//Asynchronous call, result callback on main thread
String callId = CC.obtainBuilder("demo.ComponentA").build().callAsyncCallbackOnMainThread(new IComponentCallback(){...});
dependencies {
addComponent 'demo_component_a' //default add dependency: project(':demo_component_a')
addComponent 'demo_component_kt', project(':demo_component_kt')
addComponent 'demo_component_b', 'com.billy.demo:demo_b:1.1.0'
}
## Advance usage
```java
// enable/disable debug log
CC.enableDebug(trueOrFalse);
// enable/disable cc process detail log
CC.enableVerboseLog(trueOrFalse);
// enable/disable caller across apps
CC.enableRemoteCC(trueOrFalse)
// cancel a cc by callId
CC.cancel(callId)
// set context for cc
CC.obtainBuilder("demo.ComponentA")...setContext(context)...build().callAsync()
// cc will cancel automaticly on activity destroyed
CC.obtainBuilder("demo.ComponentA")...cancelOnDestroyWith(activity)...build().callAsync()
// cc will cancel automaticly on fragment destroyed
CC.obtainBuilder("demo.ComponentA")...cancelOnDestroyWith(fragment)...build().callAsync()
// set cc actionName
CC.obtainBuilder("demo.ComponentA")...setActionName(actionName)...build().callAsync()
// set cc timeout in milliseconds
CC.obtainBuilder("demo.ComponentA")...setTimeout(1000)...build().callAsync()
// add extenal params
CC.obtainBuilder("demo.ComponentA")...addParam("name", "billy").addParam("id", 12345)...build().callAsync()
// build a success CCResult
CCResult.success(key1, value1).addData(key2, value2)
// build a failed CCResult
CCResult.error(message).addData(key, value)
// send CCResult to caller (you should make sure this method called for each onCall(cc) invoked)
CC.sendCCResult(cc.getCallId(), ccResult)
// get cc result if success or not
ccResult.isSuccess()
// success code(0:success, <0: failed, 1:component reached but result is failed)
ccResult.getCode()
// get error message
ccResult.getErrorMessage()
// get external data
Map<String, Object> data = ccResult.getDataMap();
if (data != null) {
Object value = data.get(key)
}
CCResult code list:
code | error status |
---|---|
0 | success |
1 | business failed in component |
-1 | default error. not used yet |
-2 | component name is empty |
-3 | CC.sendCCResult(callId, null) or interceptor returns null |
-4 | An exception was thrown during cc |
-5 | no component object found for the specified component_name |
-6 | context is null and get application failed by reflection |
-7 | connect failed during cc across apps |
-8 | cc is canceled |
-9 | cc is timeout |
-10 | component.onCall(cc) return false, bus no CCResult found |
-
Custom interceptors
- Create a class that implements the ICCInterceptor interface
- Call the chain.proceed() method to keep the call chain down and not call to block the CC
- You can modify the parameters of the CC object before calling chain.proceed()
- After calling the chain.proceed() method, you can modify CCResult
see demo: MissYouInterceptor.java
-
register/unregister dynamic component
Definition: Unlike the static component (IComponent), which is automatically registered to ComponentManager at compile time, dynamic components do not automatically register and work through manual registration/unregistration
1. Dynamic components need to implement interfaces: IDynamicComponent
2. It is necessary to call CC.registerComponent(component) manually, similar to the BroadcastReceiver dynamic registration
3. It is necessary to call CC.unregisterComponent(component) manually, similar to the BroadcastReceiver dynamic unregistration
4. Other usage are the same as static components
-
You can have multiple modules include in a module
In a module, you can have multiple implementation classes for the IComponent interface (or IDynamicComponent interface) IComponents are automatically registered to the component management class ComponentManager at compile time IDynamicComponents are not
-
A component can process multiple actions
In the onCall(CC cc) method, gets actions to handle separately via cc.getActionName()
see:ComponentA
-
Auto register Custom ActionProcessor into component
-
Add custom permission protection to calls across app components
- create a new module
- add dependence in module/build.gradle:
compile 'com.billy.android:cc:0.3.0'
- modify module/src/main/AndroidManifest: add permission protection for BroadcastReceiver, like this: component_protect_demo
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.billy.cc.demo.component.protect" > <permission android:name="cc.permission.com.billy.cc.demo.REMOTE_CC" android:protectionLevel="signature" /> <uses-permission android:name="cc.permission.com.billy.cc.demo.REMOTE_CC" /> <application> <receiver android:name="com.billy.cc.core.component.ComponentBroadcastReceiver" android:permission="cc.permission.com.billy.cc.demo.REMOTE_CC" /> </application> </manifest>
- other modules dependent on this module
You can easily do AOP work with CC, such as:
- Activity open requires user login:
- check the user login status before startActivity
- already login:
- startActivity immediately and CC.sendCCResult(callId, CCResult.success());
- not login:
- start login activity and wait for result
- login success: startActivity and CC.sendCCResult(callId, CCResult.success());
- login failed: CC.sendCCResult(callId, CCResult.error("login failed"));
demo: seeLifecycleComponent.java
- Pre-load activity data before context.startActivity with PreLoader
- define a component for open the activity
public class ComponentA implements IComponent {
@Override
public String getName() {
return "demo.ComponentA";
}
@Override
public boolean onCall(CC cc) {
int preLoaderId = PreLoader.preLoad(new Loader());
Intent intent = new Intent(this, PreLoadBeforeLaunchActivity.class);
intent.putExtra("preLoaderId", preLoaderId);
startActivity(intent);
CC.sendCCResult(cc.getCallId(), CCResult.success());
return false;
}
}
- call that component by CC to open activity
// pre-load is needless here, the logistic of component are all inside that component itself
CC.obtainBuilder("demo.ComponentA").build().call();
none