內(nèi)容分享之用NFC分享文件

Android允許你通過(guò)Android Beam功能來(lái)傳輸大文件,不過(guò)該功能只能在帶有NFC并且Android版本在(4.1及以上 API 16)的設(shè)備上運(yùn)行.更多關(guān)于A(yíng)ndroid Beam的內(nèi)容可以看Beaming NDEF Messages to Other Devices,NFC的可以看Near Field Communication.

1. 發(fā)送文件到其他設(shè)備

要通過(guò)NFC發(fā)送文件,需要做到以下三點(diǎn):

  • 請(qǐng)求權(quán)限(NFC和external storage的使用權(quán)限).
  • 測(cè)試設(shè)備是否支持NFC.
  • 給Android Beam提供URI.

對(duì)于A(yíng)ndroid Beam文件傳輸功能,有四點(diǎn)要求:

  • 只支持Android 4.1(API 16)及以上.
  • 傳輸?shù)奈募仨毷窃?strong>external storage中.
  • 所有你要發(fā)送的文件的必須是world-readable的,你也可以通過(guò)File.setReadable(true,false)來(lái)設(shè)置該權(quán)限.
  • 你必須要給要傳輸?shù)奈募?strong>提供相應(yīng)的URI(不能用 FileProvider.getUriForFile生成的Content URI).

1.1 在Manifest中聲明

1.1.1 請(qǐng)求權(quán)限

 // NFC
 <uses-permission android:name="android.permission.NFC" />
 //READ_EXTERNAL_STORAGE
 <uses-permission
            android:name="android.permission.READ_EXTERNAL_STORAGE" />

注意READ_EXTERNAL_STORAGE這個(gè)權(quán)限,在A(yíng)PI 19以前不需要,但是從API 19開(kāi)始就需要了,所以這里還是要加上,為了兼容.

1.1.2 設(shè)置NFC feature

因?yàn)镹FC是屬于hardware feature,需要添加<uses-feature>標(biāo)簽,如下:

<uses-feature
   android:name="android.hardware.nfc"
   android:required="true" />

注意:

  • android:required為true表示如果你的設(shè)備沒(méi)有該feature,則你的app就不能運(yùn)行. 如果該功能只是你的一個(gè)可選擇的功能,你應(yīng)該將android:required的值設(shè)為false.
  • 這個(gè)屬性只是告知性的,Google Play會(huì)用這個(gè)屬性來(lái)過(guò)濾你的設(shè)備不支持的應(yīng)用.
  • 設(shè)置了<uses-feature>還要記得添加相應(yīng)的權(quán)限.

1.1.3 設(shè)置SDK版本

因?yàn)锳ndroid Beam只支持Android 4.1(API 16)及以上,你過(guò)你的app必須要該功能,那么需要設(shè)置android:minSdkVersion="16".

1.2 測(cè)試設(shè)備是否支持Android Beam

要測(cè)試設(shè)備是否支持Android Beam,需要三步:

i. 將NFC這個(gè)featrue設(shè)置成optional,如下:

<uses-feature
        android:name="android.hardware.nfc"
        android:required="false" />

ii. 測(cè)試設(shè)備是否支持NFC.調(diào)用PackageManager.hasSystemFeature()方法并將FEATURE_NFC作為參數(shù)傳入.

// NFC isn't available on the device
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
    /*
     * Disable NFC features here.
     * For example, disable menu items or buttons that activate
     * NFC-related features
     */
    // Android Beam file transfer isn't supported
}

iii. 測(cè)試設(shè)備是否支持Android Beam.通過(guò)判斷Android版本來(lái)測(cè)試.

// Android Beam file transfer isn't supported
if (Build.VERSION.SDK_INT <
        Build.VERSION_CODES.JELLY_BEAN_MR1) {
    // If Android Beam isn't available, don't continue.
    mAndroidBeamAvailable = false;
    /*
     * Disable Android Beam file transfer features here.
     */
    // Android Beam file transfer is available, continue
}

在驗(yàn)證設(shè)備已經(jīng)支持NFC和Android Beam后,使用時(shí)還有判斷設(shè)備是否開(kāi)啟了NFC和Android Beam功能,可以分別用下面兩個(gè)方法:
a. NfcAdapter的isEnabled()來(lái)驗(yàn)證NFC功能是否開(kāi)啟.
b. NfcAdapter的isNdefPushEnabled ()來(lái)驗(yàn)證是否開(kāi)啟了Android Beam功能.

1.3 創(chuàng)建提供文件的回調(diào)方法

一旦你驗(yàn)證了你的設(shè)備支持Android Beam來(lái)進(jìn)行文件傳輸之后,需要添加一個(gè)回調(diào)方法來(lái)返回一組Uri對(duì)象,這些對(duì)象是你想要傳輸?shù)奈募膹?fù)制品的URI,但Android Beam檢測(cè)到你想要分享文件給其他設(shè)備時(shí),系統(tǒng)就會(huì)調(diào)用該回調(diào)方法. 要添加該回調(diào)方法,要實(shí)現(xiàn)NfcAdapter.CreateBeamUrisCallback這個(gè)接口然后override里面的createBeamUris()這個(gè)抽象方法,如下示例:

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;
        }
    }
    ...
}

