利用 Android 系統(tǒng)原生 API 實(shí)現(xiàn)分享功能(2)

在之前的一篇文章利用 Android 系統(tǒng)原生 API 實(shí)現(xiàn)分享功能中主要說(shuō)了下實(shí)現(xiàn)流程,但具體實(shí)施起來(lái)其實(shí)還是有許多坑要面對(duì)霞玄。那這篇文章就是提供一個(gè)封裝好的 Share2 庫(kù)供大家參考晃虫。

GitHub 項(xiàng)目地址:Share2


看過(guò)上一篇文章的同學(xué)應(yīng)該知道禽翼,要調(diào)用 Android 系統(tǒng)內(nèi)建的分享功能膳算,主要有三步流程:

  • 創(chuàng)建一個(gè) Intent ,指定其 Action 為 Intent.ACTION_SEND趾浅,表示要?jiǎng)?chuàng)建一個(gè)發(fā)送指定內(nèi)容的隱式意圖愕提。

  • 然后指定需要發(fā)送的內(nèi)容和類型,設(shè)置分享的文本內(nèi)容或文件的Uri皿哨,以及文件的類型浅侨,便于是支持該類型內(nèi)容的應(yīng)用打開(kāi)。

  • 最后向系統(tǒng)發(fā)送隱式意圖证膨,開(kāi)啟系統(tǒng)分享選擇器如输,分享完成后收到結(jié)果返回。

更多相關(guān)內(nèi)容請(qǐng)參考上一篇央勒,這里就不再重復(fù)贅述了不见。


知道大致的實(shí)現(xiàn)流程后,其實(shí)只要解決下面幾個(gè)問(wèn)題后就可以具體實(shí)施了崔步。

確定要分享的內(nèi)容類型

這其實(shí)是直接決定了最終的實(shí)現(xiàn)形態(tài)稳吮,我們知道常見(jiàn)的使用場(chǎng)景中,只是為了在應(yīng)用間分享圖片和一些文件井濒,那對(duì)于那些只是分享文本的產(chǎn)品而言灶似,兩者實(shí)現(xiàn)起來(lái)要考慮的問(wèn)題完全不同。

所以為了解決這個(gè)問(wèn)題瑞你,我們可以預(yù)先定好支持的分享內(nèi)容類型喻奥,針對(duì)不同類型可以進(jìn)行不同的處理。

@StringDef({ShareContentType.TEXT, ShareContentType.IMAGE,
        ShareContentType.AUDIO, ShareContentType.VIDEO, ShareContentType.File})
@Retention(RetentionPolicy.SOURCE)
@interface ShareContentType {
    /**
     * Share Text
     */
    final String TEXT = "text/plain";

    /**
     * Share Image
     */
    final String IMAGE = "image/*";

    /**
     * Share Audio
     */
    final String AUDIO = "audio/*";

    /**
     * Share Video
     */
    final String VIDEO = "video/*";

