Android 如何将一个大文件或多个文件发送到其他应用程序,并知道何时删除它们? 背景
我有一个,它允许发送APK文件到其他应用程序 在Android 4.4(包括)之前,我要做的就是发送原始APK文件的路径(所有文件都在“/data/app/…”下,即使没有根目录也可以访问) 这是发送文件的代码(可用文档):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.
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文件。这就是我所想的:
将临时文件放入缓存文件夹会有任何帮助吗?任何为此目的注册的应用程序都应该能够处理具有相同文件名但不同路径的文件。为了能够应对只有在接收活动正在运行时才能访问其他应用程序提供的文件这一事实(请参见或),接收应用程序需要将文件复制到其永久访问的目录中。我的猜测是,一些应用程序没有实现复制过程来处理相同的文件名(复制时,所有文件的文件路径可能都相同) 我建议通过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 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);