On the open-source Android 10 Chinese client with experience in adapting technology Code

Personal blog navigation page (click on the right link to open a personal blog): Daniel take you on technology stack 

Our App adaptation from  targetSdkVersion = 26 upgrade cross-version to 29 , and therefore will encounter a large number of pits, the final version of the configuration is as follows:

Now fit into the fill hole guide contains the actual code that experience, never copy any document translation

1.Region.Op相关异常:java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed

When  targetSdkVersion> = Build.VERSION_CODES.P  call when canvas.clipPath (path,  Region.Op.XXX ); caused by abnormal, the reference source as follows:

@Deprecated
public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) {
     checkValidClipOp(op);
     return nClipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt);
}

private static void checkValidClipOp(@NonNull Region.Op op) {
     if (sCompatiblityVersion >= Build.VERSION_CODES.P
         && op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE) {
         throw new IllegalArgumentException(
                    "Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed");
     }
}

We can see that when a target version from Android P start, Canvas.clipPath ( @NonNull  Path path,  @NonNull  Region.Op OP); has been abandoned, and abandoned API is included abnormal risk, and only Region.Op.INTERSECT Region .Op.DIFFERENCE get compatible, google is currently unclear how the purpose of the move, just as simple as throwing an exception prompted developers to fit almost all blog solutions are simple and crude as follows:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    canvas.clipPath(path);
} else {
    canvas.clipPath(path, Region.Op.XOR);// REPLACE、UNION 等
}

But we certainly need some advanced logic operation effect how to do? Simulation results page as reading novels, the solution is as follows, with Path.op in place, the first operation Path, give canvas.clipPath:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
    Path mPathXOR = new Path();
    mPathXOR.moveTo(0,0);
    mPathXOR.lineTo(getWidth(),0);
    mPathXOR.lineTo(getWidth(),getHeight());
    mPathXOR.lineTo(0,getHeight());
    mPathXOR.close();
    //以上根据实际的Canvas或View的大小,画出相同大小的Path即可
    mPathXOR.op(mPath0, Path.Op.XOR);
    canvas.clipPath(mPathXOR);
}else {
    canvas.clipPath(mPath0, Region.Op.XOR);
}

2. plaintext HTTP restrictions

When  targetSdkVersion> = Build.VERSION_CODES.P  , the default limits the HTTP request, and the relevant log from:

java.net.UnknownServiceException: CLEARTEXT communication to xxx not permitted by network security policy

The first solution: Application code that add the following node in AndroidManifest.xml

<application android:usesCleartextTraffic="true">

The second solution: the new xml directory in the directory res, has built a new xml file network_security_config.xml skipped in xml directory, and then add the following Application in AndroidManifest.xml node Code

android:networkSecurityConfig="@xml/network_config"

Random name, as follows:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

3.Android Q property reader (10)

Android Q behavior related changes not elaborate, most of the online blog on Android Q adaptation are talking about behavior change, we will be based on practical problems encountered in the practical solution

1, the scanning system albums, video, pictures, video selector is provided by the ContentResolver, the main code is as follows:

private static final String[] IMAGE_PROJECTION = {
            MediaStore.Images.Media.DATA,
            MediaStore.Images.Media.DISPLAY_NAME,
            MediaStore.Images.Media._ID,
            MediaStore.Images.Media.BUCKET_ID,
            MediaStore.Images.Media.BUCKET_DISPLAY_NAME};

 Cursor imageCursor = mContext.getContentResolver().query(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    IMAGE_PROJECTION, null, null, IMAGE_PROJECTION[4] + " DESC");

String path = imageCursor.getString(imageCursor.getColumnIndexOrThrow(IMAGE_PROJECTION[0]));
String name = imageCursor.getString(imageCursor.getColumnIndexOrThrow(IMAGE_PROJECTION[1]));
int id = imageCursor.getInt(imageCursor.getColumnIndexOrThrow(IMAGE_PROJECTION[2]));
String folderPath = imageCursor.getString(imageCursor.getColumnIndexOrThrow(IMAGE_PROJECTION[3]));
String folderName = imageCursor.getString(imageCursor.getColumnIndexOrThrow(IMAGE_PROJECTION[4]));

