Android 使用ACTION_CREATE_DOCUMENT编写的文件在Google Drive上为空,但在本地存储中为空
我有一个应用程序,它使用Android 使用ACTION_CREATE_DOCUMENT编写的文件在Google Drive上为空,但在本地存储中为空,android,android-intent,google-drive-api,Android,Android Intent,Google Drive Api,我有一个应用程序,它使用ACTION\u CREATE\u DOCUMENT导出CSV文件,以获取要写入的Uri。这在一段时间内运行良好,但最近当我去保存一个文件并选择Google Drive时,我注意到生成的文件是空的(0字节)。但是,如果选择Downloads文件夹作为目标(本地存储),则会正确创建并写入文件 我将问题归结为一个简单的测试活动(如下),并验证代码的所有部分实际上都在执行中(没有捕获到异常,ParcelFileDescriptor不为null,等等) 我在安卓7和8模拟器以及
ACTION\u CREATE\u DOCUMENT
导出CSV文件,以获取要写入的Uri。这在一段时间内运行良好,但最近当我去保存一个文件并选择Google Drive时,我注意到生成的文件是空的(0字节)。但是,如果选择Downloads文件夹作为目标(本地存储),则会正确创建并写入文件
我将问题归结为一个简单的测试活动(如下),并验证代码的所有部分实际上都在执行中(没有捕获到异常,ParcelFileDescriptor
不为null,等等)
我在安卓7和8模拟器以及安卓8物理设备上进行了测试,结果都显示了相同的行为,但一部旧安卓5手机工作正常(??)
该应用程序的完整版本具有并正确请求读取和写入外部存储权限来处理某些存储,但Google Drive不需要这些权限,因此我在本例中省略了它(行为与授予的权限相同)
我得到的uri看起来像一个有效的内容uri(content://com.google.android.apps.docs.storage/document/acc%3...
)
还有其他人看到过这个问题吗?我做错什么了吗?如何让Google Drive再次运行此功能?
public class MainActivity extends AppCompatActivity {
private static final int FILE_EXPORT_REQUEST_CODE = 12;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK)
return;
switch(requestCode) {
case FILE_EXPORT_REQUEST_CODE:
if( data != null ) {
Uri uri = data.getData();
if( uri != null ) {
new ExportCSV(this).execute(uri);
}
}
break;
}
}
// test Activity has a single button that calls this
public void saveFile(View v) {
Intent exportIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
exportIntent.addCategory(Intent.CATEGORY_OPENABLE);
exportIntent.setType("text/csv");
String filename = "test.csv";
exportIntent.putExtra(Intent.EXTRA_TITLE, filename);
startActivityForResult(exportIntent, FILE_EXPORT_REQUEST_CODE);
}
private static class ExportCSV extends AsyncTask<Uri, Void, Boolean> {
private final WeakReference<Context> context;
ExportCSV(Context c) {
context = new WeakReference<>(c);
}
@Override
protected Boolean doInBackground(Uri... uris) {
Uri uri = uris[0];
Context c = context.get();
if( c == null ) {
return false;
}
String data = "colA,colB\n1,2\n";
boolean success = false;
try {
ParcelFileDescriptor pfd = c.getContentResolver().openFileDescriptor(uri, "w");
if( pfd != null ) {
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
fileOutputStream.write(data.getBytes());
fileOutputStream.close();
success = true;
}
}
catch(Exception e) {
e.printStackTrace();
}
return success;
}
}
}
public类MainActivity扩展了AppCompatActivity{
私有静态最终整型文件\导出\请求\代码=12;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@凌驾
受保护的void onActivityResult(int请求代码、int结果代码、意图数据){
if(resultCode!=结果\u确定)
返回;
开关(请求代码){
案例文件\导出\请求\代码:
如果(数据!=null){
Uri=data.getData();
if(uri!=null){
新建ExportCSV(this).execute(uri);
}
}
打破
}
}
//测试活动只有一个按钮调用此
公共作废保存文件(视图v){
Intent exportIntent=新意图(Intent.ACTION\u CREATE\u DOCUMENT);
exportIntent.addCategory(Intent.CATEGORY\u可打开);
exportIntent.setType(“text/csv”);
字符串filename=“test.csv”;
exportIntent.putExtra(Intent.EXTRA_标题、文件名);
startActivityForResult(导出意图、文件导出请求代码);
}
私有静态类ExportCSV扩展异步任务{
私有最终引用上下文;
ExportCSV(上下文c){
上下文=新的WeakReference(c);
}
@凌驾
受保护的布尔doInBackground(Uri…Uri){
Uri=Uri[0];
Context c=Context.get();
如果(c==null){
返回false;
}
String data=“colA,colB\n1,2\n”;
布尔成功=假;
试一试{
ParcelFileDescriptor pfd=c.getContentResolver().openFileDescriptor(uri,“w”);
如果(pfd!=null){
FileOutputStream FileOutputStream=新的FileOutputStream(pfd.getFileDescriptor());
fileOutputStream.write(data.getBytes());
fileOutputStream.close();
成功=真实;
}
}
捕获(例外e){
e、 printStackTrace();
}
回归成功;
}
}
}
来自@greenapps的评论让我走上了正确的道路。将其更改为使用getContentResolver()。openOutputStream(…)
修复了该问题:
try {
OutputStream os = c.getContentResolver().openOutputStream(uri);
if( os != null ) {
os.write(data.getBytes());
os.close();
}
}
catch(IOException e) {
e.printStackTrace();
}
更新:在我的应用程序中使用此功能一段时间后,问题再次出现(写入Google Drive的文件为0字节,但可以在本地写入)。清除我的应用程序的缓存数据再次解决了此问题。请参阅 在安卓4.3及更低版本上,如果你想让你的应用程序从另一个应用程序检索文件,它必须调用一个意图,如ACTION\u PICK或ACTION\u GET\u CONTENT。然后,用户必须选择要从中拾取文件的单个应用程序,并且所选应用程序必须为用户提供一个用户界面,以便用户浏览和拾取可用文件 在Android 4.4(API级别19)及更高版本上,您还可以选择使用ACTION_OPEN_DOCUMENT intent,它显示一个由系统控制的选择器UI,允许用户浏览其他应用程序提供的所有文件。从这个UI中,用户可以从任何受支持的应用程序中选择文件 在Android 5.0(API级别21)及更高版本上,您还可以使用ACTION_OPEN_DOCUMENT_TREE intent,它允许用户为客户端应用程序选择要访问的目录 对我来说,以下解决方案非常适合本地和Android 9上的google drive
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK && data != null) {
if (requestCode == REQUEST_SAVE_File) {
try {
stream = getContentResolver().openOutputStream(data.getData());
((BitmapDrawable) imageView.getDrawable()).getBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream);
strem.close(); ///very important
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
意图的创建如下所示
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/png");
intent.putExtra(Intent.EXTRA_TITLE, "my-image.png");
startActivityForResult(intent, REQUEST_SAVE_FILE);
确保在写入后关闭流。否则可能无法保存文件。OutputStream os=getContentResolver().openOutputStream(uri)@greenapps哇,这是一个简单的解决方案。谢谢,现在可以用了!是因为它是一个内容Uri而不是一个文件Uri吗?我不知道。你解决过这个问题吗?这让我发疯,破坏了我的应用程序评级(答案中的修复似乎解决了一段时间,但现在我发现清除缓存数据是唯一的事情。如果它真的非常有效,你可以始终将文件视为下载,它会自动将其放入用户的下载文件夹中,然后他们可以将其移动到他们想要的地方。可能有点痛苦,但他们不会责怪你应用程序似乎是一个谷歌驱动器的问题。但这改变了我的一个应用程序的最爱的功能的行为…我开始考虑离开Android这个const