LZ阅读的是中文翻译版本:http://hukai.me/android-training-course-in-chinese/basics/activity-lifecycle/recreating.html,试验机系统版本7.1。
2、 Android分享操作
2.1 分享简单的数据
2.1.1 给其他App发送简单的数据
发送文本
Intent sendTextIntent = new Intent();
sendTextIntent.setAction(Intent.ACTION_SEND);
sendTextIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.text_for_send));
sendTextIntent.setType("text/plain");
startActivity(Intent.createChooser(sendTextIntent, getResources().getText(R.string.open_web_with)));
发送二进制流数据
Intent sendTextIntent = new Intent();
sendTextIntent.setAction(Intent.ACTION_SEND);
File file = new File(Environment.getExternalStorageDirectory().toString() + "/photos/20170329165514.png");
Uri image_uri = Uri.fromFile(file);
sendTextIntent.putExtra(Intent.EXTRA_STREAM, image_uri);
sendTextIntent.setType("image/jpeg");
startActivity(Intent.createChooser(sendTextIntent, getResources().getText(R.string.open_web_with)));
一次发送多块内容
ArrayList<Uri> imageUris = new ArrayList<Uri>();
imageUris.add(imageUri1); // Add your image URIs here
imageUris.add(imageUri2);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
shareIntent.setType("image/*");
startActivity(Intent.createChooser(shareIntent, "Share images to.."));
首先在接收的Activity的清单文件中添加intent-filter,指明该Activity可以接收的文件类型,一个Activity可以有多个intent-filter。
<activity android:name=".ui.MyActivity" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
然后再代码中处理接收到的数据即可
void onCreate (Bundle savedInstanceState) {
...
// Get intent, action and MIME type
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
if (Intent.ACTION_SEND.equals(action) && type != null) {
if ("text/plain".equals(type)) {
handleSendText(intent); // Handle text being sent
} else if (type.startsWith("image/")) {
handleSendImage(intent); // Handle single image being sent
}
} else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
if (type.startsWith("image/")) {
handleSendMultipleImages(intent); // Handle multiple images being sent
}
} else {
// Handle other intents, such as being started from the home screen
}
...
}
void handleSendText(Intent intent) {
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
if (sharedText != null) {
// Update UI to reflect text being shared
}
}
void handleSendImage(Intent intent) {
Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
if (imageUri != null) {
// Update UI to reflect image being shared
}
}
void handleSendMultipleImages(Intent intent) {
ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
if (imageUris != null) {
// Update UI to reflect multiple images being shared
}
}
2.1.3 给ActionBar增加分享功能
这里要使用到ShareActionProvider。首先为相应的Activity定义一个menu文件,目录在res/menu下。例
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menu_item_share"
android:showAsAction="ifRoom"
android:title="Share"
app:actionProviderClass="android.support.v7.widget.ShareActionProvider" />
...
</menu>
注意上面代码中的app:actionProviderClass属性使用的是support.v7包中的ShareActionProvider,这是因为在高版本系统中使用的ActionBar都是用的support包中的,所以相应的ShareActionProvider也要使用support包中的,否则会出现什么效果也没有的情况。
接着在Activity中复写onCreateOptionsMenu方法
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_share, menu);
MenuItem share = menu.findItem(R.id.action_share);
ShareActionProvider mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(share);
Intent sendTextIntent = new Intent();
sendTextIntent.setAction(Intent.ACTION_SEND);
sendTextIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.text_for_send));
sendTextIntent.setType("text/plain");
mShareActionProvider.setShareIntent(sendTextIntent);
return true;
}
关键的几步是,获取MenuItem,然后调用MenuItemCompat的getActionProvider来获得ShareActionProvider,当然,这个ShareActionProvider也是v7包中的,最后为其设置ShareIntent即可。这样App运行起来后,就可以在ActionBar的右侧看到一个常见的分享按钮,点击它,就出现一系列可以将指定内容分享到其中的App列表。
2.3 使用NFC分享文件
这里使用的是Android Beam功能近场传输文件。该功能只支持4.2及以上版本的系统。
在编写代码之前,首先得为应用添加NFC和WRITE_EXTERNAL_STORAGE权限。
<uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
同时,为了指定NFC功能,还要添加uses-feature
<uses-feature
android:name="android.hardware.nfc"
android:required="false"/>
上面的权限和特色功能都添加以后,才可以进行接下来的编码。
首先是NFC发送文件。不过在此之前,先判断手机是否支持NFC功能
PackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)
继续判断手机的系统版本是否等于或高于4.2
Build.VERSION.SDK_INT <
Build.VERSION_CODES.JELLY_BEAN_MR1
若上面两项都满足,就可以进行具体的发送文件功能的实现
首先创建一个NfcAdapter对象,它就是用于执行具体传输的对象
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
接着需要创建一个提供文件的回调函数,它是供文件给NfcAdapter对象发送的,要实现NfcAdapter.CreateBeamUrisCallback回调接口。
public class MainActivity extends Activity {
...
// List of URIs to provide to Android Beam
private Uri[] mFileUris = new Uri[10];
...
/**
* Callback that Android Beam file transfer calls to get
* files to share
*/
private class FileUriCallback implements
NfcAdapter.CreateBeamUrisCallback {
public FileUriCallback() {
}
/**
* Create content URIs as needed to share with another device
*/
@Override
public Uri[] createBeamUris(NfcEvent event) {
return mFileUris;
}
}
...
}
接下来就是创建回调函数的对象并设置给NfcAdapter对象
mFileUriCallback = new FileUriCallback();
// Set the dynamic callback for URI requests.
mNfcAdapter.setBeamPushUrisCallback(mFileUriCallback,this);
最后指定要发送的文件
/*
* Create a list of URIs, get a File,
* and set its permissions
*/
private Uri[] mFileUris = new Uri[10];
String transferFile = "transferimage.jpg";
File extDir = getExternalFilesDir(null);
File requestFile = new File(extDir, transferFile);
requestFile.setReadable(true, false);
// Get a URI for the File and add it to the list of URIs
fileUri = Uri.fromFile(requestFile);
if (fileUri != null) {
mFileUris[0] = fileUri;
} else {
Log.e("My Activity", "No File URI available for file.");
}
这个文件的Uri数组,就是回调函数里返回的mFileUris数组。
2.3.2 接收其他设备的文件
当Android Beam文件传输将文件拷贝至接收设备后,它会fabulous一个包含intent的通知,该Intent拥有:ACTION_VIEW,首个被传输文件的MIME类型,以及一个指向第一个文件的URI。所以,要接收文件,就得在Activity的intent-filter中添加VIEW的action,同时还要添加默认的category和data。
<activity
android:name="com.example.android.nfctransfer.ViewActivity"
android:label="Android Beam Viewer" >
...
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
...
</intent-filter>
</activity>
因为App可能接收到除了Android Beam文件传输之外的某个来源所发出的含有VIEW_ACTION的Intent,所以要检查Intent的Scheme和Authority。
下面就是具体的接收实例
public class MainActivity extends Activity {
...
// A File object containing the path to the transferred files
private File mParentPath;
// Incoming Intent
private Intent mIntent;
...
/*
* Called from onNewIntent() for a SINGLE_TOP Activity
* or onCreate() for a new Activity. For onNewIntent(),
* remember to call setIntent() to store the most
* current Intent
*
*/
private void handleViewIntent() {
...
// Get the Intent action
mIntent = getIntent();
String action = mIntent.getAction();
/*
* For ACTION_VIEW, the Activity is being asked to display data.
* Get the URI.
*/
if (TextUtils.equals(action, Intent.ACTION_VIEW)) {
// Get the URI from the Intent
Uri beamUri = mIntent.getData();
/*
* Test for the type of URI, by getting its scheme value
*/
if (TextUtils.equals(beamUri.getScheme(), "file")) {
mParentPath = handleFileUri(beamUri);
} else if (TextUtils.equals(
beamUri.getScheme(), "content")) {
mParentPath = handleContentUri(beamUri);
}
}
...
}
...
}
...
public String handleFileUri(Uri beamUri) {
// Get the path part of the URI
String fileName = beamUri.getPath();
// Create a File object for this filename
File copiedFile = new File(fileName);
// Get a string containing the file's parent directory
return copiedFile.getParent();
}
...
...
public String handleContentUri(Uri beamUri) {
// Position of the filename in the query Cursor
int filenameIndex;
// File object for the filename
File copiedFile;
// The filename stored in MediaStore
String fileName;
// Test the authority of the URI
if (!TextUtils.equals(beamUri.getAuthority(), MediaStore.AUTHORITY)) {
/*
* Handle content URIs for other content providers
*/
// For a MediaStore content URI
} else {
// Get the column that contains the file name
String[] projection = { MediaStore.MediaColumns.DATA };
Cursor pathCursor =
getContentResolver().query(beamUri, projection,
null, null, null);
// Check for a valid cursor
if (pathCursor != null &&
pathCursor.moveToFirst()) {
// Get the column index in the Cursor
filenameIndex = pathCursor.getColumnIndex(
MediaStore.MediaColumns.DATA);
// Get the full file name including path
fileName = pathCursor.getString(filenameIndex);
// Create a File object for the filename
copiedFile = new File(fileName);
// Return the parent directory of the file
return new File(copiedFile.getParent());
} else {
// The query didn't work; return null
return null;
}
}
}
...