Android 8.0 simply adapts to those things

Posted by error_22 on Mon, 12 Aug 2019 13:32:40 +0200

Android 8.0 has been released for a long time and has not been adapted and upgraded based on user equipment and market requirements. Now there are some problems in adapting, so organize and record them. Official website The introduction of Android 8.0's new features and adaptation-related is very clear, and the side dishes are gradually upgraded according to the official requirements.

Upgrade SDK

The first thing to deal with is targetSDK = 26 compileSDK >= 26. After synchronization, attention should be paid to whether the three-party SDK in the project needs to be upgraded and adapted. If you need to upgrade, please carefully refer to the three-party SDK documents for upgrade. After that, it is suggested that debug and release be tested separately for normal packaging. It may be as simple as changing a version number, or it may involve many complex upgrades of tripartite packages, which need to be taken seriously.

targetSdkVersion = 26

Push Notice

After upgrading SDK, the dish tested push messages, some of which could not be displayed properly, because Android 8.0 added notification channels, allowing different types of notification channels to be established. We can create different types of notification channels, and users can selectively turn off uninterested or low-priority push. The advantage of sending messages is that they will not be cancelled as a whole because there are too many messages being pushed.

Creating notification channels requires three parameters: ChannelID / ChannelName / importance. ChannelID is unique. ChannelName will be shown in the system setup instructions, and importance is the importance of notification.

  1. IMPORTANCE_HIGH can be displayed anywhere, with sound
  2. IMPORTANCE_DEFAULT can be displayed anywhere, with sound but without visual interference.
  3. IMPORTANCE_MIN is silent and appears only in the status bar. It cannot be used with start Foreground.
// Android 8.0 puts notifications into specific channels
NotificationManager notificationManager =
    (NotificationManager) context.getSystemService(android.content.Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    NotificationChannel channel = new NotificationChannel(PUSH_CHANNEL_ID, PUSH_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
    if (notificationManager != null) {
        notificationManager.createNotificationChannel(channel);
    }
}
// Need to pass in ChannelID
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, PUSH_CHANNEL_ID);
...
notificationBuilder.build();

The promotion of small dishes is relatively simple, and the official website provides a lot of rich styles and details, which can be consulted by the official website.

Apk Download and Installation

Since Android N, Android has increased restrictions on file management; after Android 7.0, Uri.fromFile has been abandoned and FileProvider has been used to process local file paths.

Uri.fromFile file path:
file:/storage/emulated/0/Android/data/package name/files/downloaded_app.apk

FileProvider file path:
content:/storage/emulated/0/Android/data/package name/files/downloaded_app.apk
  1. Declare Provider in Android Manifest. xml;
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
  <meta-data
      android:name="android.support.FILE_PROVIDER_PATHS"
      android:resource="@xml/file_path" />
</provider>
  1. Create a new xml folder under res, and then create a file_path file corresponding to Provider.
<?xml version="1.0" encoding="utf-8"?>
<paths>
  <external-files-path
      name="external_files_path"
      path="Download" />
</paths>
  1. Use implicit intent to install Apk;
private void installApk(File apk) {
    Uri uri = null;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
        uri = Uri.fromFile(apk);
    } else {
        uri = FileProvider.getUriForFile(mContext, mContext.getPackageName() +  ".fileprovider", apk);
    }
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(uri, "application/vnd.android.package-archive");
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    mContext.startActivity(intent);
}
  1. New permission requirements for Android O;
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

Matters needing attention

  1. android:name defaults to android.support.v4.content.FileProvider, but sometimes needs to integrate three-party SDK has been occupied, when using the new Provider can be built to prevent conflicts;
public class MyFileProvider extends FileProvider {}
  1. android:authorities are usually Uri domain names with uniqueness and the same path as when installing Apk.
android:authorities="${applicationId}.fileprovider"

FileProvider.getUriForFile(mContext, mContext.getPackageName() +  ".fileprovider", apk);
  1. Android: export = "false": FileProvider defaults to private non-exportable;
  2. android:grantUriPermissions="true": Allows temporary access to files;
  3. android:resource="@xml/file_path": Set the file path accessed by FileProvider, which can be freely defined;
  4. file_path.xml is a file path that can be set freely. Android provides the following by default.
[files-path] -> "/data/data/Package name/files"
[external-path] -> "/storage/emulated/0"
[cache-path] -> "/data/data/Package name/cache"
[external-files-path] -> "/storage/emulated/0/Android/data/Package name/files"
[external-cache-path] -> "/storage/emulated/0/Android/data/Package name/cache"

Backstage broadcasting restrictions

After Android 8.0, the system imposed more restrictions on background operations, and App applications could not register most implicit broadcasts in Android Manifest; especially "android.net.conn.CONNECTIVITY_CHANGE", but for "android.net.conn.CONNECTIVITY_CHANGE". The following broadcasts Exceptions;

// Boot-up broadcasting
Intent.ACTION_LOCKED_BOOT_COMPLETED
Intent.ACTION_BOOT_COMPLETED

// User additions and deletions
Intent.ACTION_USER_INITIALIZE

