Android Partition Storage

1. Android Storage

Android storage is divided into internal storage and external storage (external storage does not refer to SD memory card or external hard disk).

①Internal storage

The storage area used for the Android system itself and applications, such as /system/, /data/ and other directories of the mobile phone. Without this storage area, the Android system and applications cannot be run.

Among them, data/data/package name/XXX is the internal storage space provided by the Android system to store data for the app. SharedPreferences, Sqlite databases, cache files, etc. created by the app are all stored in this folder. This directory can only be accessed by the app itself, and other applications and users cannot access files stored in this space, and this directory will be removed when the app is uninstalled.

The method of obtaining the absolute path/data of the internal storage of the mobile phone:

Environment.getDataDirectory().getAbsolutePath();

Note: If the mobile phone has obtained the root authority, it can access the file data in the internal storage space.

②External storage

The internal storage space of the mobile phone is usually not very large. Once the internal storage capacity of the mobile phone is exhausted, the mobile phone may become unusable. Therefore, some relatively large media files need to be placed in the external storage of the fuselage. The previous mobile phone has an SD memory card slot, insert the SD memory card, and the system will mount the storage space of the SD memory card as an external storage space. But the current mobile phone does not support the expansion function of SD memory card. Instead, the mobile phone comes with a built-in storage space in the body. The function of the body storage and the SD memory card is exactly the same. Part of the area is divided into internal storage, and the other part is divided into external storage, and then these storage spaces are mounted by means of Linux file mounting.

The method of obtaining the path /storage/emulated/0 of the external storage of the mobile phone:
Environment.getExternalStorageDirectory().getAbsolutePath();

③Many mobile phones now have a large built-in storage space. Therefore, mobile phones with Android 4.4 system and above divide the built-in storage into two different storage areas: internal storage and external storage, which are used to store different data.

If the Android4.4 system and above mobile phones also support a separate external SD card, then the external SD card must be external storage, and there are two external storage spaces on the mobile phone at this time. In order to distinguish these two external storages, Google provides the getExternalFilesDirs method. This API can obtain multiple external storage spaces, and it returns a File array. The File array contains the external storage of the phone itself and the external SD card. external storage.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

    File[] files = getExternalFilesDirs( Environment.MEDIA_MOUNTED);

    for(File file:files){

        Log.e("Environment", file.getAbsolutePath())

    }

}

Note: Android phones support multiple external storage media spaces. If the user removes the media on the device, the external storage may become unavailable. So before performing any work with external storage, it is best to call getExternalStorageState() to check if the medium is available.

//Judge whether external storage is available
if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {     ... }

b17fe52e74ea47bdb2fcdcd7f7ef1c07.png

It can be seen that the internal storage and the external storage can be on the same storage medium, but they are only conceptually distinguished, that is, the internal storage and the external storage are different areas on the same storage medium.

 

2. Android application storage directory

①Application private directory in internal storage space

For each installed App, the system will automatically create a corresponding folder under the data/data directory of the internal storage space with the name of the application package. This folder contains SharedPreferences and SQLiteDatabase persistent application-related data, etc. This folder is the private folder of the application, and other applications and users cannot access the contents of the folder (except Root users). Each application does not require permission to access its own internal storage. These files are also removed when the user uninstalls the app.

1) Obtain the path of the files folder under the current app package name folder:

context.getFilesDir().getAbsolutePath();

Return result: data/data/packagename/files

2) Obtain the cache folder path under the current app package name folder:

context.getCacheDir().getAbsolutePath();

Return result: data/data/packagename/cache

3) Create or open an existing directory in internal storage:

context.getDir("setting", MODE_PRIVATE).getAbsolutePath();

Return result: data/data/packagename/setting

4) Obtain all file names under the internal storage files path:

context.fileList();

5) Delete the files under the internal storage files path:

//Return true to indicate successful deletion

context.deleteFile(filename);

6) To create a new file in the files or cache directory, you can use the File() constructor and pass it to the method of specifying the internal storage directory provided by the above method of File:

//Save the content to the private files directory

public void save(String filename, String content) throws IOException{

    File file= new File(context.getFilesDir(), filename);

    FileOutputStream myfos= new FileOutputStream(file);

    myfos.write(content.getBytes());

    myfos.close();

}

or read the file:

//Get the files in the private files directory by file name

public String get(String filename) throws IOException {

    File file= new File(context.getFilesDir(), filename);

    FileInputStream fis = new FileInputStream(file);

    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    byte[] data = new byte[1024];

    int len = -1;

    while ((len = fis.read(data)) != -1) {

        baos.write(data, 0, len);

    }

    return new String(baos.toByteArray());

}

The Android system can call the openFileOutput() method provided in the Context class to obtain the FileOutputStream to operate on the file. The openFileOutput() method accepts two parameters: the first parameter is the file name, which is used when the text is created. Note that the file name specified here cannot contain a path (because it is stored in /data/data//files by default) /Under contents). The second parameter is the operation mode of the file. There are two main types: MODE_PRIVATE: the default operation mode, which means that when the same file name is specified, when the file name has content, calling it again will overwrite the original content; MODE_APPEND: means the Appends content to the file if it already exists.

After calling openFileOutput() to obtain the FileOutputStream object, you can use the Java stream to write data into the file.

write to file:

//Save the content to the private files directory

public void save(String filename, String content) throws IOException{

    FileoutputStream myfos=context.openFileoutput(filename,Context.MODE_PRIVATE);

    myfos.write(content.getBytes());

    myfos.close();

}

read file:

//Get the files in the private files directory by file name

public String get(String filename) throws IOException {

    FileInputStream fis = context.openFileInput( filename);

    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    byte[] data = new byte[1024];

    int len = -1;

    while ((len = fis.read(data)) != -1) {

        baos.write(data, 0, len);

    }

    return new String(baos.toByteArray());

}

If you want to save files in the data/data/packagename/cache directory, you can also use File to save them. It should be noted that this directory is suitable for temporary files and should be cleaned up regularly. The system may delete files in the cache if disk space is low, so be sure to check for the existence of cached files before reading.

②Application private directory in external storage space

The external storage can be an external SD card, or a partial partition of the built-in memory card. External storage can be divided into public directories and private directories. The READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permissions are not required to operate the private directories of external storage.

The Android SDK provides APIs for developers to directly operate the application private directory under the external storage space:

1) Obtain the files path of an application in external storage

context.getExternalFilesDir(type).getAbsolutePath();

Return result: /storage/emulated/0/Android/data/packagename/files

2) Obtain the cache path of an application in external storage

context.getExternalCacheDir().getAbsolutePath()

Return result: /storage/emulated/0/Android/data/packagename/cache

This directory and its contents are deleted when the user uninstalls the app. Also, files in these directories are not read by the system media scanner, so they cannot be accessed from the MediaStore content provider. Therefore, if your media files do not need to be used by other applications, they should be stored in a private storage directory on the external storage.

A private directory for external storage can be obtained by calling getExternalFilesDir() and passing it a name.

public void save(String filename, String content) throws IOException{

    boolean canUse = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);

    if(canUse){

      File file= new File( context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "c.txt");

      FileOutputStream myfos= new FileOutputStream(file);

      myfos.write(content.getBytes());

      myfos.close();

    }

}

If the subfile directory name is not defined in advance, you can call getExternalFilesDir() and pass null, which will return the root directory of the application's private directory on the external storage. type can pass the subdirectory constants predefined by the system according to the file type, such as picture Environment.DIRECTORY_PICTURES, then return /storage/emulated/0/Android/data/package name/files/Pictures, or pass null to directly return /storage/emulated /0/Android/data/package name/files.

Note: The application private directory in the external storage space will be deleted when the user uninstalls the application. If you save files that need to be available after the user uninstalls the app, such as when your app downloads photos and the user should keep those photos, you should save the files to a public directory on external storage.