//Android Q 公有目录只能通过Content Uri + id的方式访问,以前的File路径全部无效,如果是Video,记得换成MediaStore.Videos
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
      path  = MediaStore.Images.Media
                       .EXTERNAL_CONTENT_URI
                       .buildUpon()
                       .appendPath(String.valueOf(id)).build().toString();
 }

2, it is determined whether there is a public directory file, beginning from the Android Q, public directory File API fail, not directly by new File (path) .exists (); determining whether the public directory file exists, the right follows:

public static boolean isAndroidQFileExists(Context context, String path){
        if (context == null) {
            return false;
        }
        AssetFileDescriptor afd = null;
        ContentResolver cr = context.getContentResolver();
        try {
            Uri uri = Uri.parse(path);
            afd = cr.openAssetFileDescriptor(Uri.parse(path), "r");
            if (afd == null) {
                return false;
            } else {
                close(afd);
            }
        } catch (FileNotFoundException e) {
            return false;
        }finally {
            close(afd);
        }
        return true;
}

3, save or download the file to a public directory, Bitmap saved Similarly, such as Download, MIME_TYPE type may refer to its own corresponding file type, described here only to APK

public static void copyToDownloadAndroidQ(Context context, String sourcePath, String fileName, String saveDirName){
        ContentValues values = new ContentValues();
        values.put(MediaStore.Downloads.DISPLAY_NAME, fileName);
        values.put(MediaStore.Downloads.MIME_TYPE, "application/vnd.android.package-archive");
        values.put(MediaStore.Downloads.RELATIVE_PATH, "Download/" + saveDirName.replaceAll("/","") + "/");

        Uri external = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
        ContentResolver resolver = context.getContentResolver();

        Uri insertUri = resolver.insert(external, values);
        if(insertUri == null) {
            return;
        }

        String mFilePath = insertUri.toString();

        InputStream is = null;
        OutputStream os = null;
        try {
            os = resolver.openOutputStream(insertUri);
            if(os == null){
                return;
            }
            int read;
            File sourceFile = new File(sourcePath);
            if (sourceFile.exists()) { // 文件存在时
                is = new FileInputStream(sourceFile); // 读入原文件
                byte[] buffer = new byte[1444];
                while ((read = is.read(buffer)) != -1) {
                    os.write(buffer, 0, read);
                }
                is.close();
                os.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            close(is,os);
        }

}

4, save the image related

 /**
     * 通过MediaStore保存,兼容AndroidQ,保存成功自动添加到相册数据库,无需再发送广告告诉系统插入相册
     *
     * @param context      context
     * @param sourceFile   源文件
     * @param saveFileName 保存的文件名
     * @param saveDirName  picture子目录
     * @return 成功或者失败
     */
    public static boolean saveImageWithAndroidQ(Context context,
                                                  File sourceFile,
                                                  String saveFileName,
                                                  String saveDirName) {
        String extension = BitmapUtil.getExtension(sourceFile.getAbsolutePath());

        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DESCRIPTION, "This is an image");
        values.put(MediaStore.Images.Media.DISPLAY_NAME, saveFileName);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
        values.put(MediaStore.Images.Media.TITLE, "Image.png");
        values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/" + saveDirName);

        Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        ContentResolver resolver = context.getContentResolver();

        Uri insertUri = resolver.insert(external, values);
        BufferedInputStream inputStream = null;
        OutputStream os = null;
        boolean result = false;
        try {
            inputStream = new BufferedInputStream(new FileInputStream(sourceFile));
            if (insertUri != null) {
                os = resolver.openOutputStream(insertUri);
            }
            if (os != null) {
                byte[] buffer = new byte[1024 * 4];
                int len;
                while ((len = inputStream.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                }
                os.flush();
            }
            result = true;
        } catch (IOException e) {
            result = false;
        } finally {
            Util.close(os, inputStream);
        }
        return result;
}

4.EditText default does not get the focus, the keyboard does not pop up automatically

This problem occurs in  targetSdkVersion> = Build.VERSION_CODES.P  case, and the device version Android P above, we have now found no relevant change is determined from the source, the solution was added the following code onCreate:

mEditText.post(() -> {
       mEditText.requestFocus();
       mEditText.setFocusable(true);
       mEditText.setFocusableInTouchMode(true);
});

5.Only fullscreen activities can request orientation 异常

The problem occurs in  targetSdkVersion> = Build.VERSION_CODES.O_MR1  , that is  API 27 , when the device is Android 26 time (more than 27 has been repaired, google might feel inappropriate, and changed back), if the non-full-screen transparent activity fixed direction, the exception occurs, but when we tested in millet, Meizu Android 26 models, and not the exception, it is reported that Huawei models that exception, this is how FML. . . No way, remove or transparent style code can be removed in a fixed direction, no solution other

6. Install APK Intent and other documents related to Intent

/*
* 自Android N开始,是通过FileProvider共享相关文件,但是Android Q对公有目录 File API进行了限制
* 从代码上看,又变得和以前低版本一样了,只是必须加上权限代码Intent.FLAG_GRANT_READ_URI_PERMISSION
*/ 
private void installApk() {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
            //适配Android Q,注意mFilePath是通过ContentResolver得到的,上述有相关代码
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.parse(mFilePath) ,"application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            startActivity(intent);
            return ;
        }

        File file = new File(saveFileName + "osc.apk");
        if (!file.exists())
            return;
        Intent intent = new Intent(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
		    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "net.oschina.app.provider", file);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        startActivity(intent);
}

