prototypez / appjoint Goto Github PK
View Code? Open in Web Editor NEW🔧 Cross module Android development made easy!
🔧 Cross module Android development made easy!
12-07 17:23:49.992 21671-21671/com.xxx.xxxx E/AndroidRuntime: FATAL EXCEPTION: main
Process: xxx.xxxx.xxxx, PID: 21671
java.lang.RuntimeException: Unable to create application com.xxxx.xxxxx.Application: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4832)
at android.app.ActivityThread.access$1700(ActivityThread.java:166)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1472)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5563)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:853)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:737)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
at com.appsflyer.AppsFlyerLib.startTracking(:890)
at com.appsflyer.AppsFlyerLib.startTracking(:875)
at com.xxxx.xxxxx.analytics.AnalyticsApp.initAppFlyer(AnalyticsApp.kt:41)
at com.xxxx.xxxxx.analytics.AnalyticsApp.onCreate(AnalyticsApp.kt:16)
at io.github.prototypez.appjoint.AppJoint.onCreate(AppJoint.java:47)
at comxxxx.xxxxx.Application.onCreate(xxxx.xxxxxApplication.java)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1020)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4829)
at android.app.ActivityThread.access$1700(ActivityThread.java:166)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1472)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5563)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:853)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:737)
有些SDK 中初始化要使用使用application来注册 比如activitycallbacks之类的操作, 如果在module application中初始化,会遇到问题
下面是我碰到的问题:
如果按照以下引用方式, 在 module 中使用 @ModuleSpec 的 application 是不会走 onCreate 方法
apply plugin: 'com.android.application'
apply plugin: 'android-aspectjx'
apply plugin: 'app-joint'
修改成:
apply plugin: 'com.android.application'
apply plugin: 'app-joint'
apply plugin: 'android-aspectjx'
正确调用
完全按照readme来的。。
一个字一个字对了的啊。
demo模块:app,module1,base
项目build.gradle,app build.gradle classpath...加了,apply加了,implement每个都加了,app依赖base,module1依赖base,base里写接口,module1实现,加注解,app里getservice。 null...
2020-09-24 13:51:14.653 30498-30498/com.example.componentizationpractice E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.componentizationpractice, PID: 30498 java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.base.Module1Service.startActivityOfModule1(android.content.Context)' on a null object reference at com.example.componentizationpractice.MainActivity$onCreate$1.onClick(MainActivity.kt:18) at android.view.View.performClick(View.java:6608) at android.view.View.performClickInternal(View.java:6585) at android.view.View.access$3100(View.java:785) at android.view.View$PerformClick.run(View.java:25921) at android.os.Handler.handleCallback(Handler.java:873) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:207) at android.app.ActivityThread.main(ActivityThread.java:6878) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:876)
Error:com.android.builder.dexing.DexArchiveBuilderException: Failed to process
做了以下改动解决了问题。。。
在app下build.gradle添加
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
gradle版本同步为
gradle-4.4-all
com.android.tools.build:gradle:3.1.4
如果模块A启动模块B的Activity,并需要模块B Activity的结果怎么办?还是用startForResult吗?
我说一个场景:A,B,C模块的某些操作需要用户登录,如果未登录则走登录流程,并返回登录结果,登录流程在D模块里的某Activity,那么A B C 在进行相应操作前都要检查D登录状态,如果未登录,A B C 怎么才能获取到D的登录结果然后进行后面的操作?
根据你提出的方法,把各个模块的暴露api通通放到单独的router 模块中,然后由各个模块引用这个router模块。这样确实可行,但是也会出现模块A只是需要模块B的接口,但是却要引用其他的api,这是不是有点多余?如果把每个模块要暴露的api放到单独一个module 然后其他模块按需依赖这些api module 这样设计会不会更改好?就像微信做的那样
如果 module a依赖于module b,比如支付模块调用前需要先执行登陆模块的初始化,那么如果保证支付模块的application执行顺序在登陆模块的application之后?
Manifest merger failed : uses-sdk:minSdkVersion 16 cannot be smaller than version 17 declared in library [io.github.prototypez:app-joint-core:1.2] C:\Users\ning.gao.gradle\caches\transforms-1\files-1.1\app-joint-core-1.2.aar\e4d347115abb15c3ed6ee929aa92e1ae\AndroidManifest.xml as the library might be using APIs not available in 16
Suggestion: use a compatible library with a minSdk of at most 16,
or increase this project's minSdk version to at least 17,
or use tools:overrideLibrary="io.github.prototypez.appjoint.core" to force usage (may lead to runtime failures)
我想问支持的最低版本必须在16以上吗?
在主Application中加上@appspec注解,运行时会报 “No injector factory”。
项目中引用了dagger2。删除注解后,运行又正常。
Gradle - V4.4/4.6 都尝试过了,出现下面相同的错误。项目能编译通过,但是打包运行失败,错误如下:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:transformClassesWithAppJointForDebug'. at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:103) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:73) at org.gradle.api.internal.tasks.execution.OutputDirectoryCreatingTaskExecuter.execute(OutputDirectoryCreatingTaskExecuter.java:51) at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:59) at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54) at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:59) at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101) at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44) at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:91) at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62) at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:59) at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54) at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43) at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34) at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:256) at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336) at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328) at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199) at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110) at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:249) at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:238) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:123) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:79) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:104) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:98) at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:663) at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:597) at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:98) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55) Caused by: groovy.lang.GroovyRuntimeException: Ambiguous method overloading for method java.io.FileInputStream#<init>. Cannot resolve which method to invoke for [null] due to overlapping prototypes between: [class java.io.FileDescriptor] [class java.io.File] [class java.lang.String] at io.github.prototypez.appjoint.plugin.AppJointTransform.transform(AppJointTransform.groovy:169) at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:239) at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:235) at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:102) at com.android.build.gradle.internal.pipeline.TransformTask.transform(TransformTask.java:230) at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73) at org.gradle.api.internal.project.taskfactory.IncrementalTaskAction.doExecute(IncrementalTaskAction.java:50) at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:39) at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:26) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:124) at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336) at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328) at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199) at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:113) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:95) ... 30 more
对于已经完成业务模块化的,感觉是没必要引入这个框架的,毕竟接口隔离业务也是一种通用的方式。这个框架,适合将一些复杂的非业务模块化的项目,进行拆分成业务模块化的形式。通过使用这个框架完全拆分成业务模块化之后,我移除了这个框架,感谢作者这个框架,在我业务模块拆分中带来的帮助,谢谢!
ps:拆分后的项目,组件的模块打成aar,业务模块接口隔离。我们认为,组件是各个业务模块都可以拿来用的,是组合模式。各个业务模块,是独立的,而且应该是可以快速打成apk的,我们称之为业务模块
interface Module1Service{
}
interface Module2Service{
}
@ServiceProvider
class Module1ServiceImpl : Module1Service{
var service = AppJoint.service(Module2Service.class)
}
@ServiceProvider
class Module2ServiceImpl : Module2Service{
var service = AppJoint.service(Module1Service.class)
}
如此就死循环了~
com.novoda.gradle.release.AndroidLibrary$LibraryUsage.getDependencyConstraints()Ljava/util/Set;
压缩文件:com/oriente/cashalo/service/PluginService.class
visiting Lkotlin/Metadata;
visiting Lio/github/prototypez/appjoint/core/ModuleSpec;
visiting Lkotlin/Metadata;
visiting Lio/github/prototypez/appjoint/core/ServiceProvider;
压缩文件:META-INF/MANIFEST.MF
压缩文件:META-INF/analytics_debug.kotlin_module
压缩文件:com/oriente/analytics/AnalyticsApp.class
压缩文件:com/oriente/analytics/BuildConfig.class
压缩文件:com/oriente/analytics/AnalyticsServiceImpl.class
压缩文件:META-INF/MANIFEST.MF
压缩文件:com/oriente/thirdpartylib/BuildConfig.class
moduleApplications: [com/oriente/analytics/AnalyticsApp]
appApplications: [:]
routerAndImpl: [com/oriente/moduleapi/AnalyticsService:com/oriente/analytics/AnalyticsServiceImpl]
appJointClassFile: /Users/trevorwang/Dev/Android/workspace/cashalo/app/build/intermediates/transforms/ajx/debug/io.github.prototypezapp-joint-core1.5_unzip/io/github/prototypez/appjoint/AppJoint.class
appJointOutputFile: /Users/trevorwang/Dev/Android/workspace/cashalo/app/build/intermediates/transforms/ajx/debug/io.github.prototypezapp-joint-core1.5_repackage/io/github/prototypez/appjoint/AppJoint.class
repackageActions: 1
java.lang.AbstractMethodError: abstract method "void com.tgnet.router.IChatService.launchToChat(long, java.lang.String, java.lang.String, java.lang.String, long, boolean)"
at app.tgnet.com.walktrace.fragment.IndexWalkTraceFragment$onViewCreated$1.onClickMessage(IndexWalkTraceFragment.kt:119)
at app.tgnet.com.walktrace.adapter.WalkTraceDetailAdapter$ViewHolder$setOtherWalkTraceInfo$$inlined$run$lambda$2.onClick(WalkTraceDetailAdapter.kt:183)
at android.view.View.performClick(View.java:6291)
at android.view.View$PerformClick.run(View.java:24931)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:101)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7529)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
你们都是怎么处理的?有有效方法吗? 我也是同样的跟上面 mrrobot97一样的错误
改下插件吧 只能遍历所有的 jarInput 了
Originally posted by @mrrobot97 in #44 (comment)
standalone 文件夹是怎么创建的,然后里面的module又是怎么创建的
W/System.err:java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object java.lang.Class.newInstance()' on a null object reference
W/System.err: at io.github.prototypez.appjoint.AppJoint.service(AppJoint.java:86)
W/System.err: at io.github.prototypez.appjoint.AppJoint.service(AppJoint.java:79)
debug发现routersMap是空的
public static synchronized <T> T service(Class<T> routerType, String name) {
T requiredRouter = (T) get().getRouterInstanceMap().get(routerType, name);
if (requiredRouter == null) {
try {
requiredRouter = (T) get().routersMap.get(routerType, name).newInstance();
get().getRouterInstanceMap().put(routerType, name, requiredRouter);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
return requiredRouter;
}
下载源码 将core引入工程,运行出现报错
Ambiguous method overloading for method java.io.FileInputStream#.
Cannot resolve which method to invoke for [null] due to overlapping prototypes between:
[class java.io.FileDescriptor]
[class java.io.File]
[class java.lang.String]
我的项目 目前是一个模块就一个project
然后打包成aar,主项目通过 像依赖github项目那样去依赖每个模块
我尝试在其中一个模块中使用Appjoint (主项目加入了对应的appjoint plugin)
运行起来后会提示找不到interface所对应的具体实现类
请问这个框架能支持跨project吗,跨module我实现了没问题,非常感谢你的框架
希望能赐教一下我怎么做,有什么思路
Caused by: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/leelion6/Documents/work space/AgentBear/app/build/intermediates/transforms/appJoint/debug/98.jar
RT
作者您好,希望能得到您的回复
首先感谢作者的思路,我认为这套方案非常对我胃口,几乎无缝切换各个组件而不需要重新sync gradle简直不要太赞~非常nice的说
其次关于url,我倒是认为可以通过某个组件专门用来解析然后分发调用各组件的接口service
这次接入的时候我没有直接选择AppJoint依赖,而是参考appjoint和Arouter的思路重新写了一遍AnnotationProcessor(原谅我不怎么会写gradle-plugins TAT),在重写的过程中发现了一些可以优化的地方:
关于依赖,在多模块下,虽说大家普遍都是在root.gradle里定义ext变量,但每个子项目的gradle里都得写一遍一样的代码块,其实这部分我认为都可以抽出来到root.gradle里统一管理,我这里采取的是在root.gradle里写subprojects
来管理:参考代码
关于ModuleSpec和AppSpec,读了源码感觉这两个注解似乎是为了保证module能够初始化而使用,但其实可以通过ContentProvider来进行Modules的初始化,官方大佬的LifeCycle就是这么玩的~所以我这个重构的代码里没有这两个方法,只有一个@ServiceImpl
注解
关于Service的实现类,有没有一种可能会存在一个声明的Service有多个实现呢,为了兼顾这种情况,我在apt的时候解析的是HashMap<Class, SparseArray<Object>>
,注解允许设定int值来决定一个tag,这样应该可以解决某个Service对应多个impl实现的问题(说不定还可以搞定优先级呢~)
关于拦截器,其实个人感觉拦截器完全可以自己在Router模块里实现的。。。不过如果有库能提供也许会更好哈哈
关于多个module组合,其实这个在我们项目里是直接预留了一个test的application,需要组合哪些就直接依赖对应的module,所以其实感觉这个问题不大
关于组件的初始化顺序,有时候不排除会有这么一种可能:先初始化某个组件,再初始化另外一个组件(比如A组件后初始化B组件),这一块目前暂时没有很好(优雅)的想法,暂留一个坑
异想天开:因为在第二点里提到了provider,这货得在manifest里声明,在这里异想天开了一下:不知道能否编译期内写入到manifest里呢(利用占位?)这里我就暂时没有去探究了哈~不过幻想总是得有的
最后这里提供一下我这边实践的工程,因为思路是参照AppJoint的,所以没必要封装为库了,只是一个实现了上面除了拦截器的实践工程:Component
最后的最后,感谢作者大大的思路,真的很棒呢
尴尬了。。搞错了
java.lang.IllegalAccessError: Illegal class access: 'io.github.prototypez.appjoint.AppJoint' attempting to access 'com.xxxxx.xxxx.xxxxxxxxx' (declaration of 'io.github.prototypez.appjoint.AppJoint' appears in /data/app/com.xxxx.xxxxxxx-1/base.apk:classes2.dex)
Execution failed for task ':app:transformClassesWithAppJointForAlphaDebug'.
groovy.lang.GroovyRuntimeException: Ambiguous method overloading for method java.io.FileInputStream#.
Cannot resolve which method to invoke for [null] due to overlapping prototypes between:
[class java.io.FileDescriptor]
[class java.io.File]
[class java.lang.String]
使用@Serviceprovider 注解的类有时候会被实例化多次,导致不同的时刻调用 AppJoint.service(A.class) 方法会返回不同的对象。
AppJoint 目前在Gradle7.0+版本上已经不能运行了 好像是一些Api发生了变动不支持部分代码生成
你好,我在使用您的框架的过程中,发现了一下bug,麻烦您看一下,是我使用的方式不对吗。过程如下:
build:gradle 4.2
gradle-6.7.1-all.zip
后出现的
不知是否你们代码量不多,或者对编译耗时这个要求不高,这个插件对于大点项目的耗时是很夸张的。
假增量,在Transform里面写了支持增量,但实际并没有这么做,还是全局遍历
读所有问题来做解析注解完全没必要,可以利用apt生成份包含有这个注解的类,从而来加速解析速度,每个类都找javassist去解析判断,性能很差的,大海捞针一样。
如果升级Gradle之后你仍然想使用AppJoint组件化框架-可以看下这里
I just cloned AppJoint repository and found out that for some reason it contains gradle-4.1-all.zip (almost 86 MB).
Is it there by mistake?
报错:java.io.FileNotFoundException:/core/build/intermediates/intermediate-jars/debug/core_repackage/io/github/prototypez/appjoint/AppJoint.class (No such file or directory)
后来我看了demo里的,发现是classpath 'com.android.tools.build:gradle:3.1.4
就改为 classpath 'com.android.tools.build:gradle:3.1.4' 这个问题就解决了
运行一下app这个项目 断点打在Module1Application.onCreate 可以看到 AppJoint.moduleApplications 有重复的 module Application
建议在ModuleSpec添加优先级参数priority,根据优先级先后调用初始化Application
Task :standalone:templatestandalone:compileDebugJavaWithJavac FAILED
错误:无法访问ModuleSpec
找不到io.github.prototypez.appjoint.core.ModuleSpec的类文件
场景:模块单独添加application开发的时候发现的问题
module中的AppJoint.service返回null
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.