③Public directory in external storage space

Generally, the persistent data involved in the application is divided into two categories: application-related data and application-independent data. Application-related data refers to data information exclusively used by the host App, such as configuration information, database information, cache files, etc. of some applications. When the application is uninstalled, this information should also be deleted to avoid unnecessary occupation of storage space. However, application-independent public files can be freely accessed by other programs, and the files remain after the application is uninstalled. For example, after the camera application is uninstalled, the photos still exist. There are 9 categories of public directories in Android external storage, all of which are folders created by the system. To operate public directories in external storage, you need to apply for permissions such as READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE. Developers can directly obtain the absolute path of the corresponding directory through the methods provided by the Environment class, and pass different type parameter types. For example, the Environment.getExternalStorageDirectory().getAbsolutePath() method is to obtain the root path of the external storage; Environment.getExternalStoragePublicDirectory(String type); method is to get the shared directory of external storage.

The Environment class provides constants for many type parameters, such as:

Environment.DIRECTORY_DCIM : Photos taken by a digital camera

Environment.DIRECTORY_MUSIC: user music

Environment.DIRECTORY_PODCASTS: audio/video clips

Environment.DIRECTORY_RINGTONES: Ringtones

Environment.DIRECTORY_ALARMS: the sound of the alarm clock

Environment.DIRECTORY_PICTURES: All pictures (excluding those taken with a camera)

Environment.DIRECTORY_MOVIES: All movies (excluding those captured with a camcorder) and Downloads / other downloaded content.

Note: After the Android 10 version, that is, api29, Android officially began to recommend partition storage. Partition storage mainly restricts the file operations of public directories in the external storage space .

 

3. Scoped Storage

Since the external storage space is divided into private directory and public directory, before Android 10, the application can obtain the permission of the external storage space by applying for the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions, and then directly read and modify any file in the external storage space through the file path. Including external private directory files of other applications, which can easily lead to leakage of user privacy. Moreover, many applications will create a folder of their own application in the external storage root directory to store application data. In this way, after the user uninstalls, the application data will not be deleted, which will cause the mobile phone files to be particularly chaotic and occupy space for a long time.

In order to solve this problem, Google has added a new feature Scoped Storage from Android 10, which is partitioned storage, or sandbox. Adding partitioned storage is intended to limit the program's use of public directories in external storage, and has no effect on internal storage private directories and external storage private directories. To put it simply, in Android 10, there is no change in the reading and writing of private directories, and the File set can still be used without any permissions; the API or SAF (storage access) provided by MediaStore must be used for reading and writing of public directories. frame).

There are two main points in the modification of the partition storage: the change of the file access method of the external storage, and the change of the permission of the file access of the external storage.

1) Changes in the way of accessing files stored externally

Before Android 10.0, there are two ways to access external storage and access directories/files. One is to access through the File path, which can directly construct the file path or obtain the file path through MediaStore; the other is to access through Uri, which can be accessed through MediaStore or SAF acquisition.

After Android 10.0, access to media files in the public directory of external storage, that is, files in the /storage/emulated/0 directory, such as DCIM, Pictures, Alarms, Music, Notifications, Podcasts, Ringtones, Movies, Download, etc., must be done through MediaStore or Storage Access Framework (SAF) obtains Uri, and then accesses it through Uri (media files, such as pictures, audio, video, etc., can be accessed through MediaStore and SAF, and non-media files can only be accessed through SAF). App cannot directly access, create, delete, modify directories/files, etc. through the path File(filePath).

2) Access to externally stored file permission changes

Restricts the operation permission of the application to the external storage space. Even after applying for READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions, you cannot read and write directories in the entire external storage space. The application can only access the public media directory in the external storage (that is, several categories of folders created by the android system, such as DCIM, Pictures, Alarms, Music, Notifications, Podcasts, Ringtones, Movies, Download, etc.), and for external Storage non-public directories, such as the root directory of external storage, private directories in external storage of other applications, etc. cannot be accessed and operated.

