概述
詳細
一、項目概述
本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地址配置
# 將原來的注冊地址的代碼塊處理並讀取出來 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圖資源配置文件
# 新建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圖進行了簡單壓縮
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並設置內容文本。
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渲染。
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源碼。