基於騰訊瀏覽服務 TBS 實現應用內開啟並瀏覽 Office 檔案

亦楓發表於2019-03-02

眾所周知,Android App 在國內環境下實現應用內開啟本地或位於遠端伺服器上 word、excel、pdf 等 Office 文件的功能遠遠沒有 iOS 系統方便得多。之前也寫過一篇文章,羅列出現有解決方案間的優劣對比。直到最近,在網上又發現一種新的解決方案:騰訊瀏覽服務(簡稱:TBS)。

TBS 簡單介紹


正如官網所言,依託 X5 核心強大的能力,TBS 致力於提供優化移動端瀏覽體驗的整套解決方案。TBS 雖然核心在於提供一套 SDK 解決傳統 WebView 的諸多使用問題。但是,利用其增強瀏覽能力,我們還能夠使用這套 SDK 實現應用內的檔案瀏覽功能、視訊播放功能等。更多詳細功能,可以參考官網介紹:

x5.tencent.com/tbs/product…

然而,美中不足的是,在瀏覽檔案方面,官方沒有提供完善的使用文件和 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 網站,有需要的朋友可以訪問地址:

github.com/Mike-bel/Tb…

關於我:亦楓,部落格地址:yifeng.studio/,新浪微博:IT亦楓

微信掃描二維碼,歡迎關注我的個人公眾號:安卓筆記俠

不僅分享我的原創技術文章,還有程式設計師的職場遐想

相關文章