@Author GQ 2017年01月12日
基本每个项目中有附件下载和查看功能,我这里选择腾讯TBS文件浏览服务
记录一下集成遇到的问题和心得 (由于项目使用Kotlin写的,所以截取代码片段)
一开始使用的是https://github.com/ZhongXiaoHong/superFileView
后来用着出问题了,还是自己重新写一下好了,顺便记录下来
so
放在 armeabi
下jar
放在 libs
下app
下的 build.gradle
中 ndk {
abiFilters "armeabi", "x86"
}
RelativeLayout
,将展示文件的tbsReaderView
add进去<RelativeLayout
android:id="@+id/rl_file"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="5dp"
android:paddingRight="5dp"/>
ReaderCallback
接口的 onCallBackAction
方法,内容空着就行class TbsFileActivity : AppCompatActivity(), TbsReaderView.ReaderCallback {
override fun onCallBackAction(p0: Int?, p1: Any?, p2: Any?) {
}
}
//缓存文件路径 ,APK_FILE是在sd卡的生成的文件夹名称"test",CACHE为"/cache"
val cacheDir: String = Environment.getExternalStorageDirectory().absolutePath + "/" + APK_FILE + CACHE
private var mTbsReaderView: TbsReaderView? = null
private var mFileName: String = ""//eg: 哈哈.doc
//避免输入法界面弹出后遮挡输入光标的问题
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE or WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
...
mTbsReaderView = TbsReaderView(this, this)
rl_file.addView(mTbsReaderView, RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT))
//初始化数据
initData()
fun initData() {
//这个"path"是上一个界面传来的参数暂时忽略
filePath = intent.getSerializableExtra("path") as String
Log.e(TAG, "文件path:" + filePath);
if (filePath.isNullOrEmpty()) {
ToastUtil.showInCenter("文件不存在")
} else {
mFileName = getFileName(filePath!!)
if (isLocalExist()) {
//缓存有直接读取
displayFile(getLocalFile())
} else {
//没有请求网络
if (filePath!!.contains("http")) {
downLoadFromNet(filePath!!)
} else {
ToastUtil.showInCenter("文件链接不合法")
}
}
}
}
private fun downLoadFromNet(url: String) {
val cacheFile = getCacheFile(url)
if (cacheFile.exists()) {
if (cacheFile.length() <= 0) {
cacheFile.delete()
return
}
}
val myFileCallback: FileCallback = object : FileCallback(cacheDir, mFileName) {
override fun onSuccess(response: Response?) {
displayFile(response?.body())
}
override fun downloadProgress(progress: Progress?) {
super.downloadProgress(progress)
}
}
OkGo.get(url).execute(myFileCallback)
}
fun displayFile(mFile: File?) {
//这里百度查到的资料
if (mFile != null && !TextUtils.isEmpty(mFile.toString())) {
//增加下面一句解决没有TbsReaderTemp文件夹存在导致加载文件失败
val bsReaderTemp = "/storage/emulated/0/TbsReaderTemp"
val bsReaderTempFile = File(bsReaderTemp)
if (!bsReaderTempFile.exists()) {
Log.e("准备创建/storage/emulated/0/TbsReaderTemp!!")
val mkdir = bsReaderTempFile.mkdir()
if (!mkdir) {
Log.e("创建/storage/emulated/0/TbsReaderTemp失败!!!!!")
}
}
//加载文件,这里的传参的key和value不需要改动
val localBundle = Bundle()
localBundle.putString("filePath", mFile.toString())
localBundle.putString("tempPath", Environment.getExternalStorageDirectory().toString() + "/" + "TbsReaderTemp")
//这步必须有,不然会报错 TbsReaderView: init Failed!
if (this.mTbsReaderView == null)
this.mTbsReaderView = getTbsReaderView(this)
val bool = this.mTbsReaderView!!.preOpen(getFileType(mFile.toString()), false)
if (bool) {
mTbsReaderView?.openFile(localBundle)
}
} else {
Log.e("文件路径无效!")
}
}
public override fun onDestroy() {
super.onDestroy()
mTbsReaderView?.onStop();
}
//查看本地文件是否存在
private fun isLocalExist(): Boolean {
return getLocalFile().exists()
}
//获取本地文件
private fun getLocalFile(): File {
return File(cacheDir, mFileName)
}
//新建TbsReaderView
private fun getTbsReaderView(context: Context): TbsReaderView {
return TbsReaderView(context, this)
}
//获取缓存文件
private fun getCacheFile(url: String): File {
val cacheFile = File(cacheDir + getFileName(url))
Log.e(TAG, "缓存文件 = " + cacheFile.toString())
return cacheFile
}
//根据链接获取文件名(带类型的),具有唯一性
private fun getFileName(url: String): String {
return Md5Util.hashKey(url) + "." + getFileType(url)
}
//拼接文件类型后缀
private fun getFileType(paramString: String): String {
var str = ""
if (TextUtils.isEmpty(paramString)) {
return str
}
val i = paramString.lastIndexOf('.')
if (i <= -1) {
return str
}
str = paramString.substring(i + 1)
return str
}
object Md5Util {
private fun bytesToHexString(bytes: ByteArray): String {
val sb = StringBuilder()
for (i in bytes.indices) {
val hex = Integer.toHexString(0xFF and bytes[i].toInt())
if (hex.length == 1) {
sb.append('0')
}
sb.append(hex)
}
return sb.toString()
}
fun hashKey(key: String): String {
var hashKey: String
try {
val mDigest = MessageDigest.getInstance("MD5")
mDigest.update(key.toByteArray())
hashKey = bytesToHexString(mDigest.digest())
} catch (e: NoSuchAlgorithmException) {
hashKey = key.hashCode().toString()
}
return hashKey
}
}
show()
方法companion object {
var filePath: String? = null
fun show(context: Context, url: String) {
var intent = Intent(context, TbsFileUI::class.java)
val bundle = Bundle()
bundle.putSerializable("path", url)
intent.putExtras(bundle)
context.startActivity(intent)
}
}
注意别忘了加权限配置
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Application
初始化
QbSdk.initX5Environment(contxt, null);
QbSdk.setDownloadWithoutWifi(true);//设置支持非Wifi下载
在其他页面调用即可
var fileUrl = "http://这里是一个网络文件地址,百度随便找一个"
fileUrl?.let { it -> TbsFileActivity.show(this, it) }