    /**
     * Share File
     */
    final String File = "*/*";
}`

在 Share2 中捏悬,一共定義了5種類別的分享內(nèi)容,基本能覆蓋常見(jiàn)的使用場(chǎng)景润梯。在調(diào)用分享接口時(shí)可以直接指定內(nèi)容類型过牙,比如像文本、圖片纺铭、音視頻寇钉、已經(jīng)其他各種類型文件。

確定分享的內(nèi)容來(lái)源

對(duì)于不同類別的內(nèi)容舶赔,可能會(huì)有不同的來(lái)源扫倡。比如文本可能就只是一個(gè)字符串對(duì)象,而對(duì)于分享圖片或其他文件,我們需要一個(gè) Uri 來(lái)標(biāo)識(shí)一個(gè)資源撵溃。這其實(shí)就引出來(lái)具體實(shí)施時(shí)的一個(gè)大問(wèn)題疚鲤,如何獲取要分享文件的 Uri,并且這個(gè) Uri 要能被接收分享內(nèi)容的應(yīng)用處理才行 缘挑。

通常獲取文件場(chǎng)景有:用戶通過(guò)打開(kāi)文件選擇器或圖片選擇器來(lái)獲取一個(gè)指定的文件集歇;用戶通過(guò)拍照或錄制音視頻來(lái)獲取语淘;用戶下載一個(gè)文件或直接獲取本地某個(gè)文件的路徑來(lái)獲取诲宇。

那么,如何獲取要分享內(nèi)容文件的 Uri惶翻?如果處理才能讓接收方也能夠根據(jù) Uri 獲取到文件姑蓝?

我們把文件 Uri 的來(lái)源劃分為下面三種類型:

系統(tǒng)返回的 Uri

常見(jiàn)場(chǎng)景:通過(guò)文件選擇器獲取一個(gè)文件的 Uri

  private static final int REQUEST_FILE_SELECT_CODE = 100;
  private @ShareContentType String fileType = ShareContentType. File;

  /**
   * 打開(kāi)文件管理選擇文件
   */
   private void openFileChooser() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("*/*");
        intent.addCategory(Intent.CATEGORY_OPENABLE);

        try {
            startActivityForResult(Intent.createChooser(intent, "Choose File"), REQUEST_FILE_SELECT_CODE);
        } catch (Exception ex) {
            // not install file manager.
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == FILE_SELECT_CODE && resultCode == RESULT_OK) {
            // 獲取到的系統(tǒng)返回的 Uri
            Uri shareFileUrl = data.getData();
        }
    }

通過(guò)這種方式獲取到的 Uri 是由系統(tǒng) ContentProvider 返回的,在 Android 4.4 之前的版本和之后的版本有較大的區(qū)別吕粗,我們后面再說(shuō)怎么處理纺荧。只要先記住這種系統(tǒng)返回給我們的 Uri 就行了。

系統(tǒng)返回的一些常見(jiàn) Uri 樣式:
content://com.android.providers.media.documents..
content://com.android.providers.downloads...
content://media/external/images/media/...
content://com.android.externalstorage.documents..

自定義 FileProvider 返回的 Uri

比如調(diào)用系統(tǒng)相機(jī)進(jìn)行拍照或錄制音視頻溯泣,要傳入一個(gè)生成目標(biāo)文件的 Uri虐秋,從 7.0 開(kāi)始我們需要用到 FileProvider 來(lái)實(shí)現(xiàn)。

  private static final int REQUEST_FILE_SELECT_CODE = 100;
   /**
     * 打開(kāi)系統(tǒng)相機(jī)進(jìn)行拍照
     */
    private void openSystemCamera() {
        //調(diào)用系統(tǒng)相機(jī)
        Intent takePhotoIntent = new Intent();
        takePhotoIntent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);

        if (takePhotoIntent.resolveActivity(getPackageManager()) == null) {
            Toast.makeText(this, "當(dāng)前系統(tǒng)沒(méi)有可用的相機(jī)應(yīng)用", Toast.LENGTH_SHORT).show();
            return;
        }

        String fileName = "TEMP_" + System.currentTimeMillis() + ".jpg";
        File photoFile = new File(FileUtil.getPhotoCacheFolder(), fileName);

        // 7.0和以上版本的系統(tǒng)要通過(guò) FileProvider 創(chuàng)建一個(gè) content 類型的 Uri
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            currentTakePhotoUri = FileProvider.getUriForFile(this, getPackageName() + ".fileProvider", photoFile);
            takePhotoIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|);
        } else {
            currentTakePhotoUri = Uri.fromFile(photoFile);
        }

        //將拍照結(jié)果保存至 outputFile 的Uri中垃沦,不保留在相冊(cè)中
        takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, currentTakePhotoUri);
        startActivityForResult(takePhotoIntent, TAKE_PHOTO_REQUEST_CODE);
    }

     // 調(diào)用系統(tǒng)相機(jī)進(jìn)行拍照與上面通過(guò)文件選擇器獲得文件 uri 的方式類似
     // 在 onActivityResult 進(jìn)行回調(diào)處理客给,此時(shí) Uri 是你 FileProvider 中指定的,注意與文件選擇器獲取的 Uri 的區(qū)別肢簿。

如果用到了 FileProvider 就要注意跟系統(tǒng) ContentProvider 返回 Uri 的區(qū)別靶剑,比如我們?cè)?Manifest 中對(duì) FileProvider 配置 android:authorities="com.xx.xxx.fileProvider" 屬性,那這時(shí)系統(tǒng)返回的 Uri 格式就變成了 :content://com.xx.xxx.fileProvider...池充,對(duì)于這種類型的 Uri 我們姑且叫自定義 FileProvider 返回的 Uri桩引,后面一并說(shuō)怎么處理。

文件的路徑

我們調(diào)用 new File 時(shí)需要傳入指定的文件路徑收夸,這個(gè)絕對(duì)路徑通常是:/storage/emulated/0/... 這種樣式坑匠,我們要想調(diào)用分享時(shí)也要變成 Uri 的形式才可以,那么如何把文件路徑變成一個(gè)文件 Uri 卧惜?這個(gè)問(wèn)題下面也一并進(jìn)行回答厘灼。

分享文件 Uri 的處理

前面提到了文件 Uri 的三種分類,對(duì)應(yīng)不同類型處理方式也不同咽瓷,不然你最先遇到的問(wèn)題就是:

java.lang.SecurityException: Uid xxx does not have permission to uri 0 @ content://com.android.providers...

這是由于對(duì)系統(tǒng)返回的 Uri 缺失訪問(wèn)權(quán)限導(dǎo)致设凹,所以要對(duì)應(yīng)用進(jìn)行臨時(shí)訪問(wèn) Uri 的授權(quán)才行,不然會(huì)提示權(quán)限缺失茅姜。

對(duì)于要分享系統(tǒng)返回的 Uri 我們可以這樣進(jìn)行處理:

// 可以對(duì)發(fā)起分享的 Intent 添加臨時(shí)訪問(wèn)授權(quán)
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

// 也可以這樣:由于不知道最終用戶會(huì)選擇哪個(gè)app闪朱,所以授予所有應(yīng)用臨時(shí)訪問(wèn)權(quán)限
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
    List<ResolveInfo> resInfoList = activity.getPackageManager().queryIntentActivities(shareIntent, PackageManager.MATCH_DEFAULT_ONLY);
    for (ResolveInfo resolveInfo : resInfoList) {
        String packageName = resolveInfo.activityInfo.packageName;
        activity.grantUriPermission(packageName, shareFileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
}

需要注意的是對(duì)于自定義 FileProvider 返回 Uri 的處理,即使是設(shè)置臨時(shí)訪問(wèn)權(quán)限,但是分享到第三方應(yīng)用也會(huì)無(wú)法識(shí)別該 Uri

典型的場(chǎng)景就是奋姿,我們?nèi)绻炎远x FileProvider 的返回的 Uri 設(shè)置分享到微信或 QQ 之類的第三方應(yīng)用锄开,會(huì)提示文件不存在,這是因?yàn)樗麄儫o(wú)法識(shí)別該 Uri胀蛮。

關(guān)于這個(gè)問(wèn)題的處理其實(shí)跟下面要說(shuō)的把文件路徑變成系統(tǒng)返回的 Uri 一樣院刁,我們只需要把自定義 FileProvider 返回的 Uri 變成第三方應(yīng)用可以識(shí)別系統(tǒng)返回的 Uri 就行了。

創(chuàng)建 FileProvider 時(shí)需要傳入一個(gè) File 對(duì)象粪狼,所以直接可以知道文件路徑退腥,那就把問(wèn)題都轉(zhuǎn)換成了:如何通過(guò)文件路徑獲取系統(tǒng)返回的 Uri

下面是根據(jù)傳入的 File 對(duì)象和類型來(lái)查詢系統(tǒng) ContentProvider 來(lái)獲取相應(yīng)的 Uri,已經(jīng)按照不同文件類型在不同系統(tǒng)版本下的進(jìn)行了適配再榄。

其中 forceGetFileUri 方法是通過(guò)反射實(shí)現(xiàn)的狡刘,處理 7.0 以上系統(tǒng)的特殊情況下的兼容性,一般情況下不會(huì)調(diào)用到困鸥。Android 7.0 開(kāi)始不允許 file:// Uri 的方式在不同的 App 間共享文件嗅蔬,但是如果換成 FileProvider 的方式依然是無(wú)效的,我們可以通過(guò)反射把該檢測(cè)干掉疾就。

   public static Uri getFileUri (Context context, @ShareContentType String shareContentType, File file){

        if (context == null) {
            Log.e(TAG,"getFileUri current activity is null.");
            return null;
        }

        if (file == null || !file.exists()) {
            Log.e(TAG,"getFileUri file is null or not exists.");
            return null;
        }

        Uri uri = null;
        
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            uri = Uri.fromFile(file);
        } else {

            if (TextUtils.isEmpty(shareContentType)) {
                shareContentType = "*/*";
            }

            switch (shareContentType) {
                case ShareContentType.IMAGE :
                    uri = getImageContentUri(context, file);
                    break;
                case ShareContentType.VIDEO :
                    uri = getVideoContentUri(context, file);
                    break;
                case ShareContentType.AUDIO :
                    uri = getAudioContentUri(context, file);
                    break;
                case ShareContentType.File :
                    uri = getFileContentUri(context, file);
                    break;
                default: break;
            }
        }
        
        if (uri == null) {
            uri = forceGetFileUri(file);
        }
        
        return uri;
    }


    private static Uri getFileContentUri(Context context, File file) {
        String volumeName = "external";
        String filePath = file.getAbsolutePath();
        String[] projection = new String[]{MediaStore.Files.FileColumns._ID};
        Uri uri = null;

        Cursor cursor = context.getContentResolver().query(MediaStore.Files.getContentUri(volumeName), projection,
                MediaStore.Images.Media.DATA + "=? ", new String[] { filePath }, null);
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID));
                uri = MediaStore.Files.getContentUri(volumeName, id);
            }
            cursor.close();
        }

        return uri;
    }

    private static Uri getImageContentUri(Context context, File imageFile) {
        String filePath = imageFile.getAbsolutePath();
        Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                new String[] { MediaStore.Images.Media._ID }, MediaStore.Images.Media.DATA + "=? ",
                new String[] { filePath }, null);
        Uri uri = null;

        if (cursor != null) {
            if (cursor.moveToFirst()) {
                int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
                Uri baseUri = Uri.parse("content://media/external/images/media");
                uri = Uri.withAppendedPath(baseUri, "" + id);
            }
            
            cursor.close();
        }
        
        if (uri == null) {
            ContentValues values = new ContentValues();
            values.put(MediaStore.Images.Media.DATA, filePath);
            uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        }

        return uri;
    }

    private static Uri getVideoContentUri(Context context, File videoFile) {
        Uri uri = null;
        String filePath = videoFile.getAbsolutePath();
        Cursor cursor = context.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
                new String[] { MediaStore.Video.Media._ID }, MediaStore.Video.Media.DATA + "=? ",
                new String[] { filePath }, null);
        
        if (cursor != null) { 
            if (cursor.moveToFirst()) { 
                int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
                Uri baseUri = Uri.parse("content://media/external/video/media");
                uri = Uri.withAppendedPath(baseUri, "" + id);
            }
            
            cursor.close();
        } 
        
        if (uri == null) {
            ContentValues values = new ContentValues();
            values.put(MediaStore.Video.Media.DATA, filePath);
            uri = context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
        }
        
        return uri;
    }


    private static Uri getAudioContentUri(Context context, File audioFile) {
        Uri uri = null;
        String filePath = audioFile.getAbsolutePath();
        Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
                new String[] { MediaStore.Audio.Media._ID }, MediaStore.Audio.Media.DATA + "=? ",
                new String[] { filePath }, null);
        
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
                Uri baseUri = Uri.parse("content://media/external/audio/media");
                uri = Uri.withAppendedPath(baseUri, "" + id);
            }
            
            cursor.close();
        }
        if (uri == null) {
            ContentValues values = new ContentValues();
            values.put(MediaStore.Audio.Media.DATA, filePath);
            uri = context.getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
        } 
        
        return uri;
    }

    private static Uri forceGetFileUri(File shareFile) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            try {
                @SuppressLint("PrivateApi")
                Method rMethod = StrictMode.class.getDeclaredMethod("disableDeathOnFileUriExposure");
                rMethod.invoke(null);
            } catch (Exception e) {
                Log.e(TAG, Log.getStackTraceString(e));
            }
        }

        return Uri.parse("file://" + shareFile.getAbsolutePath());
    }

通過(guò) File Path 轉(zhuǎn)成 Uri 的方式澜术,我們最終統(tǒng)一了調(diào)用系統(tǒng)分享時(shí)傳入內(nèi)容 Uri 的三種不同場(chǎng)景,最終全部轉(zhuǎn)換為傳遞系統(tǒng)返回的 Uri猬腰,讓第三方應(yīng)用能夠正常的獲取到分享內(nèi)容鸟废。

最終實(shí)現(xiàn)

Share2 按照上述方法進(jìn)行了具體實(shí)施,可以通過(guò)下面的方式進(jìn)行集成:

// 添加依賴
compile 'gdut.bsx:share2:0.9.0'

根據(jù) FilePath 獲取 Uri

 public Uri getShareFileUri() {
       return FileUtil.getFileUri(this, ShareContentType.FILE, new File(filePath));;
 }

分享文本

new Share2.Builder(this)
    .setContentType(ShareContentType.TEXT)
    .setTextContent("This is a test message.")
    .setTitle("Share Text")
    .build()
    .shareBySystem();

分享圖片

new Share2.Builder(this)
    .setContentType(ShareContentType.IMAGE)
    .setShareFileUri(getShareFileUri())
    .setTitle("Share Image")
    .build()
    .shareBySystem();

分享圖片到指定界面姑荷,比如分享到微信朋友圈

new Share2.Builder(this)
    .setContentType(ShareContentType.IMAGE)
    .setShareFileUri(getShareFileUri())
    .setShareToComponent("com.tencent.mm", "com.tencent.mm.ui.tools.ShareToTimeLineUI")
    .setTitle("Share Image To WeChat")
    .build()
    .shareBySystem();

分享文件

new Share2.Builder(this)
    .setContentType(ShareContentType.FILE)
    .setShareFileUri(getShareFileUri())
    .setTitle("Share File")
    .build()
    .shareBySystem();

最終效果

GitHub 項(xiàng)目地址:Share2

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盒延,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鼠冕,更是在濱河造成了極大的恐慌添寺,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件懈费,死亡現(xiàn)場(chǎng)離奇詭異计露,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)憎乙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)薄坏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人寨闹,你說(shuō)我怎么就攤上這事【耍” “怎么了繁堡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我椭蹄,道長(zhǎng)闻牡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任绳矩,我火速辦了婚禮罩润,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翼馆。我一直安慰自己割以,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布应媚。 她就那樣靜靜地躺著严沥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪中姜。 梳的紋絲不亂的頭發(fā)上消玄,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音丢胚,去河邊找鬼翩瓜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛携龟,可吹牛的內(nèi)容都是我干的兔跌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼骨宠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼浮定!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起层亿,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤桦卒,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后匿又,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體方灾,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年碌更,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了裕偿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痛单,死狀恐怖嘿棘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情旭绒,我是刑警寧澤鸟妙,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布焦人,位于F島的核電站,受9級(jí)特大地震影響重父,放射性物質(zhì)發(fā)生泄漏花椭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一房午、第九天 我趴在偏房一處隱蔽的房頂上張望矿辽。 院中可真熱鬧,春花似錦郭厌、人聲如沸袋倔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)奕污。三九已至,卻和暖如春液走,著一層夾襖步出監(jiān)牢的瞬間碳默,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工缘眶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘱根,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓巷懈,卻偏偏與公主長(zhǎng)得像该抒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子顶燕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,072評(píng)論 25 707
  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程凑保,因...
    小菜c閱讀 6,401評(píng)論 0 17
  • 對(duì)于 App 的分享功能,基本上是一個(gè)剛需涌攻,本文主要介紹運(yùn)用系統(tǒng)原生分享功能時(shí)候需要注意的一些問(wèn)題欧引。對(duì)于某些特定平...
    石先閱讀 43,660評(píng)論 7 60
  • 最美的是下雨天~ 聆聽(tīng)傘上的滴點(diǎn)~ 和你走過(guò)的小巷~ 最美的是走雨巷~ 眷戀磚上的屋檐~ 與你牽手的眼前~
    養(yǎng)圖閱讀 276評(píng)論 0 2
  • 管道機(jī)制一開(kāi)始是UNIX中出現(xiàn)的,一個(gè)程序的輸出直接成為下一個(gè)程序的輸入恳谎,就像水流過(guò)管道一樣方便(類似于函數(shù)式編程...
    ArimaKisho閱讀 13,553評(píng)論 0 0