关注https://developer.android.com/training/secure-file-sharing/index.html并且能够使用fileprovider将app的内部目录(/ data / data / package / files / xxx /)中的文件共享到客户端应用程序。
如何将assets文件夹(而不是内部目录)中的文件共享到客户端应用程序。
由于
答案 0 :(得分:4)
请参阅CommonsWare中的CWAC-Provider,这是一个可以完全按照您的需要进行操作的库。
答案 1 :(得分:0)
这是我最终使用的方式,希望这会对某人有所帮助。 在清单文件中添加了提供程序
<provider
android:name=".AssetsProvider"
android:authorities="yourpackage.provider"
android:exported="true"
android:grantUriPermissions="true"
android:readPermission="yourpermission"></provider>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.OPENABLE" />
<data android:mimeType="application/octet-stream" />
</intent-filter>
</activity>
在inProvider Activity onCreate()之后获取资源列表并将uriArray返回给调用者(Consumer App)
String[] assetFilesList = null;
// Get Asset Mangaer
AssetManager assetManager = getAssets();
try {
assetFilesList = assetManager.list();
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
}
// Set up an Intent to send back to apps that request files
mResultIntent = new Intent("yourpackage.ACTION_SEND_MULTIPLE");
// new Uri list
ArrayList<Uri> uriArrayList = new ArrayList<>();
// Set the Activity's result to null to begin with
setResult(Activity.RESULT_CANCELED, null);
Uri fileUri;
if (assetFilesList != null) {
for (String currFile : assetFilesList) {
Log.i(TAG, "Adding File " + currFile);
// parse and create uri
fileUri = Uri.parse("content://" + this.getPackageName() + ".provider/" + currFile);
// add current file uri to the list
uriArrayList.add(fileUri);
}
}
else {
Log.e(TAG, "files array is pointing to null");
}
if (uriArrayList.size() != 0) {
// Put the UriList Intent
mResultIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriArrayList);
mResultIntent.setType("application/octet-stream");
// Set the result
this.setResult(Activity.RESULT_OK, mResultIntent);
} else {
// Set the result to failed
mResultIntent.setDataAndType(null, "");
this.setResult(RESULT_CANCELED, mResultIntent);
}
// Finish Activity and return Result to Caller
finish();
我的资产提供者类,我没有实现查询,更新等......因为这些对我来说不是必需的。
public class AssetsProvider extends ContentProvider {
static final String TAG = "AssetsProvider";
@Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
Log.v(TAG, "AssetsGetter: Open asset file " + uri.toString());
AssetManager am = getContext().getAssets();
String file_name = uri.getLastPathSegment();
if (file_name == null)
throw new FileNotFoundException();
AssetFileDescriptor afd = null;
try {
afd = am.openFd(file_name);
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
}
return afd;
}
@Override
public String getType(Uri p1) {
// TODO: Implement this method
return null;
}
@Override
public int delete(Uri p1, String p2, String[] p3) {
// TODO: Implement this method
return 0;
}
@Override
public Cursor query(Uri p1, String[] p2, String p3, String[] p4, String p5) {
// TODO: Implement this method
return null;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
// TODO: Implement this method
return super.query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal);
}
@Override
public Uri insert(Uri p1, ContentValues p2) {
// TODO: Implement this method
return null;
}
@Override
public boolean onCreate() {
// TODO: Implement this method
return false;
}
@Override
public int update(Uri p1, ContentValues p2, String p3, String[] p4) {
// TODO: Implement this method
return 0;
}
}
Gradle构建选项以避免压缩资产文件(这些是我在资产中拥有的文件类型)
aaptOptions {
noCompress '.json' , '.xls'
}
跟随消费者活动
在onCreate()中 - 需要setPackage(),因为我们希望将ACTION_PICK发送到特定的应用程序
Intent mRequestFileIntent = new Intent(Intent.ACTION_PICK);
mRequestFileIntent.setPackage("yourAssetsProviderpackage");
mRequestFileIntent.setType("application/octet-stream");
try {
startActivityForResult(mRequestFileIntent, 0);
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "Install Assets Provider app before start", Toast.LENGTH_LONG).show();
finish();
}
在onActivityResult()
上添加了覆盖方法public void onActivityResult(int requestCode, int resultCode,
Intent returnIntent) {
// If the selection didn't work
if (resultCode != Activity.RESULT_OK) {
// Exit without doing anything else
Log.e(TAG, "Activity returned fail");
} else {
// get array list
ArrayList<Uri> uriArrayList = returnIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
// create directory in internal storage to store the assets from uri list
String toPath = this.getFilesDir().getPath();
if (uriArrayList != null) {
AssetFileDescriptor mInputAFD;
for (int i = 0; i < uriArrayList.size(); i++) {
// Get the file's content URI
Uri returnUri = uriArrayList.get(i);
try {
mInputAFD = getContentResolver().openAssetFileDescriptor(returnUri, "r");
// Get file name
String fileName = returnUri.getLastPathSegment();
Log.i(TAG, "URI " + returnUri.toString() + " fileName " + fileName);
// Create dest filename and copy
File dest = new File(toPath + "/" + fileName);
copyRaw(mInputAFD, dest);
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
// Break loop at first exception
break;
}
}
}
}
}
使用AssetFileDescriptor复制文件的CopyRaw方法
public void copyRaw(AssetFileDescriptor fd, File destinationFile) throws IOException {
FileChannel sourceChannel = new FileInputStream(fd.getFileDescriptor()).getChannel();
FileChannel destinationChannel = new FileOutputStream(destinationFile).getChannel();
sourceChannel.transferTo(fd.getStartOffset(), fd.getLength(), destinationChannel);
}
在使用者清单文件中添加权限
<uses-permission android:name="yourpermission" />