我一直在努力将相机功能集成到我的应用程序中,但是不希望出现以下错误:
E/ContextImpl:尝试从非可视上下文com.camtest.App@385f002访问视觉服务WindowManager:Visual services,例如WindowManager、WallpaperService或LayoutInflater应该从Activity或其他可视上下文中访问。使用Activity或使用Context#createWindowContext(int, Bundle)创建的上下文,它们已调整为屏幕上某个区域的配置和可视边界。
java.lang.IllegalAccessException: 尝试从非可视上下文com.camtest.App@385f002访问视觉服务WindowManager
这个错误是由这行代码引起的:
final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
我查看了如何实现createWindowContext
,但由于一些目标设备较旧且不符合升级到Android 11的条件,因此createWindowContext
不是一个选项。
第一次尝试时,我按照CodeLabs之一来实现CameraX。相机的表现与预期相同,但触发了异常。所以我找到了一个不同的示例来实现CameraX,但我仍然遇到了相同的IllegalAccessException
异常。
有任何建议吗?
package com.camtest;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class CamTest extends AppCompatActivity {
private Executor executor = Executors.newSingleThreadExecutor();
private int REQUEST_CODE_PERMISSIONS = 9001;
private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA", "android.permission.WRITE_EXTERNAL_STORAGE"};
PreviewView mPreviewView;
ImageView captureImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.camera_test);
mPreviewView = findViewById(R.id.camera);//was previewView
captureImage = findViewById(R.id.captureImg);
if(allPermissionsGranted()){
startCamera(); //start camera if permission has been granted by user
} else{
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
}
}
private void startCamera() {
final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); //This line triggers `E/ContextImpl: Tried to access visual service WindowManager from a non-visual Context`
cameraProviderFuture.addListener(new Runnable() {
@Override
public void run() {
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
bindPreview(cameraProvider);
} catch (ExecutionException | InterruptedException e) {
// No errors need to be handled for this Future.
// This should never be reached.
}
}
}, ContextCompat.getMainExecutor(this));
}
void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
Preview preview = new Preview.Builder().build();
ImageCapture imageCapture = new ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
Camera camera = cameraProvider.bindToLifecycle(
((LifecycleOwner) this),
cameraSelector,
preview,
imageCapture);
preview.setSurfaceProvider(
mPreviewView.getSurfaceProvider());
captureImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
File file = new File(getBatchDirectoryName(), mDateFormat.format(new Date())+ ".jpg");
ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
imageCapture.takePicture(outputFileOptions, executor, new ImageCapture.OnImageSavedCallback () {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
new Handler().post(new Runnable() {
@Override
public void run() {
Toast.makeText(CamTest.this, "Image Saved successfully", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onError(@NonNull ImageCaptureException error) {
error.printStackTrace();
}
});
}
});
}
public String getBatchDirectoryName() {
String app_folder_path = "";
app_folder_path = Environment.getExternalStorageDirectory().toString() + "/images";
File dir = new File(app_folder_path);
if (!dir.exists() && !dir.mkdirs()) {
}
return app_folder_path;
}
private boolean allPermissionsGranted(){
for(String permission : REQUIRED_PERMISSIONS){
if(ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){
return false;
}
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if(requestCode == REQUEST_CODE_PERMISSIONS){
if(allPermissionsGranted()){
startCamera();
} else{
Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show();
this.finish();
}
}
}
}
这个活动是由MainActivity的onCreate中下面的代码启动的:
Button button_test = findViewById(R.id.button_test);
button_test.setOnClickListener(view -> {
Intent intent = new Intent(MainActivity.this, CamTest.class);
startActivityForResult(intent,0);
});
编辑:完整的堆栈跟踪-
E/ContextImpl: Tried to access visual service WindowManager from a non-visual Context:com.camtest.App@dd90e6b Visual services, such as WindowManager, WallpaperService or LayoutInflater should be accessed from Activity or other visual Context. Use an Activity or a Context created with Context#createWindowContext(int, Bundle), which are adjusted to the configuration and visual bounds of an area on screen.
java.lang.IllegalAccessException: Tried to access visual service WindowManager from a non-visual Context:com.camtest.App@dd90e6b
at android.app.ContextImpl.getSystemService(ContextImpl.java:1914)
at android.content.ContextWrapper.getSystemService(ContextWrapper.java:803)
at androidx.camera.camera2.internal.Camera2UseCaseConfigFactory.<init>(Camera2UseCaseConfigFactory.java:50)
at androidx.camera.camera2.Camera2Config.lambda$defaultConfig$1(Camera2Config.java:60)
at androidx.camera.camera2.-$$Lambda$Camera2Config$g_hY10kZhqC56um0PalOLTzuFlU.newInstance(Unknown Source:0)
at androidx.camera.core.CameraX.lambda$initAndRetryRecursively$9$CameraX(CameraX.java:575)
at androidx.camera.core.-$$Lambda$CameraX$u-Xx2b6YXY5GXNXRh-mDiDnHdpQ.run(Unknown Source:10)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
编辑 #2: 为了重现这个错误,必须启用 VmPolicy 的 StrictMode。在 MainActivity.onCreate 中添加以下代码:
if( BuildConfig.BUILD_TYPE.contentEquals( "debug" ) ){
/*StrictMode.setThreadPolicy( new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());*/
StrictMode.setVmPolicy( new StrictMode.VmPolicy.Builder()
.detectAll()//.detectNonSdkApiUsage()
.penaltyLog()
.build());
}
编辑 #3: 从 CameraX 版本 1.0.0-beta12 升级到 1.0.0-rc1(截至今天的当前版本)没有产生任何影响。
Application
上下文,因为我猜测com.camtest.App
就是这个。您能否发布完整的堆栈跟踪? - CommonsWareContext
来自哪里。如果您没有使用最新的CameraX,可以尝试升级。否则,如果您有能力创建一个重现问题的示例项目,可以在问题跟踪器上提交问题。一时之间,感觉您没有做错任何事情。 - CommonsWaresurfaceManagerProvider.newInstance(mAppContext,
。 - Pnemonic