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.
- IMPORTANCE_HIGH can be displayed anywhere, with sound
- IMPORTANCE_DEFAULT can be displayed anywhere, with sound but without visual interference.
- 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
- 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>
- 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>
- 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); }
- New permission requirements for Android O;
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
Matters needing attention
- 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 {}
- 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);
- Android: export = "false": FileProvider defaults to private non-exportable;
- android:grantUriPermissions="true": Allows temporary access to files;
- android:resource="@xml/file_path": Set the file path accessed by FileProvider, which can be freely defined;
- 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