In systems prior to Android 10, both the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions need to be applied for reading and writing data to external storage through the MediaStore API or File Path.

After Android 10.0, there are different situations:

(1) When an application uses the MediaStore API to create media files in the specified shared media directory or to query, modify, or delete the media file set created by the application, it does not need to apply for READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions. This is because the system will write the packageName of our application into the owner_package_name field in the system file database when it is created, so as to determine which application created the file in subsequent use. (If the app is uninstalled and then reinstalled, previously saved files will not belong to the files created by the app).

(2) When querying the media files (pictures, audio, and videos) contributed by other applications in the shared media directory through the MediaStore API, you need to apply for the READ_EXTERNAL_STORAGE permission. If you do not apply for this permission, you cannot query the file Uri contributed by other applications through the ContentResolver , you can only query the files contributed by your own application. When using the MediaStore API, even if you apply for the special READ_EXTERNAL_STORAGE permission, you are only allowed to read media files such as audio, video, and pictures shared by other applications, and you are not allowed to access downloaded data and non-media files created by other applications (pdf, office, doc, txt, etc.). The only way to access non-media files contributed by other apps in Android 10 is to use the file selector provided by the Storage Access Framework to request the user to operate the specified file.

(3) If you need to modify or delete media files contributed by other applications, you need to apply for the WRITE_EXTERNAL_STORAGE permission. If the application does not have the WRITE_EXTERNAL_STORAGE permission to modify the files of other apps, an exception of java.lang.SecurityException: xxxx has no access to content://media/external/images/media/52 will be thrown. When the application applies for the WRITE_EXTERNAL_STORAGE permission to modify the media files contributed by other apps, another Exception android.app.RecoverableSecurityException: xxxxxx has no access to content://media/external/images/media/52 will be thrown. At this time, give the RecoverableSecurityException to Catch, and apply to the user for permission to modify the file. After the user agrees to the operation, the Uri result can be obtained in the onActivityResult callback for operation.

try {

    editFile(editFileUri)

}catch (rse : RecoverableSecurityException){

    requestForOtherAppFiles( REQUEST_CODE_FOR_EDIT_IMAGE,rse)

}

private fun requestForOtherAppFiles( requestCode: Int, rse: RecoverableSecurityException){

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

        startIntentSenderForResult( rse.userAction.actionIntent.intentSender, requestCode, null, 0, 0, 0, null)

    }

}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

    super.onActivityResult(requestCode, resultCode, data)

    if (resultCode == Activity.RESULT_OK){

        when(requestCode){

            REQUEST_CODE_FOR_EDIT_IMAGE ->{

                editImage(editImageUri)

            }

            REQUEST_CODE_FOR_DELETE_IMAGE ->{

                contentResolver.delete( deleteImageUri,null,null)

            }

        }

    }

}

Notice:

①The application private directory includes the application private directory in the internal storage space and the application private directory in the external storage space. These two areas have not been changed accordingly, and they are still the same as before. These two private directories can be accessed without adding any permissions, and can be directly accessed, created, deleted, modified directories/files, etc. through the path File(filePath). And when the corresponding application is uninstalled, these two private directories will also be removed.

②As a transitional stage, Android 10 can still avoid partition storage by the following two methods: set targetSdkVersion to 29 or less, or set android:requestLegacyExternalStorage="true" in the manifest. But when it comes to Android 11, requestLegacyExternalStorage becomes invalid. At this time, the preserveLegacyExternalStorage attribute is added, and the overwritten application can still be used, but the new application cannot be used.

 

4. Partition storage backdoor MANAGE_EXTERNAL_STORAGE

Partition storage also has a back door. In Android 11, a new permission MANAGE_EXTERNAL_STORAGE is added. This permission will authorize reading and writing of all shared storage content, which will also include non-media type files. However, apps that have obtained this permission still cannot access other apps' app-specific directories, whether it is external storage or internal storage.