實(shí)現(xiàn)了接口后,就可以將該接口實(shí)現(xiàn)類(lèi)的實(shí)例提供給Android Beam,通過(guò)setBeamPushUrisCallback()方法:

public class MainActivity extends Activity {
    ...
    // Instance that returns available files from this app
    private FileUriCallback mFileUriCallback;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Android Beam file transfer is available, continue
        ...
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        /*
         * Instantiate a new FileUriCallback to handle requests for
         * URIs
         */
        mFileUriCallback = new FileUriCallback();
        // Set the dynamic callback for URI requests.
        mNfcAdapter.setBeamPushUrisCallback(mFileUriCallback,this);
        ...
    }
    ...
}

注意: 除了通過(guò)setBeamPushUrisCallback()來(lái)提供URI之外,還可以使用setBeamPushUris()(方法,只不過(guò)前者是動(dòng)態(tài)的,后者是固定的.

1.4 設(shè)置要發(fā)送的文件

/*
 * 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.");
}

2. 從其他設(shè)備接收文件

2.1 響應(yīng)展示數(shù)據(jù)的請(qǐng)求

當(dāng)Android Beam傳輸問(wèn)文件之后,它會(huì)彈出一個(gè)notification,里面包含有intent,這個(gè)intent由一個(gè)action為ACTION_VIEW,MIME類(lèi)型為第一個(gè)文件的文件類(lèi)型以及第一個(gè)文件的URI組成.當(dāng)用戶(hù)點(diǎn)擊這個(gè)notification之后,這個(gè)intent就會(huì)被發(fā)出. 要讓你的app來(lái)響應(yīng)該intent,你需要在manifest中相應(yīng)的Activity下添加<intent-filter>標(biāo)簽.如下示例:

 <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>

2.2 請(qǐng)求文件讀取權(quán)限

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

2.3 獲取復(fù)制過(guò)來(lái)的文件的路徑

Android Beam將所有復(fù)制過(guò)來(lái)的文件放在一個(gè)路徑下,上述提到的intent中包含了第一個(gè)文件的URI,但是你的app也有可能被其他的app啟動(dòng)不一定是Android Beam,而其他啟動(dòng)你的app的intent攜帶的URI可能是不一樣的格式,所以你要判斷Uri的scheme和authority來(lái)決定如何處理:

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);
            }
        }
        ...
    }
    ...
}

2.3.1 處理file URI

File URI包含文件的絕對(duì)路徑和文件名,處理如下:

    ...
    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();
    }
    ...

2.3.2 處理Content URI

如果intent包含的是content URI,那么該URI指向的文件的路徑和文件名可能存在MediaStore中.你可以通過(guò)測(cè)試URI的authority的值來(lái)判斷其是否來(lái)自MediaStore. MediaStore的URI可能是來(lái)自Android Beam傳輸過(guò)來(lái)的也有可能是其他app傳過(guò)來(lái)的,但是兩者都能拿到路徑和文件名.
根據(jù)URI的authority來(lái)判斷content provider的類(lèi)型,然后進(jìn)行相應(yīng)的處理:

    ...
    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;
             }
        }
    }
    ...

Reference

  1. Sharing Files with NFC
  2. Sending Files to Another Device
  3. Receiving Files from Another Device
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末顿锰,一起剝皮案震驚了整個(gè)濱河市窍株,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌穗酥,老刑警劉巖循帐,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件框仔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡拄养,警方通過(guò)查閱死者的電腦和手機(jī)离斩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)衷旅,“玉大人捐腿,你說(shuō)我怎么就攤上這事∈炼ィ” “怎么了茄袖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)嘁锯。 經(jīng)常有香客問(wèn)我宪祥,道長(zhǎng),這世上最難降的妖魔是什么家乘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任蝗羊,我火速辦了婚禮,結(jié)果婚禮上仁锯,老公的妹妹穿的比我還像新娘耀找。我一直安慰自己,他們只是感情好业崖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布野芒。 她就那樣靜靜地躺著,像睡著了一般双炕。 火紅的嫁衣襯著肌膚如雪狞悲。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天妇斤,我揣著相機(jī)與錄音摇锋,去河邊找鬼。 笑死站超,一個(gè)胖子當(dāng)著我的面吹牛荸恕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播顷编,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼戚炫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了媳纬?” 一聲冷哼從身側(cè)響起双肤,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤施掏,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后茅糜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體七芭,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年蔑赘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狸驳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缩赛,死狀恐怖耙箍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酥馍,我是刑警寧澤辩昆,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站旨袒,受9級(jí)特大地震影響汁针,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜砚尽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一施无、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧必孤,春花似錦猾骡、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至购啄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嘱么,已是汗流浹背狮含。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留曼振,地道東北人几迄。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像冰评,于是被迫代替她去往敵國(guó)和親映胁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容