鍍金池/ 教程/ Android/ Building Apps with Content Sharing
Launch mode 和 Intent flags專題
Canvas & Drawables
UTAustinX_UT.9.01x: Effective Thinking Through Mathematics
《JavaScript 語言精粹》
Memory leak專題
React基礎
《Test Driven Development: By Example》一書
Developer tools
安卓開發(fā)技能樹
<a rel="nofollow" href="https://mp.weixin.qq.com/s?__biz=MzA3NDM
Best Practices for Interaction and Engagement
各個安卓版本引入的主要新特性
Building Apps with Connectivity &amp; the Cloud
List.toArray()再強轉是一定會失敗的
深入Android frameworks
Google dev 100 days系列視頻
Building Apps with Contacts &amp; Sign-In
關系型數(shù)據(jù)庫設計范式
《App研發(fā)錄》一書
REST API設計
Google IO 2015摘要
自定義View/ViewGroup以及高性能實現(xiàn)自定義UI
安卓系統(tǒng)點擊事件處理
《50 Android Hacks》一書
Building Apps with Content Sharing
Flux基礎
<a rel="nofollow" href="http://developer.android.com/training/in
依賴注入(以Dagger 2為例)
Java同步機制
Java對象內(nèi)存的使用情況
JSR133(Java memory model)
Google官方Material Design手冊(<a rel="nofollow" href="http://develop
Futurice公司安卓團隊的建議
安卓性能優(yōu)化
  • 1.
Best Practices for Performance
<a rel="nofollow" href="http://www.vogella.com/tutorials/Android
<a rel="nofollow" href="http://blog.danlew.net/2014/11/19/styles
Handling Runtime Changes
<a rel="nofollow" href="http://www.vogella.com/tutorials/Android
Building Apps with Graphics &amp; Animation
<a rel="nofollow" href="http://tools.android.com/tech-docs/new-b
Android項目架構
MVP(Model-View-Presenter)模式
<a rel="nofollow" href="http://www.infoq.com/cn/es6-in-depth/"">
《Android源碼設計模式解析與實戰(zhàn)》一書
Rx在Android中的最佳實踐
函數(shù)調(diào)用時,傳遞參數(shù)應該是不可變的(Immutable)
ProGuard
面向對象六大原則(SOLID+)
深入理解Java虛擬機
深入Java深淺拷貝、immutable、unmodifiable
Best Practices for User Input
UI上的一些高效方式/最佳實踐
<a rel="nofollow" href="https://blog.stylingandroid.com/ripples-
Best Practices for User Interface
安卓測試驅動開發(fā)/安卓測試驗證
暗時間:學會正確思考
技術筆記
Aspect Oriented Programming(AOP)
Best Practices for Background Jobs
安卓系統(tǒng)動效專題
Feed系統(tǒng)的設計
Data binding(MVVM,Model-View-ViewModel)
Effective Java一書筆記
<a rel="nofollow" href="http://developer.android.com/training/in
Rx (Reactive eXtention)
MultiDex專題
一些很棒的點子
WebRTC

Building Apps with Content Sharing

  • Sharing Simple Data
    • Intent && ActionProvider
    • 發(fā)送數(shù)據(jù)(發(fā)起intent調(diào)起其他app處理)
      • Send Text Content
      • Send Binary Content
      • Send Multiple Pieces of Content
    • 接收數(shù)據(jù)
      • AndroidManifest.xml中為Activity定義<intent-filter>
      • 在Activity的onCreate中調(diào)用getIntent()獲取action、數(shù)據(jù),并進行處理
  • Sharing Files
    • 唯一“安全”的方式就是:將文件對應的URI通過Intent發(fā)送出去,并為該URI提供臨時的訪問權限。而這些步驟都可以通過FileProvider完成
    • 在AndroidManifest.xml中聲明provider
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.myapp">
          <application
              ...>
              <provider
                  android:name="android.support.v4.content.FileProvider"
                  android:authorities="com.example.myapp.fileprovider"
                  android:grantUriPermissions="true"
                  android:exported="false">
                  <meta-data
                      android:name="android.support.FILE_PROVIDER_PATHS"
                      android:resource="@xml/filepaths" />
              </provider>
              ...
          </application>
      </manifest>
    • <meta-data>指定描述要分享的目錄的xml文件
    • 指定要分享的目錄
      <paths>
          <files-path path="images/" name="myimages" />
      </paths>
    • <paths>標簽可以有多個子標簽,<files-path>指定app的files目錄下的分享目錄名,<external-path>指定外部存儲(Environment.getExternalStorageDirectory())的分享目錄名,<cache-path>指定app的cache目錄下的分享目錄名;分享路徑只能在xml中描述;
    • 如上配置后,需要訪問files/images/default_image.jpg時,對應uri為:content://com.example.myapp.fileprovider/myimages/default_image.jpg
    • Receive File Requests
      • 定義一個Selection Activity,響應Intent action,例如:ACTION_PICK
          <activity
              android:name=".FileSelectActivity"
              android:label="@"File Selector" >
              <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="text/plain"/>
                  <data android:mimeType="image/*"/>
              </intent-filter>
          </activity>
      • 其他app發(fā)起該intent,通過startActivityForResult()onActivityResult()中發(fā)起請求、處理結果
      • 在Selection Activity的onCreate函數(shù)中,解析其他app的請求
          File requestFile = new File(mImageFilename[position]);
          // Use the FileProvider to get a content URI
          try {
              fileUri = FileProvider.getUriForFile(
                      MainActivity.this,
                      "com.example.myapp.fileprovider",
                      requestFile);
          } catch (IllegalArgumentException e) {
              Log.e("File Selector",
                      "The selected file can't be shared: " +
                      clickedFilename);
          }
      • 賦予臨時訪問權限
          mResultIntent = new Intent("com.example.myapp.ACTION_RETURN_FILE");
          if (fileUri != null) {
              // Grant temporary read permission to the content URI
              mResultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
          }
      • 使用setFlags()賦予臨時訪問權限更安全,Context.grantUriPermission()賦予的權限只有手動調(diào)用Context.revokeUriPermission()才會被移除
      • 返回結果
          mResultIntent.setDataAndType(
                  fileUri,
                  getContentResolver().getType(fileUri));
          // Set the result
          MainActivity.this.setResult(Activity.RESULT_OK,
                  mResultIntent);
          finish();
    • Requesting a Shared File
      • 通常流程是:app發(fā)起一個帶有請求的intent,分享文件的app的相應Activity被啟動,該Activity顯示文件列表,用戶選擇文件后返回被選中的文件的Uri
      • 發(fā)起請求
          mRequestFileIntent = new Intent(Intent.ACTION_PICK);
          mRequestFileIntent.setType("image/jpg");
          startActivityForResult(mRequestFileIntent, 0);
      • 訪問返回的文件
          @Override
          public void onActivityResult(int requestCode, int resultCode,
                  Intent returnIntent) {
              // If the selection didn't work
              if (resultCode != RESULT_OK) {
                  // Exit without doing anything else
                  return;
              } else {
                  // Get the file's content URI from the incoming Intent
                  Uri returnUri = returnIntent.getData();
                  /*
                  * Try to open the file for "read" access using the
                  * returned URI. If the file isn't found, write to the
                  * error log and return.
                  */
                  try {
                      /*
                      * Get the content resolver instance for this context, and use it
                      * to get a ParcelFileDescriptor for the file.
                      */
                      mInputPFD = getContentResolver().openFileDescriptor(returnUri, "r");
                  } catch (FileNotFoundException e) {
                      e.printStackTrace();
                      Log.e("MainActivity", "File not found.");
                      return;
                  }
                  // Get a regular file descriptor for the file
                  FileDescriptor fd = mInputPFD.getFileDescriptor();
                  ...
              }
          }
    • Retrieving File Information
      • MIME Type
        Uri returnUri = returnIntent.getData();
        String mimeType = getContentResolver().getType(returnUri);
      • File's Name and Size
        Uri returnUri = returnIntent.getData();
        Cursor returnCursor =
              getContentResolver().query(returnUri, null, null, null, null);
        int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
        int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
        nameView.setText(returnCursor.getString(nameIndex));
        sizeView.setText(Long.toString(returnCursor.getLong(sizeIndex)));
  • Sharing Files with NFC

    • Android Beam,大文件傳輸,from 4.1 API 16
    • Android Beam NDEF,小數(shù)據(jù)傳輸,from 4.0 API 14
    • 發(fā)送文件

      • 權限:<uses-permission android:name="android.permission.NFC" />,<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
      • NFC feature:<uses-feature android:name="android.hardware.nfc" android:required="true" />
      • minSdkVersion >= 16
      • 檢查是否支持
          if (PackageManager.hasSystemFeature(PackageManager.FEATURE_NFC) &&
                  Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
              mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
          }
      • 創(chuàng)建提供文件的回調(diào)

          // 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;
              }
          }
        
          ...
          // 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);
          ...
      • 設置需要發(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.");
          }
    • 接收文件
      • 設置intent-filter
          <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>
      • 權限:<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
      • 獲取接收文件的路徑
          // 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);
              }
          }
      • 讀取
          String fileName = beamUri.getPath();
          File copiedFile = new File(fileName);
      • 根據(jù)不同的content provider讀取文件
          ...
          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;
                  }
              }
          }
          ...