The app can apply for the MANAGE_EXTERNAL_STORAGE permission. This is for those file management apps, such as es explore, they must have such permissions, otherwise the file list cannot be listed, especially for non-media types. But this permission needs to be applied when it is put on Google Play.

In Android 11, applying for the MANAGE_EXTERNAL_STORAGE permission through Intent can guide the user to the system settings page, allowing the user to choose whether to allow the app to "access all files" (All Files Access).

The permission is declared in the manifest:

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>

Application permission code:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {

     if(!Environment.isExternalStorageManager()) { //Determine whether you have the MANAGE_EXTERNAL_STORAGE permission

        Intent intent = new Intent();

        intent.setAction( Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);

        ActivityCompat.startActivity( v.getContext(), intent, null);

        return;

    }

}

 

5.MediaStore

MediaStore does not need to apply for permission to access the files stored in the public directory of the app itself (but if the app is uninstalled and reinstalled, the previously saved files will not belong to the files created by the app), and if you want to access files saved in the public directory by other apps Files need to apply for permission.

MediaStore operates files by Uri. The Uri of each directory is as follows:

①Image type, the corresponding Uri is content://media/external/images/media MediaStore.Images.Media.EXTERNAL_CONTENT_URI, and the default path is Pictures.

②Video type, the corresponding Uri is content://media/external/video/media MediaStore.Video.Media.EXTERNAL_CONTENT_URI, and the default path is Movies.

③Audio type, the corresponding Uri is content://media/external/audio/media MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, and the default path is Music.

④Download type, the corresponding Uri is content://media/external/downloads MediaStore.Downloads.EXTERNAL_CONTENT_URI, and the default path is Download.

⑤File type, the corresponding Uri is content://media/external/MediaStore.Files.getContentUri("external"), the default path is Documents.

Examples of using MediaStore:

① Write a file

//Read Bitmap from Assets

Bitmap bitmap = null;

try {

    bitmap = BitmapFactory.decodeStream( getAssets().open("test.jpg"));

} catch (IOException e) {

    e.printStackTrace();

}

if (bitmap == null) return;

// Get the Uri of the saved file

ContentResolver contentResolver = getContentResolver();

ContentValues values = new ContentValues();

Uri insertUri = contentResolver.insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

// Save the picture to the Pictures directory

