DeadObjectException 仅适用于 Android 10

问题描述 投票:0回答:0

我正在为 Android 版本 9 到 12(目前)开发智能手机/平板电脑应用程序。 对于版本 9、11 和 12,一切都很好,没有特别的问题。我在 Android 10 上尝试了我的应用程序,当它从初始屏幕切换到登录活动时,它崩溃并显示以下消息:

android.os.DeadObjectException
    at android.os.BinderProxy.transactNative(Native Method)
    at android.os.BinderProxy.transact(BinderProxy.java:575)
    at android.view.IWindow$Stub$Proxy.windowFocusChanged(IWindow.java:829)
    at com.android.server.wm.WindowState.reportFocusChangedSerialized(WindowState.java:3691)
    at com.android.server.wm.WindowManagerService$H.handleMessage(WindowManagerService.java:5254)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loop(Looper.java:237)
    at android.os.HandlerThread.run(HandlerThread.java:67)
    at com.android.server.ServiceThread.run(ServiceThread.java:44)

没有指定我的一些文件或部分代码的任何内容。

关于这个错误是什么的任何想法?

这是我的 SplashScreenActivity 代码:

package it.company.product.activities.initialization;

import butterknife.ButterKnife;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import it.company.product.BuildConfig;
import it.company.product.R;
import it.company.product.activities.dashboard.PatientDashboardActivity;
import it.company.product.activities.login.LoginActivity;
import it.company.product.application.App;
import it.company.product.databinding.ActivitySplashScreenBinding;
import it.company.product.di.InternalDatabaseInstance;
import it.company.product.di.NetworkInstance;
import it.company.product.license.LicenseManager;
import it.company.product.license.OnRegistrationListener;
import it.company.etmlib.datacommunication.update.OnUpdateDownloadListener;
import it.company.etmlib.datacommunication.update.SimpleEventBus;
import it.company.product.update.UpdateManager;
import it.company.etmlib.datacommunication.license.protocol.VerifyLicenseResponse;
import it.company.etmlib.datahandling.globalconfiguration.GlobalConfiguration;
import it.company.etmlib.datahandling.globalconfiguration.GlobalConfiguration.InternalCode;
import it.company.etmlib.utilities.Utils;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;

import com.jakewharton.retrofit2.adapter.rxjava2.HttpException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;
import javax.inject.Named;

import static it.company.etmlib.utilities.Utils.sleep;

@SuppressLint("CustomSplashScreen")
public class SplashScreenActivity extends Activity {

    private final Logger easyLog = LoggerFactory.getLogger(SplashScreenActivity.class.getSimpleName());

    private ActivitySplashScreenBinding viewBinding;

    private UpdateManager mUpdateManager;

    private LicenseManager mLicenseManager;

    private CompositeDisposable mCompositeDisposable;

    private static final String[] ANDROID_9_10_BLE_PERMISSIONS = new String[] {
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.CAMERA,
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.RECORD_AUDIO,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };

    private static final String[] ANDROID_11_BLE_PERMISSIONS = new String[] {
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.CAMERA,
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.RECORD_AUDIO,
    };
    
    @RequiresApi(api = Build.VERSION_CODES.S)
    private static final String[] ANDROID_12_BLE_PERMISSIONS = new String[] {
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.BLUETOOTH_ADVERTISE,
            Manifest.permission.BLUETOOTH_SCAN,
            Manifest.permission.BLUETOOTH_CONNECT,
            Manifest.permission.CAMERA,
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.RECORD_AUDIO,
    };

    @Inject
    InternalDatabaseInstance mInternalDatabaseInstance;

    @Inject
    NetworkInstance mNetworkInstance;

    @Inject
    @Named("Custom")
    SharedPreferences mCustomSharedPreferences;

    @Inject
    @Named("Default")
    SharedPreferences mDefaultSharedPreferences;

    @Inject
    SimpleEventBus mSimpleEventBus;

    private int mProgress = 20;
    private static final int STEP_PROGRESS = 20;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        viewBinding = ActivitySplashScreenBinding.inflate(getLayoutInflater());
        View view = viewBinding.getRoot();
        setContentView(view);

