眾所周知,Android App 在國內環境下實現應用內開啟本地或位於遠端伺服器上 word、excel、pdf 等 Office 文件的功能遠遠沒有 iOS 系統方便得多。之前也寫過一篇文章,羅列出現有解決方案間的優劣對比。直到最近,在網上又發現一種新的解決方案:騰訊瀏覽服務(簡稱:TBS)。
TBS 簡單介紹
正如官網所言,依託 X5 核心強大的能力,TBS 致力於提供優化移動端瀏覽體驗的整套解決方案。TBS 雖然核心在於提供一套 SDK 解決傳統 WebView 的諸多使用問題。但是,利用其增強瀏覽能力,我們還能夠使用這套 SDK 實現應用內的檔案瀏覽功能、視訊播放功能等。更多詳細功能,可以參考官網介紹:
然而,美中不足的是,在瀏覽檔案方面,官方沒有提供完善的使用文件和 Demo 案例。經過一番折騰,和借鑑這篇文章 ,總算能夠實現應用內開啟 Office 文件的功能,於是整理於此。
本地檔案瀏覽
注意:TBS 只能開啟瀏覽本地檔案,對於位於伺服器的遠端檔案,無法實現線上預覽,只能通過先下載再開啟的方式進行瀏覽。這裡我們先來看看如何使用 TBS 開啟本地檔案。
首先還是新增 SDK 依賴。下載 TBS 提供的 jar 包和 so 檔案,新增到工程中對應的 libs 和 jniLibs 目錄下。如圖:
然後在 app/build.gradle 檔案中對 libs 目錄中 jar 檔案的依賴可以是這樣:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}複製程式碼
需要注意的是:TBS 目前只提供 armeabi 型別 CPU 架構的 so 庫。當然,也可以將 so 檔案放置於 libs 目錄下,只不過需要在 app/build.gradle 中額外修改 so 檔案依賴配置:
android {
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}複製程式碼
配置完成之後,再來看看如何使用。這裡主要使用的是 TbsReaderView
類。在宿主 Activity 中實現 ReaderCallback
介面,並通過 Java 程式碼動態建立 TbsReaderView 物件,將其新增到 content view 當中。比如:
mTbsReaderView = new TbsReaderView(this, this);
RelativeLayout rootRl = (RelativeLayout) findViewById(R.id.rl_root);
rootRl.addView(mTbsReaderView, new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))複製程式碼
ReaderCallback
介面提供的方法可以不予處理(目前不知道有什麼用途,但是一定要實現這個介面類):
@Override
public void onCallBackAction(Integer integer, Object o, Object o1) {
}複製程式碼
可能你會有疑惑,為什麼不將 TbsReaderView
放在 layout 佈局檔案中,而是在程式碼中手動 add 進去。經測試,這麼做會報錯,提示找不到這個類。然後我們檢視 TbsReaderView
原始碼,發現只有這麼一個建構函式:
public TbsReaderView(Context var1, TbsReaderView.ReaderCallback var2) {
super(var1.getApplicationContext());
if(!(var1 instanceof Activity)) {
throw new RuntimeException("error: unexpect context(none Activity)");
} else {
this.d = var2;
this.a = var1;
this.e = new au(this);
}
}複製程式碼
而沒有提供含 AttributeSet
型別引數的建構函式,也就是說,這個版本的 TBS 只允許我們通過 new 的方式建立 TbsReaderView
例項並使用。
說完這個,再接著看如何開啟本地檔案。程式碼也很簡單:
private void displayFile() {
Bundle bundle = new Bundle();
bundle.putString("filePath", getLocalFile().getPath());
bundle.putString("tempPath", Environment.getExternalStorageDirectory().getPath());
boolean result = mTbsReaderView.preOpen(parseFormat(mFileName), false);
if (result) {
mTbsReaderView.openFile(bundle);
}
}複製程式碼
可以看到,通過 Bundle
型別引數的形式向 TbsReaderView
物件傳遞檔案地址和另一個臨時目錄地址。這兩個資料,缺一不可。
這裡可能大家又有疑問,沒有文件介紹,我們是如何知道 Bundle
引數傳遞哪些資料的呢?還是檢視 TbsReaderView
原始碼,在該類中存在這兩個成員變數:
public static final String KEY_FILE_PATH = "filePath";
public static final String KEY_TEMP_PATH = "tempPath";複製程式碼
第一個引數很好理解,第二個引數不知道什麼意思,根據名字需要的應該是一個臨時目錄地址,反正傳上就對了。這裡順便教大家兩個技巧,以後閱讀 Jar 包原始碼時也許能夠用得上:
1,如何搜尋 jar 包中的指定類檔案?
使用 jar -tf
命令能夠列出 jar 包中的內容,如果再配上 grep 命令便能搜尋指定類檔案,如:
yifeng:desktop yifeng $ jar -tf tbs_sdk_thirdapp.jar | grep -i TbsReaderView
com/tencent/smtt/sdk/TbsReaderView$ReaderCallback.class
com/tencent/smtt/sdk/TbsReaderView.class複製程式碼
2,如何搜尋 jar 包中的指定字串?
使用 zipgrep 命令,如果搜尋的關鍵字包含空格,則需要使用引號:
yifeng:desktop yifeng $ zipgrep filePath tbs_sdk_thirdapp.jar
com/tencent/smtt/sdk/TbsReaderView.class:Binary file (standard input) matches複製程式碼
回到 TBS 使用上,最後別忘了在 onDestroy()
生命週期函式中新增:
@Override
protected void onDestroy() {
super.onDestroy();
mTbsReaderView.onStop();
}複製程式碼
至此,使用 TBS 實現應用內開啟預覽本地 Office 檔案的全部過程結束,我們來看一下效果圖:
可以看到,第一次使用需要下載騰訊提供的 pdf 瀏覽外掛,之後開啟時則無需再次載入。
更多時候,我們預覽的檔案都位於遠端伺服器上。所以,我們需要預先下載檔案,下載完成之後再使用 TBS 開啟本地檔案。感興趣的話,不妨繼續來看。
遠端檔案下載
Android App 下載檔案的方式有很多,這裡我們使用一種最簡單的方式來做,利用系統提供的 DownloadManager
類來實現下載功能,並監聽下載進度。
先來看看如何利用 DownloadManager
下載檔案。根據使用場景,這裡我遮蔽預設的通知欄提示訊息,並設定本地存放位置,相關程式碼如下:
mDownloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mFileUrl));
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, mFileName);
request.allowScanningByMediaScanner();
request.setNotificationVisibility(Request.VISIBILITY_HIDDEN);
mRequestId = mDownloadManager.enqueue(request);複製程式碼
然後是監聽下載進度,使用的是 ContentObserver
類。自定義一個繼承自 ContentObserver
的監聽類:
private class DownloadObserver extends ContentObserver {
private DownloadObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
queryDownloadStatus();
}
}複製程式碼
在 onChange()
回撥方法中實時查詢下載進度。當查詢到下載完成時,使用 TBS 開啟下載到本地對應目錄的檔案:
private void queryDownloadStatus() {
DownloadManager.Query query = new DownloadManager.Query().setFilterById(mRequestId);
Cursor cursor = null;
try {
cursor = mDownloadManager.query(query);
if (cursor != null && cursor.moveToFirst()) {
//已經下載的位元組數
int currentBytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
//總需下載的位元組數
int totalBytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
//狀態所在的列索引
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
Log.i("downloadUpdate: ", currentBytes + " " + totalBytes + " " + status);
mDownloadBtn.setText("正在下載:" + currentBytes + "/" + totalBytes);
if (DownloadManager.STATUS_SUCCESSFUL == status && mDownloadBtn.getVisibility() == View.VISIBLE) {
mDownloadBtn.setVisibility(View.GONE);
mDownloadBtn.performClick();
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}複製程式碼
做好這些準備後,在下載檔案的時候註冊監聽:
mDownloadObserver = new DownloadObserver(new Handler());
getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"), true, mDownloadObserver);複製程式碼
並在 onDestroy()
方法中取消監聽:
@Override
protected void onDestroy() {
super.onDestroy();
if (mDownloadObserver != null) {
getContentResolver().unregisterContentObserver(mDownloadObserver);
}
}複製程式碼
最後,別忘記在 Manifest 清單檔案中新增相應使用許可權(許可權含義,就不再一一介紹):
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"/>
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>複製程式碼
總體來看,TBS 提供的檔案瀏覽功能還是很強大的。據官網介紹,目前能夠支援 42 種不同格式的檔案。除此之外,TBS 還有一些其他的用途也非常值得嘗試。只是,期待官方能夠提供更多的使用文件介紹,幫助我們更方便地使用起來。
備註:
本文相關程式碼已上傳至 GitHub 網站,有需要的朋友可以訪問地址:
關於我:亦楓,部落格地址:yifeng.studio/,新浪微博:IT亦楓
微信掃描二維碼,歡迎關注我的個人公眾號:安卓筆記俠
不僅分享我的原創技術文章,還有程式設計師的職場遐想