if (insertUri != null) {

    OutputStream os = null;

    try {

        os = contentResolver.openOutputStream( insertUri);

        bitmap.compress( Bitmap.CompressFormat.PNG, 100, os);

    } catch (FileNotFoundException e) {

        e.printStackTrace();

    } finally {

        try {

            if (os != null) {

                os.close();

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

This example directly saves the picture to the root directory of Pictures. If you want to create a subdirectory under Pictures, you need to use RELATIVE_PATH (Android version >= 10).

For example, if you want to save the picture in the Pictures/test directory, you only need to add the subdirectory to ContentValues:

ContentValues values = new ContentValues();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {

    // Specify a subdirectory, otherwise save to the root directory of the corresponding media type folder

    values.put( MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES +"/test");

}

You can also add other information to ContentValues, such as: file name, MIME, etc. Go ahead and modify the example above:

ContentValues values = new ContentValues();

// Get the Uri of the saved file

values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");

// Specify the saved file name, if not set, the system will take the current timestamp as the file name

values.put(MediaStore.Images.Media.DISPLAY_NAME, "test_" + System.currentTimeMillis() + ".png");

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {

    // Specify a subdirectory, otherwise save it to the root directory of the corresponding media type folder values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/test");

}

②Delete the files created by your own application

After obtaining the corresponding Uri, just use contentResolver.delete(uri,null,null).

③Query the files created by your own application

// Inquire

ContentResolver contentResolver = getContentResolver();

Cursor cursor = contentResolver.query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.Media._ID, MediaStore.Images.Media.WIDTH, MediaStore.Images.Media.HEIGHT}, MediaStore.Images.Media._ID + " > ? ", new String[]{"100"}, MediaStore.Images.Media._ID + " DESC");

// get all Uri

List<Uri> filesUris = new ArrayList<>();

while (cursor.moveToNext()) {

    int index = cursor.getColumnIndex( MediaStore.Images.Media._ID);

    Uri uri = ContentUris.withAppendedId( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getLong(index));

    filesUris.add(uri);

}

cursor.close();

// Obtain specific content through Uri and display it on the interface

ParcelFileDescriptor pfd = null;

try {

    pfd = contentResolver.openFileDescriptor( filesUris.get(0), "r");

    if (pfd != null) {

        Bitmap bitmap = BitmapFactory.decodeF ileDescriptor(pfd.getFileDescriptor());

        ((ImageView) findViewById(R.id.image)).se tImageBitmap(bitmap);

    }

} catch (FileNotFoundException e) {

    e.printStackTrace();

} finally {

    if (pfd != null) {

        try {

            pfd.close();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

④Query files created by other applications

Accessing files created by your own app does not require the READ_EXTERNAL_STORAGE permission. The filesUris obtained by the above code only includes the files created before by this application.

If you need to obtain the files of other applications together, you can apply for the READ_EXTERNAL_STORAGE permission.

⑤ Modify files created by other applications

Similarly, you need to apply for the WRITE_EXTERNAL_STORAGE permission. However, even after applying for the WRITE_EXTERNAL_STORAGE permission, the following exception will still be reported:

android.app.RecoverableSecurityException: xxx has no access to content://media/external/images/media/100

This is because it is also necessary to apply to the user for permission to modify.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {

    try {

        delete();

    } catch (RecoverableSecurityException e) {

        e.printStackTrace();

        // Pop up a dialog box to apply to the user for permission to modify other application files

        requestConfirmDialog(e);

    }

}

private void delete() {

    Uri uri = Uri.parse( "content://media/external/images/media/100");

    getContentResolver().delete(uri, null, null);

}

@RequiresApi(api = Build.VERSION_CODES.Q)

private void requestConfirmDialog( RecoverableSecurityException e) {

    try {

        startIntentSenderForResult( e.getUserAction().getActionIntent().getIntentSender(), 0, null, 0, 0, 0, null);

    } catch (IntentSender.SendIntentException ex){

        ex.printStackTrace();

    }

}

@Override

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {

    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_OK){

        delete();

    }

}

⑥Download the file to the Download directory

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

private void downloadApkAndInstall(String downloadUrl, String apkName) {

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {

        // use the original method

    } else {

        new Thread(() -> {

            BufferedInputStream bis = null;

            BufferedOutputStream bos = null;

            try {

                URL url = new URL(downloadUrl);

                URLConnection urlConnection = url.openConnection();

                InputStream is = urlConnection.getInputStream();

                bis = new BufferedInputStream(is);

                ContentValues values = new ContentValues();

                values.put( MediaStore.MediaColumns.DISPLAY_NAME, apkName);

                values.put( MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);

                ContentResolver contentResolver = getContentResolver();

                Uri uri = contentResolver.insert( MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);

                OutputStream os = contentResolver.openOutputStream(uri);

                bos = new BufferedOutputStream(os);

                byte[] buffer = new byte[1024];

                int bytes = bis.read(buffer);

                while (bytes >= 0) {

                    bos.write(buffer, 0, bytes);

                    bos.flush();

                    bytes = bis.read(buffer);

                }

                runOnUiThread(() -> installAPK(uri));

            } catch (IOException e) {

                e.printStackTrace();

            } finally {

                try {

                    if (bis != null) bis.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

                try {

                    if (bos != null) bos.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        }).start();

    }

}

private void installAPK(Uri uri) {

    Intent intent = new Intent( Intent.ACTION_VIEW);

    intent.addFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION);

    intent.setDataAndType(uri, "application/vnd.android.package-archive");

    startActivity(intent);

}              

 

Guess you like

Origin blog.csdn.net/zenmela2011/article/details/128812341