7.Activity transparent relevant, windowIsTranslucent property

Android10 another sinkhole, if you want to display a translucent Activity, which before android10 normal style Activity only need to set windowIsTranslucent = true can be, but to Android10, it does not have the effect, but if the dynamic set View.setVisibility () , the interface will be more than a blur ...

Solution: Use the Dialog Style Theme, and set windowIsFloating = true, this time the problem again, if the root Activity layout is not set fitsSystemWindow = true, the default layout will root padddingTop a status bar height. The interface looks normal, so if you have a dynamic adaptation of the code Activity status bar height, we need to set fitsSystemWindow = true root layout, otherwise they will find extra padddingTop a status bar height

8. clipboard compatible

Only when the application is interactive and monitor the situation in order to access the clipboard clipboard change, can not be accessed directly from the clipboard onResume callback, the benefits of doing so is to avoid some of the contents of the back monitor application response crazy clipboard.

We APK development practice pit temporarily encountered on these, of course Android Q changes is quite large, for example, as well as private sandbox App file, locate and background pop-up permissions Activity restrictions, these must be appropriate to step on pit according to their own practice equipped, qualified as possible to read the official documentation, reference improvements.

Attached Java / C / C ++ / machine learning / Algorithms and Data Structures / front-end / Android / Python / programmer reading / single books books Daquan:

(Click on the right to open there in the dry personal blog): Technical dry Flowering
===== >> ① [Java Daniel take you on the road to advanced] << ====
===== >> ② [+ acm algorithm data structure Daniel take you on the road to advanced] << ===
===== >> ③ [database Daniel take you on the road to advanced] << == ===
===== >> ④ [Daniel Web front-end to take you on the road to advanced] << ====
===== >> ⑤ [machine learning python and Daniel take you entry to the Advanced Road] << ====
===== >> ⑥ [architect Daniel take you on the road to advanced] << =====
===== >> ⑦ [C ++ Daniel advanced to take you on the road] << ====
===== >> ⑧ [ios Daniel take you on the road to advanced] << ====
=====> > ⑨ [Web security Daniel take you on the road to advanced] << =====
===== >> ⑩ [Linux operating system and Daniel take you on the road to advanced] << = ====

There is no unearned fruits, hope you young friends, friends want to learn techniques, overcoming all obstacles in the way of the road determined to tie into technology, understand the book, and then knock on the code, understand the principle, and go practice, will It will bring you life, your job, your future a dream.

Published 47 original articles · won praise 0 · Views 282

Guess you like

Origin blog.csdn.net/weixin_41663412/article/details/104863691