You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android集成POI-Android读取Word文档遇SchemaTypeLoaderException错误求助

解决Apache POI-Android读取Word文件时的SchemaTypeLoaderException错误

这个错误本质是XMLBeans在Android环境下的类加载冲突,加上StAX库配置不当、POI依赖不完整导致的。下面是适配Android API22的完整可运行解决方案:

一、修正Gradle配置

首先调整项目和应用级的Gradle文件,确保依赖完整且无冲突,同时开启MultiDex(API22方法数限制必须):

项目级build.gradle

buildscript {
    ext.kotlin_version = '1.3.72'
    repositories {
        google()
        jcenter()
    }
    ext {
        poiVersion = '3.17'
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

应用级build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.example.diffchecker"
        minSdkVersion 21
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        multiDexEnabled true // 关键:API22必须开启,避免方法数超限
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'pub.devrel:easypermissions:2.0.1'
    implementation 'androidx.multidex:multidex:2.0.1' // 添加MultiDex依赖

    // 完整添加POI-Android依赖,排除冲突的StAX库
    implementation ("com.github.SUPERCILEX.poi-android:poi:$poiVersion") {
        exclude group: 'stax', module: 'stax-api'
        exclude group: 'javax.xml.stream', module: 'stax-api'
    }
    implementation ("com.github.SUPERCILEX.poi-android:poi-ooxml:$poiVersion") {
        exclude group: 'stax', module: 'stax-api'
        exclude group: 'javax.xml.stream', module: 'stax-api'
    }
}

二、完整Activity代码(适配API22)

包含权限申请、文件选择、Word读取的完整逻辑,注意资源自动关闭和错误处理:

import android.Manifest
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import org.apache.poi.xwpf.extractor.XWPFWordExtractor
import org.apache.poi.xwpf.usermodel.XWPFDocument
import pub.devrel.easypermissions.EasyPermissions
import java.io.IOException

class MainActivity : AppCompatActivity(), EasyPermissions.PermissionCallbacks {

    private val REQUEST_CODE_PICK_FILE = 1001
    private val STORAGE_PERMISSIONS = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 必须在使用POI前初始化StAX配置,避免XML解析冲突
        System.setProperty("org.apache.poi.javax.xml.stream.XMLInputFactory", "com.fasterxml.aalto.stax.InputFactoryImpl")
        System.setProperty("org.apache.poi.javax.xml.stream.XMLOutputFactory", "com.fasterxml.aalto.stax.OutputFactoryImpl")
        System.setProperty("org.apache.poi.javax.xml.stream.XMLEventFactory", "com.fasterxml.aalto.stax.EventFactoryImpl")

        // 检查权限后启动文件选择
        if (EasyPermissions.hasPermissions(this, *STORAGE_PERMISSIONS)) {
            launchWordPicker()
        } else {
            EasyPermissions.requestPermissions(
                this,
                "需要读取存储权限来访问Word文件",
                100,
                *STORAGE_PERMISSIONS
            )
        }
    }

    private fun launchWordPicker() {
        val pickIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
            // 指定只选择docx格式的Word文件
            type = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
            addCategory(Intent.CATEGORY_OPENABLE)
        }
        startActivityForResult(pickIntent, REQUEST_CODE_PICK_FILE)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_CODE_PICK_FILE && resultCode == RESULT_OK) {
            val fileUri = data?.data ?: return
            readWordContent(fileUri)
        }
    }

    private fun readWordContent(uri: Uri) {
        var doc: XWPFDocument? = null
        var extractor: XWPFWordExtractor? = null

        try {
            // 使用Kotlin的use函数自动关闭输入流,避免资源泄漏
            contentResolver.openInputStream(uri).use { inputStream ->
                doc = XWPFDocument(inputStream)
                extractor = XWPFWordExtractor(doc)
                val content = extractor.text

                // 这里可以替换为你自己的文本处理逻辑
                Toast.makeText(this, "读取成功!内容预览:\n${content.take(100)}...", Toast.LENGTH_LONG).show()
                println("完整Word内容:\n$content")
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Toast.makeText(this, "读取失败:${e.message}", Toast.LENGTH_SHORT).show()
        } finally {
            // 手动关闭剩余资源
            try {
                extractor?.close()
                doc?.close()
            } catch (ioe: IOException) {
                ioe.printStackTrace()
            }
        }
    }

    // EasyPermissions回调实现
    override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
        if (requestCode == 100) {
            launchWordPicker()
        }
    }

    override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
        Toast.makeText(this, "权限被拒绝,无法读取Word文件", Toast.LENGTH_SHORT).show()
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
    }
}

三、关键注意点

  1. MultiDex必须开启:API22的Dalvik虚拟机限制了单个DEX文件的方法数,POI依赖大量类,不开MultiDex会直接崩溃。
  2. 依赖完整性:必须同时添加poi和poi-ooxml依赖,前者处理旧版doc,后者处理新版docx,你需要的docx读取依赖后者。
  3. StAX初始化时机:必须在创建XWPFDocument之前设置系统属性,确保使用Aalto的StAX实现,避免Android原生XML解析库的冲突。
  4. 资源自动管理:使用Kotlin的use函数处理输入流,确保流自动关闭,同时在finally块手动关闭文档和提取器,避免内存泄漏。
  5. 权限处理:API22需要动态申请READ_EXTERNAL_STORAGE权限,使用EasyPermissions简化权限申请流程。

内容的提问来源于stack exchange,提问作者Adam Lee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 15:23:11