RxJava API使用示例
概述
详细
一、项目概述
本Demo是为了方便大家了解RxJava的API,我将所有的RxJava API(至少是官方文档中提到的)都写在一个android apk中,并在其中附以功能描述,代码示例,marble-diagram(Rx用来描述数据及其处理流程的图), 以及一些使用场景. 所有的资料都是在APK中,使用的时候不会消耗任何流量,而且你可以在任何时候任何地方学习使用.
示例程序的特点如下:
1. API涵盖全面: 包含了核心库及所有扩展实现库200个左右的API.
2. 数据本地化,无需流量: 示例中的所有数据和图片都是本地加载的,所以无需消耗流量.
3. 示例代码都是从源码中直接生成,所以看起来跟代码直接运行的效果是一样的
二、主界面展示
上图为整个Demo的运行示例截图:
-
左边为Rxjava主要组件及运算符类别入口
-
右边为单个运算符的marble-diagram, 详细的运算符列表,代码示例以及代码的执行结果。
-
点击相应的运算符,界面会切换到该运算符的marble-diagram以及示例代码,同时会执行该代码,将执行结果输出到结果区。
三、代码实现
在整个Demo中,主要部分就是RxJava API运算子的呈现,包括marble图,API描述,示例代码,运算结果的展现
及不同运算子切换的交互。
所有的UI组成部分都是以插件的形式插入到Demo的UI体系中,设计图如下:
APIBaseActivity: API详情主Activity,包含了重定向输出结果到结果view以及整合DisplayPluginManager中所有View的展示的功能。
DisplayPluginManager: 管理所有的展示Plugin。
Plugin: 展示Plugin的接口类,主要负责根据不同的操作符ID提供不同的插件View。
MarbleDiagramPlugin: Marble图展示插件。
DescriptionPlugin: API描述展示插件。
SampleCodePlugin: 示例代码展示插件。
下边详细介绍这四部分的核心实现:
Marble图
marble图的地址是我在看官方文档的时候,手动扣取的,所有地址都保存在项目的MarbleDiagramPlugin.java中
最初demo使用的是动态获取marble图地址,由于原图需要消耗流量且图片较大,所以我是将所有图片从网络拉取到本地直接打包到apk中,而且在这个过程中对图片进行了进一步的打包压缩。
-
读取marble地址配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | # 将原来的注册地址的代码块处理并读取出来 def ProcessRegisterBlock(lines): if len (lines) = = 0 : return None mb = [] for line in lines: line = line.strip( '\r\n ,);' ) line = line.replace( '\"' ,''); if len (line) < 10 or not line.startswith( 'http' ): continue mb.append(line) return mb # 查找到Constants对应的Key def FindKey(line): start = line.find( 'Constants.' ) end = line.find( ',' ,start) return line[start: end] # 将名称转化为android的资源描述符名称 def Url2Id(url): start = url.rfind( '/' ) if start < 0 : return None return 'R.drawable.' + url[start + 1 :].replace( '.' , '_' ).lower() codes = {} # 拼装待生成的目标代码的路径 dest = os.path.join( 'app' , 'src' , 'main' , 'java' , 'union' , 'uc' , 'com' , 'rxjava_example' , 'plugin' ) if not os.path.exists(dest): os.path.makedirs(dest) key = "" # 打开marble图地址配置源码文件,并逐行扫描处理 with open (os.path.join(dest, 'MarbleDiagramPlugin.java' ), 'r' ) as input : block = [] find_add = False for line in input : if len (line.strip()) = = 0 : continue if line.find( 'add(Constants.' ) > = 0 : find_add = True if len (block) > 0 : code = ProcessRegisterBlock(block) codes[key] = code block = [] key = FindKey(line) http_start = line.find( '\"http' ) if http_start > = 0 : http_end = line.find( '\"' , http_start + 1 ) if http_end > = 0 : block.append(line[http_start:http_end]) elif find_add: block.append(line) if find_add and len (block) > 0 : code = ProcessRegisterBlock(block) codes[key] = code |
2. 生成Marble图资源配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | # 新建marble图资源配置文件 with open (os.path.join(dest, 'MarbleDiagram.java' ), 'w' ) as output: # 生成header header = ''' package union.uc.com.rxjava_example.plugin; import java.util.HashMap; import java.util.Map; import union.uc.com.rxjava_example.contants.Constants; import union.uc.com.rxjava_example.R; public class MarbleDiagram{ private Map<String, Integer[]> mCodes = new HashMap<>(); public MarbleDiagram(){ ''' footer = ''' } public Integer[] get(String key){ return mCodes.get(key); } private void add(String key, Integer... urls) { mCodes.put(key, urls); } } ''' output.write(header) # 生成footer for key,code in codes.items(): if code = = None : continue s = "" if len (code) = = 1 : code0 = Url2Id(code[ 0 ]) s = '\nadd(%s,%s);' % (key, code0) elif len (code) = = 2 : code0 = Url2Id(code[ 0 ]) code1 = Url2Id(code[ 1 ]) s = '\nadd(%s,%s,%s);' % (key, code0, code1) elif len (code) = = 3 : code0 = Url2Id(code[ 0 ]) code1 = Url2Id(code[ 1 ]) code2 = Url2Id(code[ 2 ]) s = '\nadd(%s,%s,%s,%s);' % (key, code0, code1, code2) elif len (code) = = 4 : code0 = Url2Id(code[ 0 ]) code1 = Url2Id(code[ 1 ]) code2 = Url2Id(code[ 2 ]) code3 = Url2Id(code[ 3 ]) s = '\nadd(%s,%s,%s,%s,%s);' % (key, code0, code1, code2, code3) output.write(s) output.write(footer) #下载图片 dir_drawable = os.path.join( 'app' , 'src' , 'main' , 'res' , 'drawable' ) for key,code in codes.items(): if code ! = None : for c in code: os.system( 'wget %s -P imgs' % (c )) # 重命名图片 for root, dirs, files in os.walk( 'imgs' ): for file in files: src = os.path.join(root, file ) dest = os.path.join(dir_drawable, file .lower().replace( '.' , '_' ) + '.png' ) os.rename(src, dest) |
3. 压缩图片,为减小API尺寸,对marble图进行了简单压缩
1 2 3 4 5 6 7 8 9 10 | dir_drawable = os.path.join( 'app' , 'src' , 'main' , 'res' , 'drawable' ) for root, dirs, files in os.walk(dir_drawable): for file in files: if not file .endswith( '.png' ): continue src = os.path.join(root, file ) img = Image. open (src) img = img.resize(( 400 , 200 ),Image.ANTIALIAS) img.save(src) del img |
API描述
API描述UI插件的实现是在DescriptionPlugin中,该Plugin主要是根据操作符id生成TextView并设置内容文本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | public class DescriptionPlugin implements DisplayPluginManager.Plugin { @Override public Tuple.Tuple2<Observable<View>, View> getView( final Context context, String key) { // 创建描述TextView, 为提高内存性能,避免无效引用,使用WeakReference. final TextView textView = new TextView(context); final Reference<TextView> ref = new WeakReference<>(textView); Observable<View> o = Observable.just(key) .map( new Func1<String, Integer>() { @Override public Integer call(String s) { // 如果资源列表为空,则加载资源列表 if (mKeyToResource == null ) { load(); } // 读取id对应的资源文本内容 return mKeyToResource.get(s); } }).map( new Func1<Integer, View>() { @Override public View call(Integer integer) { // 更新TextView 文本 textView.setText(integer); return textView; } }); return new Tuple.Tuple2<>(o, (View) textView); } |
API示例代码
示例代码的实现是在SampleCodePlugin中,其实现逻辑是根据操作符id读取示例代码,并使用markdown view渲染。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class SampleCodePlugin implements DisplayPluginManager.Plugin { @Override public Tuple.Tuple2<Observable<View>, View> getView( final Context context, String key) { // 创建Markdown view,使用weakreference,为了处理嵌入式touch事件的处理冲突,还需要重写其 onTeouchEvent方法。 MarkdownView markdownView = new MarkdownView(context){ @Override public boolean onTouchEvent(MotionEvent event) { requestDisallowInterceptTouchEvent( true ); return super .onTouchEvent(event); } }; markdownView.setBackgroundColor(Color.LTGRAY); final Reference<MarkdownView> ref = new WeakReference<>(markdownView); Observable<View> o = Observable.just(key) // .observeOn(Schedulers.io()) .map( new Func1<String, String>() { @Override public String call(String s) { // 根据id获取示例代码,示例代码已经提前生成。 return mSampleCode.get(s); } }) .observeOn(Schedulers.from(UIThreadExecutor.SINGLETON)) .map( new Func1<String, View>() { @Override public View call(String s) { // 使用markdownview加载示例代码 MarkdownView mv = ref.get(); if (mv != null ) { mv.loadMarkdown(s); } return mv; } }); return new Tuple.Tuple2<>(o, (View) markdownView); } |
根据操作符切换
每当点击操作符之后,界面更新的同时,程序也会执行该操作符对应的示例代码,并将示例代码结果输出到界面,输出的形式是重定向日志的方式,即将程序输出结果逐行append到结果输出界面。
四、项目结构
如上图,为整个demo工程的项目结构截图,为一个通用的android项目结构图,其中的
README.md:为Rxjava及部分项目的介绍。
*.py文件:为自动生成marble图及示例代码的脚本。
五、其他
本示例从逻辑到实现概要介绍如上,详细内容,请参考demo源码。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?