        configureLogBack();

        ButterKnife.bind(this);
        App application = (App)getApplication();
        application.getAppComponent().inject(this);

        mCompositeDisposable = new CompositeDisposable();

        mLicenseManager = new LicenseManager();
        mLicenseManager.setOnRegistrationListener(new OnRegistrationListener() {
            @Override
            public void onRegistrationSuccess() {

                mCustomSharedPreferences.edit().putBoolean(App.KEY_FIRST_TIME, false).apply();
                verifyLicense();
            }

            @Override
            public void onFinishActivity() {

                finish();
            }

            @Override
            public void onRegistrationExpired() {

            }
        });

        mUpdateManager = new UpdateManager(this, mNetworkInstance, mSimpleEventBus);
        mUpdateManager.setOnUpdateDownloadListener(new OnUpdateDownloadListener() {
            @Override
            public void onDownloadSuccess(File file) {

//                launchUpdateInstallation(file);
            }

            @Override
            public void onDownloadError() {

            }

            @Override
            public void onDownloadDelayed() {

            }
        });

        initializeProgress();

        View decorView = getWindow().getDecorView();
        int uiOptions = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE);

        decorView.setSystemUiVisibility(uiOptions);

        evaluateAppPermissions();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == 1234 && resultCode == Activity.RESULT_OK) {
            if (getPackageManager().canRequestPackageInstalls()) {

                easyLog.info("[onActivityResult] Evaluating permissions...");
                evaluateAppPermissions();
            }
        }
        else {

            easyLog.error("[onActivityResult] Error on requestCode or result for canRequestPackageInstalls(). Closing app...");
            new AlertDialog.Builder(this)
                    .setTitle(R.string.dialog_permission_error_title)
                    .setMessage(R.string.dialog_permission_error_message)
                    .setPositiveButton(R.string.button_ok, (dialog, which) -> finishAndRemoveTask())
                    .show();
        }
    }

    private void evaluateAppPermissions() {

        if(Build.VERSION.SDK_INT == Build.VERSION_CODES.P || Build.VERSION.SDK_INT == Build.VERSION_CODES.Q)
            ActivityCompat.requestPermissions(this, ANDROID_9_10_BLE_PERMISSIONS, GlobalConfiguration.PERMISSIONS_REQUEST_CODE);
        else if(Build.VERSION.SDK_INT == Build.VERSION_CODES.R)
            ActivityCompat.requestPermissions(this, ANDROID_11_BLE_PERMISSIONS, GlobalConfiguration.PERMISSIONS_REQUEST_CODE);
        else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
            ActivityCompat.requestPermissions(this, ANDROID_12_BLE_PERMISSIONS, GlobalConfiguration.PERMISSIONS_REQUEST_CODE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        int error = 0;
        for(int g=0; g<grantResults.length; g++) {

            easyLog.warn("[onRequestPermissionsResult] permission --> " + permissions[g] + " | granted --> " + grantResults[g]);
            if (requestCode != GlobalConfiguration.PERMISSIONS_REQUEST_CODE || grantResults[g] == PackageManager.PERMISSION_DENIED)
                error++;
        }

        if(error == 0) {

            // All missing permissions have been granted
            easyLog.info("[onRequestPermissionsResult] All missing permissions have been granted, proceeding...");
            verifyLicense();
        }
        else {

            // If after requesting the missing permissions some have not yet been granted, I notify the user and close the app
            easyLog.info("[onRequestPermissionsResult] Some permits have not been granted. Closing the app...");
            new AlertDialog.Builder(this)
                    .setTitle(R.string.dialog_permission_error_title)
                    .setMessage(R.string.dialog_permission_error_message)
                    .setPositiveButton(R.string.button_ok, (dialog, which) -> finishAndRemoveTask())
                    .show();
        }
    }

    private void initializeProgress() {
        runOnUiThread(() -> viewBinding.splashProgressBar.setProgress(mProgress));
    }

    void doProgress() {
        mProgress += STEP_PROGRESS;
        runOnUiThread(() -> viewBinding.splashProgressBar.setProgress(mProgress));
    }

    void setText(String text) {
        runOnUiThread(() -> viewBinding.splashProgressText.setText(text));
    }

    private void verifyLicense() {

        doProgress();
        setText(getString(R.string.progress_verifing_license));
        sleep(1000);

        mCompositeDisposable.add(
            mNetworkInstance.verifyLicense(this)
                .subscribeOn(Schedulers.io())
                .delay(1000, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe( this::manageLicenseStatus,
                            throwable -> {
                                if(throwable instanceof HttpException) {
                                    easyLog.error("[VerifyLicense] Http status -> "+ ((HttpException) throwable).code() + "\n" + Utils.stacktraceToString(throwable));
                                }
                                else {
                                    easyLog.warn("[VerifyLicense] Connessione internet assente, verifico la licenza in un secondo momento");
                                }
                            }
            )
        );
    }

    private void manageLicenseStatus(VerifyLicenseResponse response) {
        switch(response.getResponse().getType()) {
            case ok:
                if(response.getVersionAvailable() != null) {
                    mUpdateManager.showAvailableVersionDialog(mCompositeDisposable, response.getVersionAvailable());
                }
                else {
                    startConnection();
                }
                break;

            case not_found:
                startProductRegistrationDialog();
                break;

            case expired:
                mLicenseManager.showExpiredContractDialog(this);
                break;

            case blocked:
            case contract_blocked:
                mLicenseManager.showBlockedDeviceDialog(this);
                break;
        }
    }

    private void startProductRegistrationDialog() {
        mLicenseManager.showProductActivationDialog(this, mNetworkInstance, mCompositeDisposable);
    }

    void startConnection() {

        setText(getString(R.string.progress_gateway_initialization));
        sleep(1000);

        doProgress();
        setText(getString(R.string.progress_configuration));

        sleep(2000);
        searchActivePatient();
    }

    void searchActivePatient() {

        String patientCode = mCustomSharedPreferences.getString(App.KEY_PERMANENT_ASSIGNMENT_CODE, null);
        String patientCodeType = mCustomSharedPreferences.getString(App.KEY_PERMANENT_ASSIGNMENT_CODE_TYPE, null);

        if(patientCode != null && patientCodeType != null) {

            mCompositeDisposable.add(
                mInternalDatabaseInstance.getInternalDatabase().patientDao().getByCodeRx(patientCode, InternalCode.valueOf(patientCodeType))
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe( patient -> {
                                    Intent intent;

                                    // request PIN if feature is set
                                    if(mDefaultSharedPreferences.getBoolean(getString(R.string.key_feature_request_pin), false)){
                                        intent = new Intent(this, RequestPinActivity.class);
                                    }
                                    else {
                                        intent = new Intent(this, PatientDashboardActivity.class);
                                    }

                                    startActivity(intent);
                                    finish();
                                },
                                throwable -> {
                                    Intent intent = new Intent(this, LoginActivity.class);
                                    startActivity(intent);
                                    finish();
                                }
                    )
            );
        }
        else {

            Intent intent = new Intent(this, LoginActivity.class);
            startActivity(intent);
            finish();
        }
    }

    static final String LOGBACK_XML_DEBUG =

            "<configuration debug='true'>" +
                    "    <property name='LOG_DIR' value='/storage/emulated/0/Android/data/it.company.product.debug/files' />" +
                    "    <appender name='LogCat' class='ch.qos.logback.classic.android.LogcatAppender'>" +
                    "        <encoder>" +
                    "            <pattern>%msg%n</pattern>" +
                    "        </encoder>" +
                    "    </appender>" +
                    "    <appender name='LogFile' class='ch.qos.logback.core.rolling.RollingFileAppender'>" +
                    "        <file>${LOG_DIR}/log.txt</file>" +
                    "        <encoder>" +
                    "            <pattern>[%d{dd-MM-yyyy HH:mm:ss.SSS}] %.-1level/[%logger{0}] %msg%n</pattern>" +
                    "        </encoder>" +
                    "        <filter class='ch.qos.logback.classic.filter.LevelFilter'>" +
                    "            <level>DEBUG</level>" +
                    "            <onMatch>DENY</onMatch>" +
                    "        </filter>" +
                    "        <rollingPolicy class='ch.qos.logback.core.rolling.TimeBasedRollingPolicy'>" +
                    "            <fileNamePattern>${LOG_DIR}/log.%d{yyyy-MM}.txt</fileNamePattern>" +
                    "            <maxHistory>6</maxHistory>" +
                    "        </rollingPolicy>" +
                    "    </appender>" +
                    "    <root level='DEBUG'>" +
                    "        <appender-ref ref='LogCat' />" +
                    "        <appender-ref ref='LogFile' />" +
                    "    </root>" +
                    "</configuration>";

    static final String LOGBACK_XML_RELEASE =

            "<configuration debug='true'>" +
                    "    <property name='LOG_DIR' value='/storage/emulated/0/Android/data/it.company.product/files' />" +
                    "    <appender name='LogCat' class='ch.qos.logback.classic.android.LogcatAppender'>" +
                    "        <encoder>" +
                    "            <pattern>%msg%n</pattern>" +
                    "        </encoder>" +
                    "    </appender>" +
                    "    <appender name='LogFile' class='ch.qos.logback.core.rolling.RollingFileAppender'>" +
                    "        <file>${LOG_DIR}/log.txt</file>" +
                    "        <encoder>" +
                    "            <pattern>[%d{dd-MM-yyyy HH:mm:ss.SSS}] %.-1level/[%logger{0}] %msg%n</pattern>" +
                    "        </encoder>" +
                    "        <filter class='ch.qos.logback.classic.filter.LevelFilter'>" +
                    "            <level>DEBUG</level>" +
                    "            <onMatch>DENY</onMatch>" +
                    "        </filter>" +
                    "        <rollingPolicy class='ch.qos.logback.core.rolling.TimeBasedRollingPolicy'>" +
                    "            <fileNamePattern>${LOG_DIR}/log.%d{yyyy-MM}.txt</fileNamePattern>" +
                    "            <maxHistory>6</maxHistory>" +
                    "        </rollingPolicy>" +
                    "    </appender>" +
                    "    <root level='DEBUG'>" +
                    "        <appender-ref ref='LogCat' />" +
                    "        <appender-ref ref='LogFile' />" +
                    "    </root>" +
                    "</configuration>";

    private void configureLogBack() {

        // Usually, LogBack framework loads the logback.xml file from the asset folder
        // From Android 10 (API 29) and above, to improve privacy, direct access to shared/external storage device is deprecated (EXT_DIR variable)
        // Also access areas that are not owned by the app is denied
        // A workaround consist in postponing the loading of the LogBack configuration after creating a log folder within the app and load the configuration from a String
        // that mirrors the XML file content
        File mediaStorageDir = getApplicationContext().getExternalFilesDir(null);

        // Create the storage directory if it does not exist
        if (mediaStorageDir != null && !mediaStorageDir.exists()) {
            if (!mediaStorageDir.mkdirs()) {
                return;
            }
        }

        // Reset the default context (which may already have been initialized) since we want to reconfigure it
        LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
        lc.stop();

        JoranConfigurator config = new JoranConfigurator();
        config.setContext(lc);

        // Load the configuration from the String representing the XML logback configuration file
        InputStream stream;
        if(BuildConfig.DEBUG) {

            stream = new ByteArrayInputStream(LOGBACK_XML_DEBUG.getBytes());
        }
        else {

            stream = new ByteArrayInputStream(LOGBACK_XML_RELEASE.getBytes());
        }

        try {

                config.doConfigure(stream);
        }
        catch (JoranException e) {

            e.printStackTrace();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        mCompositeDisposable.dispose();
    }
}

提前致谢

android crash android-10.0 deadobjectexception
© www.soinside.com 2019 - 2024. All rights reserved.