Today I saw a good article to share with you, worship the boss.
Android 10 pit filling adaptation guide, including actual experience code, never copy translated documents
Related exception:: Invalid - only INTERSECT and DIFFERENCE are allowed
The exception caused by calling (path, ); when targetSdkVersion >= Build.VERSION_CODES.P, the reference source code is as follows:
@Deprecated public boolean clipPath(@NonNull Path path, @NonNull op) { checkValidClipOp(op); return nClipPath(mNativeCanvasWrapper, (), ); } private static void checkValidClipOp(@NonNull op) { if (sCompatiblityVersion >= Build.VERSION_CODES.P && op != && op != ) { throw new IllegalArgumentException( "Invalid - only INTERSECT and DIFFERENCE are allowed"); } }
We can see that when the target version starts from Android P, (@NonNull Path path, @NonNull op); has been abandoned and is an abandoned API with exception risks. Only and compatible, almost all blog solutions are as follows:
if (.SDK_INT >= Build.VERSION_CODES.P) { (path); } else { (path, );// REPLACE, UNION, etc.}
But what should we do if we need some advanced logical operation effects? For example, the simulation page-turning reading effect of a novel, the solution is as follows: Use instead, first calculate the Path, and then
Give: if(.SDK_INT >= Build.VERSION_CODES.P){ Path mPathXOR = new Path(); (0,0); (getWidth(),0); (getWidth(),getHeight()); (0,getHeight()); (); //The above is to draw the same size of the Path according to the actual Canvas or View size (mPath0, ); (mPathXOR); }else { (mPath0, ); }
2. Plain text HTTP restrictions
When targetSdkVersion >= Build.VERSION_CODES.P, HTTP requests are restricted by default, and related logs appear:
: CLEARTEXT communication to xxx not permitted by network security policy
The first solution: add the following node code in Application
<application android:usesCleartextTraffic="true">
The second solution: Create a new xml directory in the res directory, skip the built ones. Create a new xml file network_security_config.xml in the xml directory, and then add the following node code in Application
android:networkSecurityConfig="@xml/network_config"
The names are random, and the content is as follows:
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true" /> </network-security-config>
Read and write media resources in Q
1. Scan the system album, video, etc. The pictures and video selectors are provided through ContentResolver. The main code is as follows:
private static final String[] IMAGE_PROJECTION = { , .DISPLAY_NAME, ._ID, .BUCKET_ID, .BUCKET_DISPLAY_NAME}; Cursor imageCursor = ().query( .EXTERNAL_CONTENT_URI, IMAGE_PROJECTION, null, null, IMAGE_PROJECTION[0] + " DESC"); String path = ((IMAGE_PROJECTION[0])); String name = ((IMAGE_PROJECTION[1])); int id = ((IMAGE_PROJECTION[2])); String folderPath = ((IMAGE_PROJECTION[3])); String folderName = ((IMAGE_PROJECTION[4])); //Android Q public directory can only be accessed through Content Uri + id. All previous File paths are invalid. If it is Video, remember to change it toif(.SDK_INT >= Build.VERSION_CODES.Q){ path = .EXTERNAL_CONTENT_URI .buildUpon() .appendPath((id)).build().toString(); }
2. Determine whether the public directory file exists. Starting from Android Q, the public directory File API has been invalid. You cannot directly judge whether the public directory file exists through new File(path).exists();. The correct way is as follows:
public static boolean isAndroidQFileExists(Context context, String path){ AssetFileDescriptor afd = null; ContentResolver cr = (); try { Uri uri = (path); afd = (uri, "r"); if (afd == null) { return false; } else { close(afd); } } catch (FileNotFoundException e) { return false; }finally { close(afd); } return true; }
3. Copy or download the file to the public directory and save Bitmap. For example, Download, the MIME_TYPE type can refer to the corresponding file type by yourself. Here, only the APK is explained. From the private directory copy to the public directory demo as follows (the same is true for remote download. Just get OutputStream, you can also download to the private directory and then copy to the public directory):
public static void copyToDownloadAndroidQ(Context context, String sourcePath, String fileName, String saveDirName){ ContentValues values = new ContentValues(); (.DISPLAY_NAME, fileName); (.MIME_TYPE, "application/-archive"); (.RELATIVE_PATH, "Download/" + ("/","") + "/"); Uri external = .EXTERNAL_CONTENT_URI; ContentResolver resolver = (); Uri insertUri = (external, values); if(insertUri == null) { return; } String mFilePath = (); InputStream is = null; OutputStream os = null; try { os = (insertUri); if(os == null){ return; } int read; File sourceFile = new File(sourcePath); if (()) { // When the file exists is = new FileInputStream(sourceFile); // Read in the original file byte[] buffer = new byte[1444]; while ((read = (buffer)) != -1) { (buffer, 0, read); } } } catch (Exception e) { (); }finally { close(is,os); } }
4. Save pictures related
/** * Save through MediaStore, compatible with AndroidQ, and automatically added to the album database after successful saving, no need to send a broadcast to tell the system to insert the album * * @param context context * @param sourceFile source file * @param saveFileName The saved file name * @param saveDirName picture subdirectory * @return Success or failure */ public static boolean saveImageWithAndroidQ(Context context, File sourceFile, String saveFileName, String saveDirName) { String extension = (()); ContentValues values = new ContentValues(); (, "This is an image"); (.DISPLAY_NAME, saveFileName); (.MIME_TYPE, "image/png"); (, ""); (.RELATIVE_PATH, "Pictures/" + saveDirName); Uri external = .EXTERNAL_CONTENT_URI; ContentResolver resolver = (); Uri insertUri = (external, values); BufferedInputStream inputStream = null; OutputStream os = null; boolean result = false; try { inputStream = new BufferedInputStream(new FileInputStream(sourceFile)); if (insertUri != null) { os = (insertUri); } if (os != null) { byte[] buffer = new byte[1024 * 4]; int len; while ((len = (buffer)) != -1) { (buffer, 0, len); } (); } result = true; } catch (IOException e) { result = false; } finally { close(os, inputStream); } return result; }
The keypad does not get the focus by default and does not automatically pop up
This problem occurs in the case of targetSdkVersion >= Build.VERSION_CODES.P, and the device version is Android P or above. The solution is to add the following code to onCreate to get the focus. If you need to pop up the keyboard, you can delay it:
(() -> { (); (true); (true); });
5. Install APK Intent and other shared files related Intents
/* * Since Android N, related files are shared through FileProvider, but Android Q has restricted the public directory File API and can only be operated through Uri. * From the code point of view, it has become the same as before, but the permission code Intent.FLAG_GRANT_READ_URI_PERMISSION must be added */ private void installApk() { if(.SDK_INT >= Build.VERSION_CODES.Q){ //Adapt to Android Q, note that mFilePath is obtained through ContentResolver, and the above related code is available. Intent intent = new Intent(Intent.ACTION_VIEW); ((mFilePath) ,"application/-archive"); (Intent.FLAG_ACTIVITY_NEW_TASK); (Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(intent); return ; } File file = new File(saveFileName + ""); if (!()) return; Intent intent = new Intent(Intent.ACTION_VIEW); if (.SDK_INT >= Build.VERSION_CODES.N) { (Intent.FLAG_ACTIVITY_NEW_TASK); (Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = (getApplicationContext(), "", file); (contentUri, "application/-archive"); } else { ((file), "application/-archive"); (Intent.FLAG_ACTIVITY_NEW_TASK); } startActivity(intent); }
Transparent related, windowIsTranslucent property
Android Q Another sinkhole. If you want to display a translucent Activity, before Android 10, ordinary style Activity only needs to set windowIsTranslucent=true, but when Android Q comes, it has no effect, and if you set() dynamically, there will be a afterimage on the interface...
Solution: Use Dialog style Activity and set windowIsFloating=true. The problem comes again. If the Activity root layout does not set fitsSystemWindow=true, the status bar will not be invaded by default, making the interface look normal.
7.Chip board compatible
In Android Q, you can access the clipboard and listen for changes in the clipboard only when the application is in an interactive situation (the default input method is interactive itself). You cannot directly access the clipboard in the onResume callback. The advantage of this is that some applications are crazy about monitoring the content of the clipboard and pop-up windows in response to the clipboard in the background.
Therefore, if you still need to monitor the clipboard, you can use the application lifecycle callback, listen to the APP background return, delay accessing the clipboard by several milliseconds, and then save the clipboard content obtained by the last access, and compare whether there is any change each time, and then proceed to the next operation.
8. Third-party images sharing and other operations, directly using file paths, such as QQ image sharing, you need to pay attention to it. This is not feasible. You can only use the MediaStore and other APIs to get Uri to operate.
These are listed based on the actual problems we encountered when SDK upgraded to 29. They are not behavior changes in translation Android Q. Please solve specific problems based on your own actual situation.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.