Android 6
Runtime permission dynamic request
Android 7
On Android 7 On the 0 system, it is forbidden to disclose the file:// URI to your application. If an Intent containing the file:// URI type leaves your application, the application fails and a FileUriExposedException exception occurs, such as calling the system camera to take pictures. To share files between applications, you can send a URI of type content:// URI, grant temporary access to the URI, and use the FileProvider class.
The general steps of using FileProvider are as follows:
1. Create an xml directory under res and create a file in this directory_ paths. xml file, as follows:
<?xml version="1.0" encoding="utf-8"?> <paths> <!-- Internal storage, equivalent to Context.getFilesDir,route:/data/data/Package name/files catalogue--> <files-path name="DocDir" path="/" /> <!-- Internal storage, equivalent to Context.getCacheDir,route:/data/data/Package name/cache catalogue--> <cache-path name="CacheDocDir" path="/" /> <!--External storage, equivalent to Context.getExternalFilesDir,route:/storage/sdcard/Android/data/Package name/files--> <external-files-path name="ExtDocDir" path="/" /> <!--External storage, equivalent to Context.getExternalCacheDir route:/storage/sdcard/Android/data/Package name/cache--> <external-cache-path name="ExtCacheDir" path="/" /> </paths>
2. Register the provider in the manifest
<provider android:name="androidx.core.content.FileProvider" android:authorities="com.unclexing.exploreapp.fileprovider" android:exported="false" android:grantUriPermissions="true"> <!--exported:To be false,by true A security exception will be reported. grantUriPermissions by true,Indicates grant URI Temporary access rights--> <meta-data android:name="androidx.core.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
3. Use FileProvider
val file = File( getExternalFilesDir(null), "/temp/" + System.currentTimeMillis() + ".jpg" ) if (!file.parentFile.exists()) { file.parentFile.mkdirs() } //Create a Uri of content type through FileProvider val imageUri = FileProvider.getUriForFile( this, "com.unclexing.exploreapp.fileprovider", file ) val intent = Intent() //Indicates that the file represented by the Uri is temporarily authorized for the target application intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) intent.action = MediaStore.ACTION_IMAGE_CAPTURE //Save captured photos to a specific URI intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri) startActivity(intent)
Android 8
Android 8.0 introduces notification channels, which allow users to create customized channels for each notification type to be displayed. The user interface calls notification channels notification categories.
private fun createNotification() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager //If you want to group, groupId should be unique val groupId = "group1" val group = NotificationChannelGroup(groupId, "advertisement") notificationManager.createNotificationChannelGroup(group) //channelId unique val channelId = "channel1" val notificationChannel = NotificationChannel( channelId, "promote information", NotificationManager.IMPORTANCE_DEFAULT ) //To add a channel to a group, you must create a group before you can add it notificationChannel.group = groupId notificationManager.createNotificationChannel(notificationChannel) //Create notification val notification = Notification.Builder(this, channelId) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)) .setContentTitle("A new notice") .setContentText("Likes and follows") .setAutoCancel(true) .build() notificationManager.notify(1, notification) } }
After Android 8.0, the background application is not allowed to start the background Service. It needs to be specified as the foreground Service through startforegroup Service(). The application has five seconds to call the startforegroup() method of the Service to display the visible notification. If the application does not call startforegroup() within this time limit, the system will stop the Service and declare the application as ANR.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val intent = Intent(this, UncleService::class.java) startForegroundService(intent) }
class UncleService : Service() { override fun onCreate() { super.onCreate() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager val channel = NotificationChannel("channelId", "channelName", NotificationManager.IMPORTANCE_HIGH) manager.createNotificationChannel(channel) val notification = Notification.Builder(this, "channelId") .build() startForeground(1, notification) } } override fun onDestroy() { super.onDestroy() stopForeground(true) } override fun onBind(p0: Intent?): IBinder? { return null } }
Don't forget to add permissions to the manifest
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
Android 9
In the network request in Android 9, http request is not allowed and https is required.
Solution:
Create a new xml directory under res, and then create a directory named: network_config.xml file
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true" /> </network-security-config>
Then configure this property under the mainfiests application tab
android:networkSecurityConfig="@xml/network_config"
This is a simple and crude method. For security and flexibility, we can specify http domain names and use http for some domain names
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">csdn.example.com</domain> </domain-config> </network-security-config>
Android 10
Location permissions
The user can better control when the application can access the device location. When the application running on Android 10 requests location access, it will give the user an authorization prompt in the form of a dialog box. At this time, there are two kinds of location access permissions: in use (foreground only) or always (foreground and background)
Add permission ACCESS_BACKGROUND_LOCATION
If your app is for Android 10 and needs to access the user's location when running in the background, you must declare the new permission in the app's manifest file
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Partitioned storage
On versions before Android 10, we applied for read-write permission of storage space when we operated files. However, these permissions are completely abused. The problem is that the storage space of the mobile phone is full of a large number of unknown files, and it is not deleted after the application is uninstalled. In order to solve this problem, the concept of partitioned storage is introduced in Android 10 to achieve better file management by adding external storage access restrictions. But the application is not complete, because we can use Android manifest Add android:requestLegacyExternalStorage="true" to the XML to request the use of the old storage mode for simple and rough adaptation. But I don't recommend this, because Android 11 enforces the partition storage mechanism, and this configuration will no longer work, so you have to do the adaptation honestly. Just look at the adaptation of Android 11 below.
Android 11
There are two ways to access without storage permission: the App's own internal storage and the App's own external storage.
The difference of storage scope access is how to access files in other directories.
Enforce partitioned storage
The shared storage space stores pictures, videos, audio and other files. These resources are public and can be accessed by all apps. There are pictures, videos, audio and downloaded files stored in the shared storage space. How to distinguish these types when App obtains or inserts files? MediaStore is needed at this time. For example, if you want to query the picture files in the shared storage space:
val cursor = contentResolver.query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null )
MediaStore.Images.Media.EXTERNAL_CONTENT_URI means to specify that the type of query file is a picture and construct a URI object. URI implements Parcelable and can be passed between processes.
Since the file cannot be accessed directly through the path, how to access the file through URI? Uri can be obtained through MediaStore or SAF. However, it should be noted that although the URI can also be constructed directly through the file path, the URI constructed in this way does not have permission to access the file.
Now let's read a text file, newtextfile, in the / sdcard / directory Txt, because it does not belong to the file of shared storage space, but belongs to other directories, it cannot be obtained through MediaStore, but only through SAF.
private fun openSAF() { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) intent.addCategory(Intent.CATEGORY_OPENABLE) //Specifies the file to select the text type intent.type = "text/plain" startActivityForResult(intent, 1) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == 1 && data != null) { val uri = data.data startRead(uri!!) } } private fun startRead(uri: Uri) { try { val inputStream = contentResolver.openInputStream(uri) val readContent = ByteArray(1024) var len: Int do { len = inputStream!!.read(readContent) if (len != -1) { Log.d(tag, "file content:${String(readContent).substring(0, len)}") } } while (len != -1) } catch (e: Exception) { Log.d(tag, "Exception:${e.message}") } }
It can be seen that files belonging to "other directories" need to be accessed through SAF, SAF returns Uri, and InputStream can be constructed through Uri to read the files.
Next, let's write the content to the file. We still need to get the Uri through SAF, and then construct the output stream.
private fun writeForUri(uri: Uri) { try { val outputStream = contentResolver.openOutputStream(uri) val content = "my name is Uncle Xing" outputStream?.write(content.toByteArray()) outputStream?.flush() outputStream?.close() } catch (e: Exception) { Log.d(tag, "Exception:${e.message}") } }
The advantage of SAF is that the system provides a file selector. The caller only needs to specify the file types to be read and written, such as text type, picture type, video type, etc. the selector will filter out the corresponding files for selection, which is simple to use.
Single authorization
Starting from Android 11, whenever an application requests permission related to location information, microphone or camera, the user oriented permission dialog box will contain the option only this time. If the user selects this option in the dialog box, the system will grant temporary single authorization to the application.
The three options in the permission dialog box are: reject, allow this run, and allow only in use.
Location permissions
Android 10 request ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION indicates that you have the permission to access the device location information in the foreground. You can also see always allow in the request pop-up box. In Android 11, the always allow option is cancelled, and the background access to device location information is not granted by default. Android 11 pulls out the device location information obtained in the background through ACCESS_BACKGROUND_LOCATION permission is the permission to access the device location information in the background. One thing to note is to request access_ BACKGROUND_ You can't request other permissions while location, otherwise the system will throw an exception. The official suggestion is to first request the access permission of foreground location information, and then request the access permission of background location information.