Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/185.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 如何将一个大文件或多个文件发送到其他应用程序,并知道何时删除它们? 背景_Android_File_Android Intent_Apk - Fatal编程技术网

Android 如何将一个大文件或多个文件发送到其他应用程序,并知道何时删除它们? 背景

Android 如何将一个大文件或多个文件发送到其他应用程序,并知道何时删除它们? 背景,android,file,android-intent,apk,Android,File,Android Intent,Apk,我有一个,它允许发送APK文件到其他应用程序 在Android 4.4(包括)之前,我要做的就是发送原始APK文件的路径(所有文件都在“/data/app/…”下,即使没有根目录也可以访问) 这是发送文件的代码(可用文档): intent=新意图(intent.ACTION\u SEND\u MULTIPLE); intent.setType(“*/*”); 最终ArrayList URI=新ArrayList(); 对于(…) add(Uri.fromFile(新文件(…)); intent.

我有一个,它允许发送APK文件到其他应用程序

在Android 4.4(包括)之前,我要做的就是发送原始APK文件的路径(所有文件都在“/data/app/…”下,即使没有根目录也可以访问)

这是发送文件的代码(可用文档):

intent=新意图(intent.ACTION\u SEND\u MULTIPLE);
intent.setType(“*/*”);
最终ArrayList URI=新ArrayList();
对于(…)
add(Uri.fromFile(新文件(…));
intent.putParcelableArrayListExtra(intent.EXTRA\u流,URI);
intent.addFlags(intent.FLAG_ACTIVITY_NEW_TASK | intent.FLAG_ACTIVITY_NO_HISTORY | intent.FLAG_ACTIVITY _CLEAR_WHEN_TASK_RESET | intent.FLAG_ACTIVITY _MULTIPLE_TASK |;
问题 因为所有应用程序的APK文件都有一个唯一的名称(即它们的包名),所以我所做的一切都起作用了

自从Lollipop(5.0)以来,所有应用程序的APK文件都被简单地命名为“base.APK”,这使得其他应用程序无法理解附加它们

这意味着我可以选择发送APK文件。这就是我所想的:

  • 将它们全部复制到文件夹中,重命名为唯一名称,然后发送

  • 将它们全部压缩到一个文件中,然后发送。压缩级别可以是最小的,因为APK文件已经被压缩了

  • 问题是,我必须尽快发送文件,如果我真的必须拥有这些临时文件(除非有其他解决方案),也必须尽快处理它们

    问题是,我不会在第三方应用程序处理完临时文件后得到通知,而且我还认为,无论我选择什么,选择多个文件都需要相当长的时间来准备

    另一个问题是,一些应用程序(如Gmail)实际上禁止发送APK文件

    问题 除了我想到的解决方案之外,还有别的选择吗?有没有一种方法可以利用我以前拥有的所有优势(快速且不留下垃圾文件)来解决这个问题

    也许可以用某种方式来监控文件?或者创建一个流而不是一个真正的文件


    将临时文件放入缓存文件夹会有任何帮助吗?

    任何为此目的注册的应用程序都应该能够处理具有相同文件名但不同路径的文件。为了能够应对只有在接收活动正在运行时才能访问其他应用程序提供的文件这一事实(请参见或),接收应用程序需要将文件复制到其永久访问的目录中。我的猜测是,一些应用程序没有实现复制过程来处理相同的文件名(复制时,所有文件的文件路径可能都相同)

    我建议通过ContentProvider而不是直接从文件系统提供文件。这样,您就可以为要发送的每个文件创建一个唯一的文件名

    接收应用程序“应该”接收大致如下的文件:

    ContentResolver contentResolver = context.getContentResolver();
    Cursor cursor = contentResolver.query(uri, new String[] { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE }, null, null, null);
    // retrieve name and size columns from the cursor...
    
    InputStream in = contentResolver.openInputStream(uri);
    // copy file from the InputStream
    
    由于应用程序应该使用contentResolver.openInputStream()打开文件,ContentProvider应该/将工作,而不仅仅是在Intent中传递文件uri。当然,可能会有一些应用程序行为不端,这需要彻底测试,但如果某些应用程序无法处理ContentProvider提供的文件,您可以添加两种不同的股票期权(一种是传统的,另一种是常规的)

    对于ContentProvider部分,有以下内容:

    不幸的是,还有这样一个问题:

    文件提供程序只能为中的文件生成内容URI 预先指定的目录

    如果您可以在应用程序构建时定义所有要共享文件的目录,那么文件提供程序将是您的最佳选择。 我假设您的应用程序希望共享来自任何目录的文件,因此您需要自己的ContentProvider实现

    要解决的问题是:

  • 如何在Uri中包含文件路径,以便在以后的阶段(在ContentProvider中)提取完全相同的路径
  • 如何创建可在ContentProvider中返回给接收应用程序的唯一文件名?对于多次调用ContentProvider,此唯一文件名必须相同,这意味着无论何时调用ContentProvider,您都无法创建唯一的id,或者每次调用都会得到不同的id
  • 问题1

    ContentProvider Uri由方案(内容://)、权限和路径段组成,例如:

    content://lb.com.myapplication2.fileprovider/123/base.apk

    第一个问题有很多解决方案。我建议对文件路径进行base64编码,并将其用作Uri中的最后一段:

    Uri uri = Uri.parse("content://lb.com.myapplication2.fileprovider/" + new String(Base64.encode(filename.getBytes(), Base64.DEFAULT));
    
    如果文件路径为,例如:

    /data/data/com.google.android.gm/base.apk

    那么得到的Uri将是:

    content://lb.com.myapplication2.fileprovider/L2RhdGEvZGF0YS9jb20uZ29vZ2xlLmFuZHJvaWQuZ20vYmFzZS5hcGs=

    要检索ContentProvider中的文件路径,只需执行以下操作:

    String lastSegment = uri.getLastPathSegment();
    String filePath = new String(Base64.decode(lastSegment, Base64.DEFAULT) );
    
    List<String> segments = uri.getPathSegments();
    String uniqueId = segments.size() > 0 ? segments.get(0) : "";
    
    问题2

    解决方案非常简单。我们在创建意图时生成的Uri中包含一个唯一标识符。此标识符是Uri的一部分,可由ContentProvider提取:

    String encodedFileName = new String(Base64.encode(filename.getBytes(), Base64.DEFAULT));
    String uniqueId = UUID.randomUUID().toString();
    Uri uri = Uri.parse("content://lb.com.myapplication2.fileprovider/" + uniqueId + "/" + encodedFileName );
    
    如果文件路径为,例如:

    /data/data/com.google.android.gm/base.apk

    那么得到的Uri将是:

    content://lb.com.myapplication2.fileprovider/d2788038-53da-4e84-b10a-8d4ef95e8f5f/L2RhdGEvZGF0YS9jb20uZ29vZ2xlLmFuZHJvaWQuZ20vYmFzZS5hcGs=

    要检索ContentProvider中的唯一标识符,只需执行以下操作:

    String lastSegment = uri.getLastPathSegment();
    String filePath = new String(Base64.decode(lastSegment, Base64.DEFAULT) );
    
    List<String> segments = uri.getPathSegments();
    String uniqueId = segments.size() > 0 ? segments.get(0) : "";
    
    List segments=uri.getPathSegments();
    字符串uniqueId=segments.size()>0?获取(0):“”;
    
    ContentProvider返回的唯一文件名将是原始文件名(base.apk)加上在基本文件名之后插入的唯一标识符。例如,base.apk变为base.apk

    虽然这一切听起来都很抽象
    public class FileProvider extends ContentProvider {
    
        private static final String[] DEFAULT_PROJECTION = new String[] {
            MediaColumns.DATA,
            MediaColumns.DISPLAY_NAME,
            MediaColumns.SIZE,
        };
    
        @Override
        public boolean onCreate() {
            return true;
        }
    
        @Override
        public String getType(Uri uri) {
            String fileName = getFileName(uri);
            if (fileName == null) return null;
            return MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileName);
        }
    
        @Override
        public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
            String fileName = getFileName(uri);
            if (fileName == null) return null;
            File file = new File(fileName);
            return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
        }
    
        @Override
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
            String fileName = getFileName(uri);
            if (fileName == null) return null;
    
            String[] columnNames = (projection == null) ? DEFAULT_PROJECTION : projection;
            MatrixCursor ret = new MatrixCursor(columnNames);
            Object[] values = new Object[columnNames.length];
            for (int i = 0, count = columnNames.length; i < count; i++) {
                String column = columnNames[i];
                if (MediaColumns.DATA.equals(column)) {
                    values[i] = uri.toString();
                }
                else if (MediaColumns.DISPLAY_NAME.equals(column)) {
                    values[i] = getUniqueName(uri);
                }
                else if (MediaColumns.SIZE.equals(column)) {
                    File file = new File(fileName);
                    values[i] = file.length();
                }
            }
            ret.addRow(values);
            return ret;
        }
    
        private String getFileName(Uri uri) {
            String path = uri.getLastPathSegment();
            return path != null ? new String(Base64.decode(path, Base64.DEFAULT)) : null;
        }
    
        private String getUniqueName(Uri uri) {
            String path = getFileName(uri);
            List<String> segments = uri.getPathSegments();
            if (segments.size() > 0 && path != null) {
                String baseName = FilenameUtils.getBaseName(path);
                String extension = FilenameUtils.getExtension(path);
                String uniqueId = segments.get(0);
                return baseName + uniqueId + "." + extension;
            }
    
            return null;
        }
    
        @Override
        public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
            return 0;       // not supported
        }
    
        @Override
        public int delete(Uri uri, String arg1, String[] arg2) {
            return 0;       // not supported
        }
    
        @Override
        public Uri insert(Uri uri, ContentValues values) {
            return null;    // not supported
        }
    
    }
    
    <provider
        android:name="lb.com.myapplication2.fileprovider.FileProvider"
        android:authorities="lb.com.myapplication2.fileprovider"
        android:exported="true"
        android:grantUriPermissions="true"
        android:multiprocess="true"/>
    
    public class SymLinkActivity extends Activity{
      @Override
      protected void onCreate(Bundle savedInstanceState)
        {
        super.onCreate(savedInstanceState);
        setContentView(lb.com.myapplication2.R.layout.activity_main);
        final Intent intent=new Intent(Intent.ACTION_SEND_MULTIPLE);
        intent.setType(MimeTypeMap.getSingleton().getMimeTypeFromExtension("apk"));
        final String filePath;
        try
          {
          final android.content.pm.ApplicationInfo applicationInfo=getPackageManager().getApplicationInfo(getPackageName(),0);
          filePath=applicationInfo.sourceDir;
          }
        catch(NameNotFoundException e)
          {
          e.printStackTrace();
          finish();
          return;
          }
        final File file=new File(filePath);
        final String symcLinksFolderPath=getFilesDir().getAbsolutePath();
        findViewById(R.id.button).setOnClickListener(new android.view.View.OnClickListener(){
          @Override
          public void onClick(final android.view.View v)
            {
            final File symlink=new File(symcLinksFolderPath,"CustomizedNameOfApkFile-"+System.currentTimeMillis()+".apk");
            symlink.getParentFile().mkdirs();
            File[] oldSymLinks=new File(symcLinksFolderPath).listFiles();
            if(oldSymLinks!=null)
              {
              for(java.io.File child : oldSymLinks)
                if(child.getName().endsWith(".apk"))
                  child.delete();
              }
            symlink.delete();
            // do some dirty reflection to create the symbolic link
            try
              {
              final Class<?> libcore=Class.forName("libcore.io.Libcore");
              final java.lang.reflect.Field fOs=libcore.getDeclaredField("os");
              fOs.setAccessible(true);
              final Object os=fOs.get(null);
              final java.lang.reflect.Method method=os.getClass().getMethod("symlink",String.class,String.class);
              method.invoke(os,file.getAbsolutePath(),symlink.getAbsolutePath());
              final ArrayList<Uri> uris=new ArrayList<>();
              uris.add(Uri.fromFile(symlink));
              intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM,uris);
              intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_NO_HISTORY|Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET|Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
              startActivity(intent);
              android.widget.Toast.makeText(SymLinkActivity.this,"succeeded ?",android.widget.Toast.LENGTH_SHORT).show();
              }
            catch(Exception e)
              {
              android.widget.Toast.makeText(SymLinkActivity.this,"failed :(",android.widget.Toast.LENGTH_SHORT).show();
              e.printStackTrace();
              // TODO handle the exception
              }
            }
        });
    
        }
    }
    
     Os.symlink(originalFilePath,symLinkFilePath);