
在android开发中使用ml kit进行文本识别时,常遇到“waiting for the text optional module to be downloaded”的错误。这通常是由于依赖配置不当导致的。本教程将详细解析此问题,并提供通过引入正确的ml kit独立sdk依赖来解决模块下载失败的专业方法,确保文本识别功能稳定运行,避免不必要的等待和错误。
ML Kit是Google提供的一套强大的机器学习SDK,广泛应用于移动应用开发,其中文本识别功能尤其受到开发者青睐。然而,在实际开发中,部分开发者可能会遇到“Waiting for the text optional module to be downloaded. Please wait.”的错误信息,这通常发生在尝试初始化或使用文本识别器时。本文将深入分析此问题的原因,并提供一个专业且有效的解决方案。
当开发者在Android应用中集成ML Kit文本识别功能时,如果配置不当,可能会在运行时看到上述模块下载错误。此错误表明ML Kit尝试从Google Play服务下载必要的文本识别模型,但由于某种原因(如网络问题、Google Play服务版本不兼容或最常见的是错误的依赖配置),导致模型下载失败或无法启动下载。
ML Kit提供两种主要的集成方式:
原问题中,开发者使用了com.google.android.gms:play-services-mlkit-text-recognition:18.0.2和com.google.android.gms:play-services-mlkit-text-recognition-common:18.0.0这两个依赖。这些是基于Google Play服务的ML Kit文本识别API。当出现“Waiting for the text optional module to be downloaded”时,很可能是在尝试通过Google Play服务获取模型时遇到了障碍。
解决此问题的最直接和有效的方法是切换到ML Kit的独立SDK,它将文本识别模型直接打包到您的应用程序中,从而避免了运行时下载模型的步骤。
步骤一:移除现有依赖
首先,打开您的 app/build.gradle 文件,移除所有与ML Kit文本识别相关的Google Play Services依赖:
// 移除这些(或类似)依赖 // 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'
步骤二:添加ML Kit独立SDK依赖
然后,添加ML Kit独立SDK的文本识别依赖。请务必使用最新稳定版本,这里以16.0.0-beta6为例,但在实际开发中应查阅官方文档获取最新版本。
// 添加ML Kit独立SDK的文本识别依赖 implementation 'com.google.mlkit:text-recognition:16.0.0-beta6'
步骤三:同步Gradle项目
修改完 build.gradle 文件后,务必点击Android Studio工具栏中的“Sync Project with Gradle Files”按钮,或者执行 File > Sync Project with Gradle Files,以确保新的依赖被正确下载和配置。
尽管依赖是问题的核心,但为了完整性,我们仍然展示一个典型的ML Kit文本识别MainActivity代码结构。请注意,以下代码与原问题中的代码逻辑基本一致,因为问题不在于Java代码逻辑,而在于底层依赖。
package com.gorkemtand.textrecognition;
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 android.Manifest;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
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 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.TextRecognizer;
import com.google.mlkit.vision.text.latin.TextRecognizerOptions; // 注意这里仍是latin选项,如果需要其他语言,需更改
import java.io.IOException;
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,这里使用默认的拉丁语识别器
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 {
InputImage inputImage = InputImage.fromFilePath(this, imageUri);
progressDialog.setMessage("正在识别文本...");
textRecognizer.process(inputImage)
.addOnSuccessListener(text -> {
progressDialog.dismiss();
String recognizedText = text.getText();
Log.d(TAG, "onSuccess: recognizedText: " + 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();
}
}
// 显示图片选择对话框(相机/图库)
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() == Activity.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() == Activity.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;
}
}
}
}无论使用哪种ML Kit集成方式,都必须在 AndroidManifest.xml 中声明必要的权限:
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 如果您的应用目标API级别为29或更高,且需要访问共享存储,可能还需要READ_EXTERNAL_STORAGE --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
注意: 对于目标API级别30及以上的应用,WRITE_EXTERNAL_STORAGE 权限的行为有所改变。通常,您应该使用 MediaStore API 来处理图片,而不是直接读写外部存储。对于相机拍摄并保存到应用私有目录或 MediaStore 的情况,WRITE_EXTERNAL_STORAGE 权限可能不再是强制性的,但为了兼容旧设备和更广泛的场景,保留它仍然是合理的做法。
“Waiting for the text optional module to be downloaded”错误是ML Kit文本识别集成中常见的挑战,但通过将Google Play Services-based API依赖替换为ML Kit独立SDK依赖(即com.google.mlkit:text-recognition),可以有效地解决此问题。这种方法确保了ML模型直接集成到您的应用中,避免了运行时下载的复杂性和潜在问题,从而提供更稳定可靠的文本识别体验。
以上就是解决Android ML Kit文本识别模块下载问题的指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号