一、项目概述
在移动端展示在线 office 文档(如 word、excel、ppt)是一项常见需求。用户点击链接即可在 app 内预览,无需下载应用或文件到本地。实现该功能主要有两种思路:
webview + 在线文档服务:利用微软 office online 或 google docs viewer,将文档 url 嵌入预览页面;
第三方 sdk(如腾讯 tbs/wps):集成浏览器内核或文档 sdk,支持在本地加载远程文档并渲染显示。
本文重点介绍两种方案的实现方法,并给出完整例子。
二、相关技术知识
1.webview 加载在线预览
office online url 模板:
https://view.officeapps.live.com/op/view.aspx?src=<url_encoded_doc_url>
google docs viewer:
https://docs.google.com/gview?embedded=true&url=<url_encoded_doc_url>
webview 配置:开启 javascript、支持缩放、调整缓存策略等。
2.腾讯 tbs sdk(x5 内核)
tbs 提供的 tbsreaderview 可加载 doc/docx/xls/ppt/pdf 等格式。
需在 app 初始化时预加载内核,并在布局中添加 tbsreaderview。
3.文件访问与权限
在线预览不需读写权限;
若下载到本地后用 sdk 打开,则需申请存储权限(android 6.0+ 运行时权限)。
4.性能与兼容
webview 方案依赖外部网络和服务;
sdk 方案包体较大但支持离线,可自定义 ui。
三、实现思路
3.1 方案一:webview + office online
在布局中放置一个 webview。
在 activity 中取得文档远程 url,进行 url 编码后拼接到 office online 查看地址。
配置 webview:启用 javascript、dom 存储、缩放控制。
调用 webview.loadurl(previewurl) 即可在线预览。
3.2 方案二:tbs sdk 离线预览
在项目 build.gradle 中引入 tbs sdk 依赖。
在 application 启动时初始化 x5:调用 qbsdk.initx5environment(...)。
在布局中添加 tbsreaderview。
在 activity 中下载或缓存文档到本地,然后调用 tbsview.openfile(bundle, null) 加载预览。
记得在 ondestroy() 中销毁 tbsreaderview。
四、整合代码
4.1 java 代码(mainactivity.java)
package com.example.officedocpreview; import android.manifest; import android.content.pm.packagemanager; import android.net.uri; import android.os.*; import android.view.view; import android.webkit.*; import android.widget.*; import androidx.annotation.nonnull; import androidx.appcompat.app.appcompatactivity; import androidx.core.app.activitycompat; import androidx.core.content.contextcompat; import com.tencent.smtt.sdk.tbsreaderview; import java.io.file; import java.io.fileoutputstream; import java.io.inputstream; import java.net.httpurlconnection; import java.net.url; public class mainactivity extends appcompatactivity { private static final int req_storage = 1001; private webview webview; private tbsreaderview tbsview; private framelayout container; private string remotedocurl = "https://example.com/test.docx"; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); webview = findviewbyid(r.id.web_view); tbsview = findviewbyid(r.id.tbs_view); container = findviewbyid(r.id.tbs_container); findviewbyid(r.id.btn_web_preview).setonclicklistener(v -> previewwithweb()); findviewbyid(r.id.btn_tbs_preview).setonclicklistener(v -> previewwithtbs()); } /** 方案一:webview + office online */ private void previewwithweb() { webview.setvisibility(view.visible); container.setvisibility(view.gone); websettings ws = webview.getsettings(); ws.setjavascriptenabled(true); ws.setbuiltinzoomcontrols(true); ws.setdisplayzoomcontrols(false); ws.setdomstorageenabled(true); string urlencoded = uri.encode(remotedocurl); string previewurl = "https://view.officeapps.live.com/op/view.aspx?src=" + urlencoded; webview.loadurl(previewurl); } /** 方案二:tbs sdk 预览,需要存储权限 */ private void previewwithtbs() { webview.setvisibility(view.gone); container.setvisibility(view.visible); if (contextcompat.checkselfpermission(this, manifest.permission.write_external_storage) != packagemanager.permission_granted) { activitycompat.requestpermissions(this, new string[]{manifest.permission.write_external_storage}, req_storage); } else { downloadandopenwithtbs(remotedocurl); } } @override public void onrequestpermissionsresult(int req, @nonnull string[] perms, @nonnull int[] grantresults) { if (req == req_storage && grantresults.length>0 && grantresults[0] == packagemanager.permission_granted) { downloadandopenwithtbs(remotedocurl); } else { toast.maketext(this, "存储权限被拒绝", toast.length_short).show(); } } /** 下载到本地后用 tbsreaderview 打开 */ private void downloadandopenwithtbs(string urlstr) { new thread(() -> { try { url url = new url(urlstr); httpurlconnection conn = (httpurlconnection) url.openconnection(); conn.connect(); inputstream is = conn.getinputstream(); file file = new file(getexternalfilesdir(null), "temp.docx"); fileoutputstream fos = new fileoutputstream(file); byte[] buf = new byte[4096]; int len; while ((len = is.read(buf))>0) fos.write(buf,0,len); fos.close(); is.close(); runonuithread(() -> openfileintbs(file.getabsolutepath())); } catch (exception e) { e.printstacktrace(); runonuithread(() -> toast.maketext(this, "下载失败", toast.length_short).show()); } }).start(); } private void openfileintbs(string filepath) { bundle params = new bundle(); params.putstring(tbsreaderview.key_file_path, filepath); params.putstring(tbsreaderview.key_temp_path, getexternalfilesdir(null).getpath()); boolean ok = tbsview.preopen(getfiletype(filepath), false); if (ok) { tbsview.openfile(params); } else { toast.maketext(this, "tbs 内核未加载或不支持", toast.length_short).show(); } } private string getfiletype(string path) { if (path == null || !path.contains(".")) return ""; return path.substring(path.lastindexof('.') + 1); } @override protected void ondestroy() { super.ondestroy(); tbsview.onstop(); } }
4.2 xml 布局与 manifest
<!-- androidmanifest.xml —— 声明 internet 与读写权限,并注册 tbsreaderview 权限 --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.officedocpreview"> <uses-permission android:name="android.permission.internet"/> <uses-permission android:name="android.permission.write_external_storage"/> <application android:allowbackup="true" android:label="officepreview" android:icon="@mipmap/ic_launcher" android:theme="@style/theme.appcompat.light.noactionbar"> <activity android:name=".mainactivity"> <intent-filter> <action android:name="android.intent.action.main"/> <category android:name="android.intent.category.launcher"/> </intent-filter> </activity> </application> </manifest> <!-- activity_main.xml —— 同时包含 webview 与 tbsreaderview --> <?xml version="1.0" encoding="utf-8"?> <framelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- webview 方式预览 --> <webview android:id="@+id/web_view" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone"/> <!-- tbs sdk 方式预览 --> <framelayout android:id="@+id/tbs_container" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone"> <com.tencent.smtt.sdk.tbsreaderview android:id="@+id/tbs_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </framelayout> <!-- 底部按钮 --> <linearlayout android:layout_gravity="bottom|center_horizontal" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="16dp"> <button android:id="@+id/btn_web_preview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="web 预览"/> <button android:id="@+id/btn_tbs_preview" android:layout_marginstart="16dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="tbs 预览"/> </linearlayout> </framelayout>
五、代码解读
1.webview 方案
将文档 url 进行 url 编码后拼接到 office online 查看地址;
在 webview 中启用 javascript、缩放支持及 dom 存储;
直接 loadurl() 即可,无需额外下载。
2.tbs sdk 方案
需提前在 application 或任意时机调用 qbsdk.initx5environment()(省略);
下载文档到 app 私有存储后,通过 tbsreaderview.preopen() 检测内核支持;
调用 openfile() 加载本地文档,支持 doc、xls、ppt、pdf 等多种格式;
在 ondestroy() 中调用 tbsview.onstop() 释放资源。
3.权限与生命周期
运行时申请写存储权限后才能保存到本地;
在 onpause()/ondestroy() 清理动画和 tbs 资源,避免内存泄漏。
六、项目总结
webview + office online:实现简单、无需集成第三方 sdk,依赖外部服务;
tbs sdk:支持离线预览和更多格式,但包体较大、需初始化内核;
本文示例将两种方案合二为一,按需选择并切换。
七、实践建议与未来展望
ui 优化:增加加载进度条、错误页面提示;
缓存策略:对已下载文件做缓存,下次直接读取;
更多格式:结合 open-source 渲染库(如 apache poi + pdf 转换)实现更多定制;
compose 时代:在 jetpack compose 中也可直接使用 androidview 嵌入 webview 或 tbs。
到此这篇关于android实现在线预览office文档的示例详解的文章就介绍到这了,更多相关android在线预览office内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论