
在android开发中,集成ml kit文本识别功能时,开发者常遇到“waiting for the text optional module to be downloaded”错误。这通常是由于使用了不恰当的ml kit依赖库版本或类型所致。本文将深入探讨此问题,并提供一个简洁有效的解决方案,确保文本识别模块能够正确下载并初始化,从而顺利实现图像到文本的转换功能。
ML Kit为Android应用提供了强大的机器学习能力,其中文本识别功能允许开发者将图像中的文本提取出来。在使用ML Kit进行文本识别时,特别是对于某些语言或高级功能,ML Kit可能需要下载额外的模型模块。当遇到“Waiting for the text optional module to be downloaded. Please wait.”这样的错误提示时,通常意味着应用未能成功地获取或初始化所需的文本识别模型。
这个问题的核心往往不在于网络连接或设备存储空间,而更多地与项目配置中引入的ML Kit依赖库有关。Google提供了多种方式来集成ML Kit,包括通过Google Play Services(适用于托管模型)和直接通过独立的ML Kit库(适用于设备端模型)。不正确的依赖选择可能导致模块无法按预期下载或加载。
解决“Waiting for the text optional module to be downloaded”错误的关键在于确保项目引入了正确的、包含设备端文本识别模型的ML Kit库。
许多开发者可能会错误地引入如下依赖:
implementation 'com.google.android.gms:play-services-mlkit-text-recognition:18.0.2' implementation 'com.google.android.gms:play-services-mlkit-text-recognition-common:18.0.0'
这些play-services-mlkit-*依赖是基于Google Play Services的ML Kit API,它们通常依赖于Google Play Services来管理模型的下载和更新。然而,对于某些设备端(on-device)的文本识别功能,更推荐直接使用ML Kit提供的独立库,它包含了所需的模型,减少了对Play Services特定版本或模块下载的依赖。
正确的解决方案是替换或补充以下依赖:
将上述play-services-mlkit-*依赖替换为(或确保项目中包含)com.google.mlkit:text-recognition。这个库直接包含了设备端的文本识别模型,通常能够避免模块下载的问题。
// 移除或注释掉: // implementation 'com.google.android.gms:play-services-mlkit-text-recognition:18.0.2' // implementation 'com.google.android.gms:play-services-mlkit-text-recognition-common:18.0.0' // 添加或更新为: implementation 'com.google.mlkit:text-recognition:16.0.0-beta6' // 使用最新的稳定版本,此处为示例版本
请注意,16.0.0-beta6是一个示例版本号,建议查阅ML Kit官方文档以获取最新的稳定版本。
步骤总结:
一旦依赖配置正确,您的MainActivity中的ML Kit文本识别逻辑应能正常工作。以下是关键部分的示例代码,展示了如何初始化和使用TextRecognizer:
package com.gorkemtand.textrecognition;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.PopupMenu;
import android.widget.Toast;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.imageview.ShapeableImageView;
import com.google.mlkit.vision.common.InputImage;
import com.google.mlkit.vision.text.Text;
import com.google.mlkit.vision.text.TextRecognition;
import com.google.mlkit.vision.text.latin.TextRecognizerOptions; // 确保使用正确的TextRecognizerOptions
import java.io.IOException;
import java.util.Objects;
import android.Manifest; // 确保Manifest导入正确
public class MainActivity extends AppCompatActivity {
    private MaterialButton inputImageBtn;
    private MaterialButton recognizeTextBtn;
    private ShapeableImageView imageIv;
    private EditText recognizedTextEt;
    private static final String TAG = "MAIN_TAG";
    private Uri imageUri = null;
    private static final int CAMERA_REQUEST_CODE = 100;
    private static final int STORAGE_REQUEST_CODE = 101;
    private String[] cameraPermissions;
    private String[] storagePermissions;
    private ProgressDialog progressDialog;
    private TextRecognizer textRecognizer;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 初始化UI组件
        inputImageBtn = findViewById(R.id.inputImageBtn);
        recognizeTextBtn = findViewById(R.id.recognizeBtn);
        imageIv = findViewById(R.id.imageIv);
        recognizedTextEt = findViewById(R.id.recognizedTextEd);
        // 初始化权限数组
        cameraPermissions = new String[] {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
        storagePermissions = new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE};
        // 初始化进度对话框
        progressDialog = new ProgressDialog(this);
        progressDialog.setTitle("请稍候");
        progressDialog.setCanceledOnTouchOutside(false);
        // 初始化TextRecognizer
        // 使用TextRecognizerOptions.DEFAULT_OPTIONS来获取默认的设备端文本识别器
        textRecognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS);
        // 设置按钮点击监听器
        inputImageBtn.setOnClickListener(v -> showInputImageDialog());
        recognizeTextBtn.setOnClickListener(v -> {
            if(imageUri == null){
                Toast.makeText(MainActivity.this,"请先选择图片...",Toast.LENGTH_SHORT).show();
            } else {
                recognizeTextFromImage();
            }
        });
    }
    private void recognizeTextFromImage() {
        Log.d(TAG, "recognizeTextFromImage: 开始识别文本");
        progressDialog.setMessage("准备图片...");
        progressDialog.show();
        try {
            // 从Uri创建InputImage
            InputImage inputImage = InputImage.fromFilePath(this, imageUri);
            progressDialog.setMessage("识别文本中...");
            // 处理图片进行文本识别
            textRecognizer.process(inputImage)
                    .addOnSuccessListener(text -> {
                        // 识别成功
                        progressDialog.dismiss();
                        String recognizedText = text.getText();
                        Log.d(TAG, "onSuccess: 识别到的文本: "+recognizedText);
                        recognizedTextEt.setText(recognizedText);
                    })
                    .addOnFailureListener(e -> {
                        // 识别失败
                        progressDialog.dismiss();
                        Log.e(TAG, "onFailure: 文本识别失败", e);
                        Toast.makeText(MainActivity.this,"文本识别失败: "+e.getMessage(),Toast.LENGTH_SHORT).show();
                    });
        } catch (IOException e) {
            // 处理图片准备失败
            progressDialog.dismiss();
            Log.e(TAG, "recognizeTextFromImage: 准备图片失败", e);
            Toast.makeText(MainActivity.this,"准备图片失败: "+e.getMessage(),Toast.LENGTH_SHORT).show();
        }
    }
    // 省略 showInputImageDialog, pickImageGallery, pickImageCamera,
    // ActivityResultLauncher, 权限检查和请求等相关方法,
    // 这些方法在原代码中已经实现,且与ML Kit核心问题无关,但对于完整的应用是必需的。
    // 确保这些方法中的权限处理和图片选择逻辑是健全的。
    private void showInputImageDialog() {
        PopupMenu popupMenu = new PopupMenu(this, inputImageBtn);
        popupMenu.getMenu().add(Menu.NONE,1,1,"相机");
        popupMenu.getMenu().add(Menu.NONE,2,2,"图库");
        popupMenu.show();
        popupMenu.setOnMenuItemClickListener(item -> {
            int id = item.getItemId();
            if(id == 1){
                Log.d(TAG, "onMenuItemClick: 点击相机...");
                if(checkCameraPermissions()){
                    pickImageCamera();
                } else {
                    requestCameraPermissions();
                }
            } else if(id == 2){
                Log.d(TAG, "onMenuItemClick: 点击图库...");
                if(checkStoragePermision()){
                    pickImageGallery();
                } else {
                    requestStoragePermission();
                }
            }
            return false;
        });
    }
    private void pickImageGallery(){
        Log.d(TAG, "pickImageGallery: ");
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setType("image/*");
        galleryActivityResultLauncher.launch(intent);
    }
    private ActivityResultLauncher<Intent> galleryActivityResultLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            result -> {
                if(result.getResultCode() == RESULT_OK){
                    Intent data = result.getData();
                    if (data != null) {
                        imageUri = data.getData();
                        Log.d(TAG, "onActivityResult: imageUri "+imageUri);
                        imageIv.setImageURI(imageUri);
                    }
                } else {
                    Log.d(TAG, "onActivityResult: 取消");
                    Toast.makeText(MainActivity.this,"已取消...",Toast.LENGTH_SHORT).show();
                }
            }
    );
    private void pickImageCamera(){
        Log.d(TAG, "pickImageCamera: ");
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.TITLE, "示例标题");
        values.put(MediaStore.Images.Media.DESCRIPTION, "示例描述");
        imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values);
        Intent intent  = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
        cameraActivityResultLauncher.launch(intent);
    }
    private ActivityResultLauncher<Intent> cameraActivityResultLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            result -> {
                if(result.getResultCode() == RESULT_OK){
                    Log.d(TAG, "onActivityResult: imageUri "+imageUri);
                    imageIv.setImageURI(imageUri);
                } else {
                    Log.d(TAG, "onActivityResult: 取消");
                    Toast.makeText(MainActivity.this,"已取消", Toast.LENGTH_SHORT).show();
                }
            }
    );
    private boolean checkStoragePermision(){
        return ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == (PackageManager.PERMISSION_GRANTED);
    }
    private void requestStoragePermission(){
        ActivityCompat.requestPermissions(this,storagePermissions,STORAGE_REQUEST_CODE);
    }
    private boolean checkCameraPermissions(){
        boolean cameraResult = ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA) == (PackageManager.PERMISSION_GRANTED);
        boolean storageResult = ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) == (PackageManager.PERMISSION_GRANTED);
        return cameraResult && storageResult;
    }
    private void requestCameraPermissions(){
        ActivityCompat.requestPermissions(this,cameraPermissions, CAMERA_REQUEST_CODE);
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode){
            case CAMERA_REQUEST_CODE:{
                if (grantResults.length > 0){
                    boolean cameraAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                    boolean storageAccepted = grantResults[1] == PackageManager.PERMISSION_GRANTED;
                    if(cameraAccepted && storageAccepted){
                        pickImageCamera();
                    } else {
                        Toast.makeText(this, "需要相机和存储权限", Toast.LENGTH_SHORT).show();
                    }
                } else {
                    Toast.makeText(this,"已取消",Toast.LENGTH_SHORT).show();
                }
            }
            break;
            case STORAGE_REQUEST_CODE:{
                if(grantResults.length > 0){
                    boolean storageAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                    if (storageAccepted) {
                        pickImageGallery();
                    } else {
                        Toast.makeText(this, "需要存储权限", Toast.LENGTH_SHORT).show();
                    }
                }
            }
            break;
        }
    }
}AndroidManifest.xml 权限配置: 确保您的AndroidManifest.xml文件中声明了必要的权限:
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 如果目标API级别是33或更高,可以考虑使用READ_MEDIA_IMAGES替代WRITE_EXTERNAL_STORAGE --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
对于Android 13 (API 33) 及更高版本,推荐使用READ_MEDIA_IMAGES权限来访问媒体文件,而不是WRITE_EXTERNAL_STORAGE。
“Waiting for the text optional module to be downloaded”错误是ML Kit文本识别集成中常见的陷阱,但通过选择正确的com.google.mlkit:text-recognition依赖库,可以有效地解决此问题。理解不同ML Kit依赖的用途,并遵循官方推荐的集成方式,是确保应用稳定性和性能的关键。通过本文提供的解决方案和注意事项,开发者可以更顺利地在Android应用中实现强大的文本识别功能。
以上就是解决Android ML Kit文本识别模块下载失败的教程的详细内容,更多请关注php中文网其它相关文章!
 
                        
                        每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
 
                Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号