// Time zone broadcasting
Intent.ACTION_TIMEZONE_CHANGED

// Language area
Intent.ACTION_LOCALE_CHANGED

// USB
UsbManager.ACTION_USB_ACCESSORY_ATTACHED
UsbManager.ACTION_USB_ACCESSORY_DETACHED
UsbManager.ACTION_USB_DEVICE_ATTACHED
UsbManager.ACTION_USB_DEVICE_DETACHED

// Bluetooth
BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED
BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED
BluetoothDevice.ACTION_ACL_CONNECTED
BluetoothDevice.ACTION_ACL_DISCONNECTED

// Telephone
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
TelephonyIntents.ACTION_*_SUBSCRIPTION_CHANGED
TelephonyIntents.SECRET_CODE_ACTION
TelephonyManager.ACTION_PHONE_STATE_CHANGED
TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED
TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED

// Login account
AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION

// data dump
Intent.ACTION_PACKAGE_DATA_CLEARED

The adaptation method is to dynamically register the unusable implicit broadcasting; note: to register and destroy in the same context;

TestReceiver testReceiver;

public void initTestReceiver() {
    testReceiver = new TestReceiver();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("android.intent.action.NEW_OUTGOING_CALL");
    intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
    context.registerReceiver(testReceiver, intentFilter);
}

public void destroyTestReceiver() {
    if (testReceiver != null) {
        context.unregisterReceiver(testReceiver);
    }
}

Backstage service restrictions

Android 8.0 also enhances the restrictions on services, and can't start back-end services directly with startService.

Programme 1:

startForegroundService() is used according to the version, but the application must call the startForeground() of the service within five seconds after the service is created; if not, the log will prompt that the startForeground is not invoked, and even the ANR application will crash.

// Start Service
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(new Intent(MainActivity.this, TestService.class));
} else {
    startService(new Intent(MainActivity.this, TestService.class));
}

public class TestService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        initNotification();
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        initNotification();
        return super.onStartCommand(intent, flags, startId);
    }

    private void initNotification() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationManager notificationManager =
                (NotificationManager) getSystemService(android.content.Context.NOTIFICATION_SERVICE);
            NotificationChannel channel =
                new NotificationChannel("push", "push_name", NotificationManager.IMPORTANCE_HIGH);
            notificationManager.createNotificationChannel(channel);
            Notification notification =
                new NotificationCompat.Builder(this, "push").setContentTitle("ACE_DEMO").setContentText("Front Office Service").build();
            startForeground(1, notification);
        }
    }
}

For some Android O devices, if the Channel or Notification content is not created, the system will prompt XX to notify XX of the power consumption in the background by default when it is cut to the background; if Notification is normal, it will also prompt the background to proceed, but only the content. In order to fill in the content, we tried many ways to cancel the notice, so the official website recommended the second scheme to start the background service.

Programme II:

Officials offer another solution. JobScheduler JobService is introduced after Android 5.0. In order to adapt to compatibility, the low version still uses JobService in the high version of ordinary Service.

JobService processes business logic through onStartJob and terminates job through onStopJob; the call is initiated with the help of JobInfo.Builder constructor;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class TestJobService extends JobService {
    @Override
    public boolean onStartJob(JobParameters params) {
        // do something
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}

public static void startTestService(Context context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        JobScheduler scheduler = context.getSystemService(JobScheduler.class);
        JobInfo.Builder builder = new JobInfo.Builder(Constants.JOB_ALARM_SERVICE_ID, new ComponentName(context, TestJobService.class));
        builder.setOverrideDeadline(5000);
        scheduler.schedule(builder.build());
    } else {
        context.startService(new Intent(context, TestJobService.class));
    }
}

Programme III:

In order to use JobService and Service conveniently, the side dish has tried three-way. android-job No need to distinguish between versions, minimum support to API 14, basically meet the daily version;

Job handles business logic through onRunJob and calls it through JobRequest.Builder constructor, and Job provides many ways including immediate/delayed/circular startup. For detailed methods, please refer to the official website.

public class TestJobCreator implements JobCreator {
    @Override
    @Nullable
    public Job create(@NonNull String tag) {
        switch (tag) {
            case TestSyncJob.TAG:
                return new TestSyncJob();
            default:
                return null;
        }
    }
}

public class TestSyncJob extends Job {
    public static final String TAG = "job_test_tag";

    @Override
    @NonNull
    protected Result onRunJob(Params params) {
        // run your job here
        return Result.SUCCESS;
    }

    public static void scheduleJob() {
        new JobRequest.Builder(TestSyncJob.TAG)
                .setExecutionWindow(30_000L, 40_000L)
                .build()
                .schedule();
    }
}

JobManager.create(this).addJobCreator(new TestJobCreator());

Android 8.0 adaptation also includes Bluetooth/background positioning and other restrictions, the side dish is not applied in practice, please refer to the official documents for details; side dish only records the difficulties encountered in the actual adaptation; if there are errors, please give more guidance!

Source: Ace Junior Monk

Topics: Android FileProvider xml SDK